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

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:qr_flutter/qr_flutter.dart';
@@ -10,7 +11,7 @@ import 'package:miler/views/helpers/constants/Colorconstants.dart';
import 'package:miler/views/helpers/constants/design_constants.dart';
import 'package:miler/views/helpers/constants/Font_constant.dart';
import 'package:miler/views/helpers/widgets/app_widgets.dart';
import 'package:miler/views/helpers/widgets/page_transitions.dart';
import 'package:miler/views/helpers/widgets/miler_sheet_kit.dart';
/// ─────────────────────────────────────────────────────────────────────────
/// COLLECT PAYMENT — money taken at the door on the shipper's behalf.
@@ -119,7 +120,7 @@ extension _MethodUi on _Method {
: 'Customer scans and pays';
IconData get icon =>
this == _Method.cash ? Icons.payments_rounded : Icons.qr_code_2_rounded;
this == _Method.cash ? LucideIcons.banknote : LucideIcons.qrCode;
Color get accent => this == _Method.cash
? ColorConstants.acceptGreen
@@ -289,11 +290,12 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
}
void _declinePayment() async {
final ok = await showModalBottomSheet<bool>(
context: context,
sheetAnimationStyle: kMilerSheetStyle,
backgroundColor: Colors.transparent,
isScrollControlled: true,
// Through the kit. The route flags here were already the right ones by
// hand — transparent, scroll-controlled, the app's entrance — which is
// exactly the problem a presenter solves: four correct copies are four
// places for the fifth to be written wrong.
final ok = await showMilerSheet<bool>(
context,
builder: (_) => _ConfirmSheet(
title: 'Customer is not paying?',
body:
@@ -310,7 +312,6 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
/// See [AppFeedback].
void _toast(String message) => AppFeedback.error(context, message);
static String _fmt(double v) =>
v == v.roundToDouble() ? v.round().toString() : v.toStringAsFixed(2);
@@ -326,7 +327,10 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
backgroundColor: ColorConstants.pureSurface,
// Same bar as the verification screens this follows: taller, clear of
// the notch, title at a size that survives sunlight and arm's length.
appBar: milerPageBar('Collect payment', onBack: () => Navigator.pop(context)),
appBar: milerPageBar(
'Collect payment',
onBack: () => Navigator.pop(context),
),
body: Column(
children: [
Expanded(
@@ -376,7 +380,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
'DUE AT THIS STOP',
style: TextStyle(
fontSize: 10.5.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
letterSpacing: 0.8,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
@@ -390,7 +394,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
'₹${_fmt(_due)}',
style: TextStyle(
fontSize: 42.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
letterSpacing: -1.6,
height: 1.05,
color: ColorConstants.slateText,
@@ -405,7 +409,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
@@ -456,7 +460,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
label,
style: TextStyle(
fontSize: 12.5.sp,
fontWeight: strong ? FontWeight.w800 : FontWeight.w600,
fontWeight: strong ? FontWeight.w700 : FontWeight.w500,
color: strong
? ColorConstants.slateText
: ColorConstants.secondaryText,
@@ -468,7 +472,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
value,
style: TextStyle(
fontSize: strong ? 14.sp : 12.5.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
color: strong
? ColorConstants.slateText
: ColorConstants.secondaryText,
@@ -496,17 +500,16 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
Widget _orderCard() {
final facts = <(IconData, String, String)>[
if (_orderId.isNotEmpty)
(Icons.receipt_long_rounded, 'Consignment', _orderId),
if (_shipper.isNotEmpty)
(Icons.storefront_rounded, 'On behalf of', _shipper),
(LucideIcons.receiptText, 'Consignment', _orderId),
if (_shipper.isNotEmpty) (LucideIcons.store, 'On behalf of', _shipper),
if (_parcels > 0)
(
_kind.icon,
_parcels == 1 ? 'Parcel' : 'Parcels',
'$_parcels collected',
),
if (_weight.isNotEmpty) (Icons.scale_rounded, 'Weight', '$_weight kg'),
if (_itemNote.isNotEmpty) (Icons.notes_rounded, 'Noted', _itemNote),
if (_weight.isNotEmpty) (LucideIcons.weight, 'Weight', '$_weight kg'),
if (_itemNote.isNotEmpty) (LucideIcons.stickyNote, 'Noted', _itemNote),
];
return _card(
@@ -529,7 +532,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
style: TextStyle(
fontSize: 12.sp,
height: 1.35,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
@@ -555,7 +558,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12.5.sp,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
@@ -570,7 +573,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
letterSpacing: -0.2,
color: ColorConstants.slateText,
fontFamily: FontConstants.fontFamily,
@@ -598,7 +601,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
_field(
controller: _reference,
hint: 'UPI reference (optional)',
icon: Icons.tag_rounded,
icon: LucideIcons.tag,
keyboard: TextInputType.number,
formatters: [FilteringTextInputFormatter.digitsOnly],
),
@@ -628,7 +631,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
'Show this to the customer',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
color: ColorConstants.upiPurple,
fontFamily: FontConstants.fontFamily,
),
@@ -666,7 +669,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
color: ColorConstants.slateText,
fontFamily: FontConstants.fontFamily,
),
@@ -677,7 +680,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 11.5.sp,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
@@ -719,7 +722,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
m.label,
style: TextStyle(
fontSize: 14.5.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
color: ColorConstants.slateText,
fontFamily: FontConstants.fontFamily,
),
@@ -728,7 +731,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
m.blurb,
style: TextStyle(
fontSize: 11.5.sp,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
@@ -737,9 +740,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
),
),
Icon(
selected
? Icons.radio_button_checked_rounded
: Icons.radio_button_unchecked_rounded,
selected ? LucideIcons.circleDot : LucideIcons.circle,
size: 20.sp,
color: selected ? m.accent : ColorConstants.borderStrong,
),
@@ -787,7 +788,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
_field(
controller: _received,
hint: 'Amount you actually took',
icon: Icons.currency_rupee_rounded,
icon: LucideIcons.indianRupee,
keyboard: const TextInputType.numberWithOptions(decimal: true),
formatters: [
FilteringTextInputFormatter.allow(RegExp(r'[0-9.]')),
@@ -797,7 +798,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
_field(
controller: _shortReason,
hint: 'Why? e.g. customer had no change',
icon: Icons.notes_rounded,
icon: LucideIcons.stickyNote,
maxLines: 2,
capitalization: TextCapitalization.sentences,
),
@@ -831,11 +832,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
Icons.account_balance_wallet_rounded,
size: 17.sp,
color: ColorConstants.tertiary,
),
Icon(LucideIcons.wallet, size: 17.sp, color: ColorConstants.tertiary),
SizedBox(width: 10.w),
Expanded(
child: Text(
@@ -844,7 +841,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
style: TextStyle(
fontSize: 12.sp,
height: 1.4,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.tertiary,
fontFamily: FontConstants.fontFamily,
),
@@ -878,7 +875,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
@@ -893,7 +890,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
'Customer cannot pay',
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.errorRed,
fontFamily: FontConstants.fontFamily,
),
@@ -938,7 +935,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
'Finish the details above',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
@@ -962,7 +959,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14.5.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
color: Colors.white,
fontFamily: FontConstants.fontFamily,
),
@@ -973,7 +970,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
color: ColorConstants.pureSurface,
shape: BoxShape.circle,
),
child: Icon(Icons.check_rounded, size: 22.sp, color: _method.accent),
child: Icon(LucideIcons.check, size: 22.sp, color: _method.accent),
),
),
);
@@ -991,7 +988,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
'Collect payment',
style: TextStyle(
fontSize: 17.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
color: ColorConstants.slateText,
fontFamily: FontConstants.fontFamily,
),
@@ -1004,7 +1001,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.check_circle_rounded,
LucideIcons.circleCheck,
size: 54.sp,
color: ColorConstants.acceptGreen,
),
@@ -1013,7 +1010,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
'Nothing to collect here',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
color: ColorConstants.slateText,
fontFamily: FontConstants.fontFamily,
),
@@ -1025,7 +1022,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
style: TextStyle(
fontSize: 13.sp,
height: 1.4,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
@@ -1033,7 +1030,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
SizedBox(height: 20.h),
MilerButton(
label: 'Continue',
icon: Icons.arrow_forward_rounded,
icon: LucideIcons.arrowRight,
color: ColorConstants.acceptGreen,
onPressed: () => Navigator.pop(context, <String, dynamic>{
'paid': true,
@@ -1066,7 +1063,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
title,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
letterSpacing: -0.2,
color: ColorConstants.slateText,
fontFamily: FontConstants.fontFamily,
@@ -1101,7 +1098,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13.5.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
color: selected ? Colors.white : ColorConstants.slateText,
fontFamily: FontConstants.fontFamily,
),
@@ -1128,7 +1125,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
onChanged: (_) => setState(() {}),
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.slateText,
fontFamily: FontConstants.fontFamily,
),
@@ -1174,7 +1171,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
),
child: Row(
children: [
Icon(Icons.info_rounded, size: 16.sp, color: accent),
Icon(LucideIcons.info, size: 16.sp, color: accent),
SizedBox(width: 8.w),
Expanded(
child: Text(
@@ -1182,7 +1179,7 @@ class _CollectPaymentPageState extends State<CollectPaymentPage> {
style: TextStyle(
fontSize: 12.sp,
height: 1.35,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: accent,
fontFamily: FontConstants.fontFamily,
),
@@ -1208,65 +1205,38 @@ class _ConfirmSheet extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SafeArea(
top: false,
child: Container(
margin: EdgeInsets.all(14.r),
padding: EdgeInsets.all(18.r),
decoration: BoxDecoration(
color: ColorConstants.pureSurface,
borderRadius: BorderRadius.circular(DesignConstants.radiusXl),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 17.sp,
fontWeight: FontWeight.w800,
letterSpacing: -0.3,
color: ColorConstants.slateText,
fontFamily: FontConstants.fontFamily,
),
),
SizedBox(height: 7.h),
Text(
body,
style: TextStyle(
fontSize: 13.sp,
height: 1.45,
fontWeight: FontWeight.w600,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
),
SizedBox(height: 18.h),
Row(
children: [
Expanded(
child: MilerButton(
label: 'Go back',
variant: MilerButtonVariant.outlined,
color: ColorConstants.slateText,
height: ButtonSizes.secondary,
onPressed: () => Navigator.pop(context, false),
),
// The kit's fabric — this was a floating white card with margins on all
// sides, the one sheet in the app not anchored to the bottom edge.
return MilerSheetScaffold(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
MilerSheetHeader(title: title, subtitle: body),
SizedBox(height: 18.h),
Row(
children: [
Expanded(
child: MilerButton(
label: 'Go back',
variant: MilerButtonVariant.outlined,
color: ColorConstants.slateText,
height: ButtonSizes.secondary,
onPressed: () => Navigator.pop(context, false),
),
SizedBox(width: 10.w),
Expanded(
child: MilerButton(
label: confirmLabel,
color: ColorConstants.errorRed,
height: ButtonSizes.secondary,
onPressed: () => Navigator.pop(context, true),
),
),
SizedBox(width: 10.w),
Expanded(
child: MilerButton(
label: confirmLabel,
color: ColorConstants.errorRed,
height: ButtonSizes.secondary,
onPressed: () => Navigator.pop(context, true),
),
],
),
],
),
),
],
),
],
),
);
}