refine(quantity): drop handful, reorder scale, potted-plant icon, year dropdown

Feedback from the running app:
- Remove "handful" from the scale (redundant with spoon); keep the enum value.
- Reorder so "packet" (sobre) follows "spoon" (cuchara).
- Use the Material Symbols potted-plant for the plant lot type and plant unit
  (adds material_symbols_icons).
- Harvest year is now a dropdown of recent years instead of free text.

38 tests green; Linux build runs with the new icon font bundled.
This commit is contained in:
vjrj 2026-07-08 11:03:57 +02:00
parent 3942975dba
commit 7e92410728
6 changed files with 36 additions and 12 deletions

View file

@ -1,5 +1,6 @@
import 'package:commons_core/commons_core.dart'; import 'package:commons_core/commons_core.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../db/enums.dart'; import '../db/enums.dart';
import '../i18n/strings.g.dart'; import '../i18n/strings.g.dart';
@ -8,15 +9,15 @@ import 'seed_glyph.dart';
import 'theme.dart'; import 'theme.dart';
/// Informal seed amounts, ascending: vibes containers seed/fruit forms. /// Informal seed amounts, ascending: vibes containers seed/fruit forms.
/// ([QuantityKind.handful] is intentionally omitted it overlaps with spoon.)
const seedScaleKinds = <QuantityKind>[ const seedScaleKinds = <QuantityKind>[
QuantityKind.aFew, QuantityKind.aFew,
QuantityKind.handful,
QuantityKind.teaspoon, QuantityKind.teaspoon,
QuantityKind.spoon, QuantityKind.spoon,
QuantityKind.packet,
QuantityKind.cup, QuantityKind.cup,
QuantityKind.jar, QuantityKind.jar,
QuantityKind.sack, QuantityKind.sack,
QuantityKind.packet,
QuantityKind.cob, QuantityKind.cob,
QuantityKind.pod, QuantityKind.pod,
QuantityKind.ear, QuantityKind.ear,
@ -61,7 +62,7 @@ class LotTypeSelector extends StatelessWidget {
ButtonSegment( ButtonSegment(
value: LotType.plant, value: LotType.plant,
label: Text(t.lotType.plant), label: Text(t.lotType.plant),
icon: const Icon(Icons.local_florist_outlined, size: 18), icon: const Icon(Symbols.potted_plant, size: 18),
), ),
], ],
selected: {value}, selected: {value},

View file

@ -1,5 +1,6 @@
import 'package:commons_core/commons_core.dart'; import 'package:commons_core/commons_core.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
/// Renders a glyph from the bundled `seedks` icon font, whose characters `a``k` /// Renders a glyph from the bundled `seedks` icon font, whose characters `a``k`
/// map to seed-themed pictograms (jar, packet, spoon). See /// map to seed-themed pictograms (jar, packet, spoon). See
@ -63,7 +64,7 @@ IconData _materialIconForKind(QuantityKind kind) => switch (kind) {
QuantityKind.cob || QuantityKind.ear => Icons.grass, QuantityKind.cob || QuantityKind.ear => Icons.grass,
QuantityKind.head || QuantityKind.seedHead => Icons.filter_vintage_outlined, QuantityKind.head || QuantityKind.seedHead => Icons.filter_vintage_outlined,
QuantityKind.fruit => Icons.local_florist_outlined, QuantityKind.fruit => Icons.local_florist_outlined,
QuantityKind.plant => Icons.local_florist_outlined, QuantityKind.plant => Symbols.potted_plant,
QuantityKind.pot => Icons.yard_outlined, QuantityKind.pot => Icons.yard_outlined,
QuantityKind.tray => Icons.grid_on_outlined, QuantityKind.tray => Icons.grid_on_outlined,
QuantityKind.bulb || QuantityKind.bulb ||

View file

@ -477,7 +477,11 @@ class _EditVarietySheetState extends State<_EditVarietySheet> {
Future<void> _showAddLotSheet(BuildContext context, VarietyDetailCubit cubit) { Future<void> _showAddLotSheet(BuildContext context, VarietyDetailCubit cubit) {
final t = context.t; final t = context.t;
final yearController = TextEditingController(); final currentYear = DateTime.now().year;
// Harvest years: current year down through the last four decades. Newest
// first because a freshly harvested lot is the common case.
final years = [for (var y = currentYear; y >= currentYear - 40; y--) y];
var selectedYear = currentYear;
var selectedType = LotType.seed; var selectedType = LotType.seed;
Quantity? selectedQuantity; Quantity? selectedQuantity;
return showModalBottomSheet<void>( return showModalBottomSheet<void>(
@ -508,14 +512,20 @@ Future<void> _showAddLotSheet(BuildContext context, VarietyDetailCubit cubit) {
}), }),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
TextField( DropdownButtonFormField<int>(
key: const Key('addLot.year'), key: const Key('addLot.year'),
controller: yearController, initialValue: selectedYear,
keyboardType: TextInputType.number,
decoration: InputDecoration( decoration: InputDecoration(
labelText: t.addLot.year, labelText: t.addLot.year,
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
), ),
items: [
for (final y in years)
DropdownMenuItem(value: y, child: Text('$y')),
],
onChanged: (value) {
if (value != null) setState(() => selectedYear = value);
},
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Text( Text(
@ -541,7 +551,7 @@ Future<void> _showAddLotSheet(BuildContext context, VarietyDetailCubit cubit) {
onPressed: () { onPressed: () {
cubit.addLot( cubit.addLot(
type: selectedType, type: selectedType,
year: int.tryParse(yearController.text.trim()), year: selectedYear,
quantity: selectedQuantity, quantity: selectedQuantity,
); );
Navigator.of(sheetContext).pop(); Navigator.of(sheetContext).pop();

View file

@ -48,6 +48,7 @@ dependencies:
path_provider: ^2.1.5 path_provider: ^2.1.5
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
material_symbols_icons: ^4.2951.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View file

@ -83,14 +83,17 @@ void main() {
await tester.tap(find.byKey(const Key('detail.addLot'))); await tester.tap(find.byKey(const Key('detail.addLot')));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.enterText(find.byKey(const Key('addLot.year')), '2023'); await tester.tap(find.byKey(const Key('addLot.year')));
await tester.tap(find.text('handful')); // pick the unit from the scale await tester.pumpAndSettle();
await tester.tap(find.text('2023').last); // pick the year from the dropdown
await tester.pumpAndSettle();
await tester.tap(find.text('spoon')); // pick the unit from the scale
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.enterText(find.byKey(const Key('quantity.count')), '2'); await tester.enterText(find.byKey(const Key('quantity.count')), '2');
await tester.tap(find.byKey(const Key('addLot.save'))); await tester.tap(find.byKey(const Key('addLot.save')));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.text('Year 2023 · 2 handfuls'), findsOneWidget); expect(find.text('Year 2023 · 2 spoons'), findsOneWidget);
await disposeTree(tester); await disposeTree(tester);
}); });

View file

@ -644,6 +644,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.13.0" version: "0.13.0"
material_symbols_icons:
dependency: transitive
description:
name: material_symbols_icons
sha256: "49c532dd0b74544e9d8d93ec0f821d52ec532e7c9263c889ebe71b1be4f34ba7"
url: "https://pub.dev"
source: hosted
version: "4.2951.0"
meta: meta:
dependency: transitive dependency: transitive
description: description: