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:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user