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:
94
test/support/golden_fonts.dart
Normal file
94
test/support/golden_fonts.dart
Normal file
@@ -0,0 +1,94 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:miler/views/helpers/constants/Font_constant.dart';
|
||||
|
||||
/// Fonts a reference shot needs, loaded once per suite.
|
||||
///
|
||||
/// ── Why every shot harness must call this ──
|
||||
///
|
||||
/// A golden rendered without a font is a picture of the fallback, and the
|
||||
/// fallback for an icon font is an empty box. That is not a loud failure — the
|
||||
/// test passes, the image is written, and a whole page of missing glyphs
|
||||
/// becomes the reference every future run is compared against.
|
||||
///
|
||||
/// It has happened twice. Poppins ships as separate weight files and had to be
|
||||
/// registered under one family before `w800` headings stopped rendering as
|
||||
/// smeared SemiBold; then Lucide arrived as a package-declared font the test
|
||||
/// binding does not resolve at all, and every icon on Activity came out as a
|
||||
/// hollow square. One loader, so the next family added to the app is added
|
||||
/// here rather than in five harnesses that will drift apart.
|
||||
Future<void> loadGoldenFonts() async {
|
||||
final poppins = FontLoader(FontConstants.fontFamily);
|
||||
for (final e in const <(String, FontWeight)>[
|
||||
('Light', FontWeight.w300),
|
||||
('Regular', FontWeight.w400),
|
||||
('Medium', FontWeight.w500),
|
||||
('SemiBold', FontWeight.w600),
|
||||
('Bold', FontWeight.w700),
|
||||
('ExtraBold', FontWeight.w800),
|
||||
('Black', FontWeight.w900),
|
||||
]) {
|
||||
final f = File('assets/fonts/Poppins/Poppins-${e.$1}.ttf');
|
||||
if (f.existsSync()) {
|
||||
poppins.addFont(f.readAsBytes().then((b) => ByteData.view(b.buffer)));
|
||||
}
|
||||
}
|
||||
await poppins.load();
|
||||
|
||||
await _loadIfPresent('MaterialIcons', const [
|
||||
'/opt/homebrew/share/flutter/bin/cache/artifacts/material_fonts/'
|
||||
'MaterialIcons-Regular.otf',
|
||||
]);
|
||||
|
||||
// ── The family name is not `Lucide` ──
|
||||
//
|
||||
// Its `IconData` carries `fontPackage: 'lucide_icons_flutter'`, and Flutter
|
||||
// resolves a packaged font under `packages/<package>/<family>`. Registering
|
||||
// it as plain `Lucide` loads a font nothing ever asks for, which looks
|
||||
// exactly like not loading it at all: every glyph renders as a hollow box
|
||||
// and the golden is written anyway.
|
||||
//
|
||||
// Both names are registered — the prefixed one is what the widgets resolve,
|
||||
// and the bare one costs nothing and covers any direct use.
|
||||
final lucide = _lucideCandidates();
|
||||
await _loadIfPresent('packages/lucide_icons_flutter/Lucide', lucide);
|
||||
await _loadIfPresent('Lucide', lucide, quiet: true);
|
||||
}
|
||||
|
||||
Future<void> _loadIfPresent(
|
||||
String family,
|
||||
List<String> paths, {
|
||||
bool quiet = false,
|
||||
}) async {
|
||||
for (final p in paths) {
|
||||
final f = File(p);
|
||||
if (!f.existsSync()) continue;
|
||||
final loader = FontLoader(family)
|
||||
..addFont(f.readAsBytes().then((b) => ByteData.view(b.buffer)));
|
||||
await loader.load();
|
||||
return;
|
||||
}
|
||||
// Absent is reported, never silent: the alternative is a reference image
|
||||
// full of empty boxes that nothing ever flags.
|
||||
if (quiet) return;
|
||||
// ignore: avoid_print
|
||||
print('[GOLDEN] $family not found — its glyphs will render as boxes');
|
||||
}
|
||||
|
||||
List<String> _lucideCandidates() {
|
||||
final home = Platform.environment['HOME'] ?? '';
|
||||
final root = Directory('$home/.pub-cache/hosted/pub.dev');
|
||||
if (!root.existsSync()) return const [];
|
||||
return [
|
||||
for (final d in root.listSync().whereType<Directory>())
|
||||
if (d.path
|
||||
.split(Platform.pathSeparator)
|
||||
.last
|
||||
.startsWith('lucide_icons_flutter-'))
|
||||
'${d.path}/assets/lucide.ttf',
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user