feat(ui): add gear preset shortcuts
This commit is contained in:
@ -806,6 +806,63 @@ class _DeviceDetailsPageState extends ConsumerState<DeviceDetailsPage> {
|
||||
_isFirmwareUpdateBusy ? null : _loadGearRatios,
|
||||
onSave: _saveGearRatios,
|
||||
presets: const [
|
||||
GearRatioPreset(
|
||||
name: 'Road',
|
||||
description:
|
||||
'Balanced 12-speed road gearing for steady cadence steps.',
|
||||
ratios: [
|
||||
0.50,
|
||||
0.58,
|
||||
0.67,
|
||||
0.76,
|
||||
0.86,
|
||||
0.97,
|
||||
1.09,
|
||||
1.22,
|
||||
1.36,
|
||||
1.51,
|
||||
1.67,
|
||||
1.84,
|
||||
],
|
||||
),
|
||||
GearRatioPreset(
|
||||
name: 'Gravel',
|
||||
description:
|
||||
'Slightly lower gearing with smooth jumps for mixed terrain rides.',
|
||||
ratios: [
|
||||
0.46,
|
||||
0.54,
|
||||
0.62,
|
||||
0.70,
|
||||
0.79,
|
||||
0.89,
|
||||
1.00,
|
||||
1.12,
|
||||
1.25,
|
||||
1.40,
|
||||
1.57,
|
||||
1.76,
|
||||
],
|
||||
),
|
||||
GearRatioPreset(
|
||||
name: 'MTB',
|
||||
description:
|
||||
'Lower climbing gears with wider top-end spacing for steep trails.',
|
||||
ratios: [
|
||||
0.42,
|
||||
0.49,
|
||||
0.57,
|
||||
0.66,
|
||||
0.76,
|
||||
0.87,
|
||||
1.00,
|
||||
1.15,
|
||||
1.32,
|
||||
1.52,
|
||||
1.75,
|
||||
2.02,
|
||||
],
|
||||
),
|
||||
GearRatioPreset(
|
||||
name: 'KeAnt Classic',
|
||||
description:
|
||||
|
||||
@ -14,6 +14,42 @@ class GearRatioPreset {
|
||||
final List<double> ratios;
|
||||
}
|
||||
|
||||
class _QuickPresetButton extends StatelessWidget {
|
||||
const _QuickPresetButton({
|
||||
required this.label,
|
||||
required this.onPressed,
|
||||
this.icon,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final VoidCallback onPressed;
|
||||
final IconData? icon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final buttonStyle = OutlinedButton.styleFrom(
|
||||
minimumSize: const Size(0, 42),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
||||
);
|
||||
|
||||
if (icon != null) {
|
||||
return OutlinedButton.icon(
|
||||
onPressed: onPressed,
|
||||
style: buttonStyle,
|
||||
icon: Icon(icon, size: 18),
|
||||
label: Text(label),
|
||||
);
|
||||
}
|
||||
|
||||
return OutlinedButton(
|
||||
onPressed: onPressed,
|
||||
style: buttonStyle,
|
||||
child: Text(label),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GearRatioEditorCard extends StatefulWidget {
|
||||
const GearRatioEditorCard({
|
||||
required this.ratios,
|
||||
@ -60,6 +96,12 @@ class _GearRatioEditorCardState extends State<GearRatioEditorCard> {
|
||||
List<double>? _stretchBase;
|
||||
int _gearLayoutVersion = 0;
|
||||
|
||||
static const List<String> _quickPresetNames = [
|
||||
'Road',
|
||||
'Gravel',
|
||||
'MTB',
|
||||
];
|
||||
|
||||
List<double> _committed = const [];
|
||||
List<double> _draft = const [];
|
||||
int _committedDefaultGearIndex = 0;
|
||||
@ -235,6 +277,25 @@ class _GearRatioEditorCardState extends State<GearRatioEditorCard> {
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
if (_isEditing) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(14, 0, 14, 10),
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
for (final presetName in _quickPresetNames)
|
||||
_QuickPresetButton(
|
||||
label: presetName,
|
||||
onPressed: () => _applyNamedPreset(presetName),
|
||||
),
|
||||
_QuickPresetButton(
|
||||
label: 'Load',
|
||||
icon: Icons.folder_open_rounded,
|
||||
onPressed: _openPresetPicker,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(14, 0, 14, 8),
|
||||
child: Row(
|
||||
@ -251,12 +312,6 @@ class _GearRatioEditorCardState extends State<GearRatioEditorCard> {
|
||||
});
|
||||
},
|
||||
),
|
||||
const Spacer(),
|
||||
OutlinedButton.icon(
|
||||
onPressed: _openPresetPicker,
|
||||
icon: const Icon(Icons.tune),
|
||||
label: const Text('Load preset'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -749,8 +804,21 @@ class _GearRatioEditorCardState extends State<GearRatioEditorCard> {
|
||||
return;
|
||||
}
|
||||
|
||||
_loadPreset(selected);
|
||||
}
|
||||
|
||||
void _applyNamedPreset(String name) {
|
||||
for (final preset in widget.presets) {
|
||||
if (preset.name.toLowerCase() == name.toLowerCase()) {
|
||||
_loadPreset(preset);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _loadPreset(GearRatioPreset preset) {
|
||||
setState(() {
|
||||
_draft = selected.ratios.map(_quantizeRatio).toList(growable: false);
|
||||
_draft = preset.ratios.map(_quantizeRatio).toList(growable: false);
|
||||
_draftDefaultGearIndex = _normalizeDefaultIndex(0, _draft.length);
|
||||
if (_sortAscending) {
|
||||
_sortDraft(animate: true);
|
||||
|
||||
Reference in New Issue
Block a user