Miler rider app: surface system, visible design language, backend lifecycle

Design system
- MilerSurface ladder (canvas → working → raised → floating) with MilerPanel
  as layer 1; canvas moved to #DEE3EA so white separates at 1.290:1.
- Visible vocabulary applied across Home, Deliveries, Activity, Account and
  the sheets: hero heads (tabular numeral + small caption, clamped at 1.3x),
  canvas wells for anything that opens, small filled tags for shelf labels,
  demoted placeholders. Recorded in DESIGN_SYSTEM.md §6.
- One icon family: 222 Material glyphs migrated to Lucide; none left outside
  lib/xpress.
- Colour semantics corrected: amber only for what is genuinely owed, brand red
  reserved for the live stop, disabled primaries go neutral rather than pale.

Data and lifecycle
- lib/data/lifecycle.dart reads mutations for what they prove; route_order.dart
  makes admin sequence the single ordering authority; service_day.dart, and
  stop_area.dart rewritten against live Coimbatore addresses (digit-token
  stripping, city stoplist, street suffixes, stammer collapse).
- countLabel states the load once, in bags.

Testing
- 1440 tests passing; golden shot harnesses for Home, Deliveries, Activity,
  sheets and verify, with test/failures/ now gitignored (diff debris).
- New pins: home_gutter_test, stop_area_test, plus updated structural bounds.

Note: this commit also carries pre-existing working-tree deletions that were
present before this work (API_SPEC.md, README.md, demo test fixtures).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-22 05:40:35 +05:30
parent c8350563d9
commit d7348e253f
387 changed files with 80693 additions and 12272 deletions

View File

@@ -52,8 +52,9 @@ void main() {
if (n is! ScrollUpdateNotification) return false;
final delta = n.scrollDelta ?? 0;
if (delta == 0) return false;
accum.value =
(accum.value * delta > 0) ? accum.value + delta : delta;
accum.value = (accum.value * delta > 0)
? accum.value + delta
: delta;
if (delta > 0 && compact.value == 0 && accum.value > 24) {
accum.value = 0;
compact.value = 1;
@@ -96,8 +97,9 @@ void main() {
double barHeight(WidgetTester tester) =>
tester.getSize(find.byKey(barKey)).height;
testWidgets('scrolling down shrinks the bar; scrolling back restores it',
(tester) async {
testWidgets('scrolling down shrinks the bar; scrolling back restores it', (
tester,
) async {
final compact = ValueNotifier(0.0);
final accum = ValueNotifier(0.0);
addTearDown(compact.dispose);
@@ -117,8 +119,9 @@ void main() {
expect(barHeight(tester), resting);
});
testWidgets('the bar never leaves — the rider can always change tab',
(tester) async {
testWidgets('the bar never leaves — the rider can always change tab', (
tester,
) async {
// This is the whole reason the behaviour changed. Whatever the rider does
// with his thumb, the tab targets stay on screen and stay tappable.
final compact = ValueNotifier(0.0);
@@ -130,10 +133,16 @@ void main() {
for (final drag in const [-300.0, -600.0, -1200.0]) {
await tester.drag(find.byType(ListView), Offset(0, drag));
await tester.pumpAndSettle();
expect(barHeight(tester), greaterThanOrEqualTo(compactHeight),
reason: 'the bar must never collapse past its compact height');
expect(find.byType(Icon), findsOneWidget,
reason: 'and its icons must stay on screen');
expect(
barHeight(tester),
greaterThanOrEqualTo(compactHeight),
reason: 'the bar must never collapse past its compact height',
);
expect(
find.byType(Icon),
findsOneWidget,
reason: 'and its icons must stay on screen',
);
}
});
@@ -155,20 +164,27 @@ void main() {
// The Align is what gives the room back; the Text keeps its own size and is
// clipped, so measuring the Text would report the same height either way.
final align = tester.getSize(
find.ancestor(of: find.byKey(labelKey), matching: find.byType(Align)).first,
find
.ancestor(of: find.byKey(labelKey), matching: find.byType(Align))
.first,
);
expect(
align.height,
lessThan(1),
reason: 'the label must hand its height back, not just go invisible',
);
expect(align.height, lessThan(1),
reason: 'the label must hand its height back, not just go invisible');
});
testWidgets('a page too short to scroll never compacts the bar',
(tester) async {
testWidgets('a page too short to scroll never compacts the bar', (
tester,
) async {
final compact = ValueNotifier(0.0);
final accum = ValueNotifier(0.0);
addTearDown(compact.dispose);
addTearDown(accum.dispose);
await tester
.pumpWidget(shell(compact: compact, accum: accum, scrollable: false));
await tester.pumpWidget(
shell(compact: compact, accum: accum, scrollable: false),
);
await tester.drag(find.byType(ListView), const Offset(0, -300));
await tester.pumpAndSettle();