feat(ux): edge fades hint that filter rows scroll horizontally

Chips clipped at the viewport edge with no cue that more filters exist.
New reusable EdgeFade widget (ShaderMask + dstIn) fades an edge only
while content remains beyond it, driven by scroll metrics; RTL mirrors
via Directionality. Applied to the inventory filter bar, market filter
bar and calendar month strip.
This commit is contained in:
vjrj 2026-07-11 07:14:19 +02:00
parent 1ab243f29e
commit bfff95fe8d
5 changed files with 262 additions and 46 deletions

View file

@ -8,6 +8,7 @@ import '../data/variety_repository.dart';
import '../domain/crop_calendar.dart';
import '../i18n/strings.g.dart';
import 'category_palette.dart';
import 'edge_fade.dart';
import 'theme.dart';
/// One crop-calendar phase as shown on the "this month" screen: its label,
@ -145,30 +146,32 @@ class _MonthStripState extends State<_MonthStrip> {
final fmt = DateFormat.MMM(locale.toLanguageTag());
return SizedBox(
height: 56,
child: ListView.separated(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
itemCount: 12,
separatorBuilder: (_, _) => const SizedBox(width: 8),
itemBuilder: (context, i) {
final m = i + 1;
final selected = m == widget.month;
return ChoiceChip(
key: Key('calendar.month.$m'),
labelPadding: const EdgeInsets.symmetric(horizontal: 6),
label: Text(
_capitalise(fmt.format(DateTime(2000, m))),
key: selected ? _selectedKey : null,
),
selected: selected,
onSelected: (_) => widget.onSelect(m),
selectedColor: seedPrimaryContainer,
labelStyle: TextStyle(
color: selected ? seedOnPrimaryContainer : seedMuted,
fontWeight: selected ? FontWeight.w600 : FontWeight.w400,
),
);
},
child: EdgeFade(
child: ListView.separated(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
itemCount: 12,
separatorBuilder: (_, _) => const SizedBox(width: 8),
itemBuilder: (context, i) {
final m = i + 1;
final selected = m == widget.month;
return ChoiceChip(
key: Key('calendar.month.$m'),
labelPadding: const EdgeInsets.symmetric(horizontal: 6),
label: Text(
_capitalise(fmt.format(DateTime(2000, m))),
key: selected ? _selectedKey : null,
),
selected: selected,
onSelected: (_) => widget.onSelect(m),
selectedColor: seedPrimaryContainer,
labelStyle: TextStyle(
color: selected ? seedOnPrimaryContainer : seedMuted,
fontWeight: selected ? FontWeight.w600 : FontWeight.w400,
),
);
},
),
),
);
}