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

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
@@ -89,13 +90,13 @@ class _FaqPageState extends State<FaqPage> {
),
_Faq(
'I did not hear a new booking arrive.',
'Check Account → Alert Sound, play the sound to be sure you can hear it '
'Check Account > Alert Sound, play the sound to be sure you can hear it '
'over traffic, and make sure the phone is not on silent. Miler also '
'needs battery optimisation turned off to alert you reliably.',
),
_Faq(
'How do I change my name, photo or address?',
'Account → Edit Profile for your name, photo and email; Account → Saved '
'Account > Edit Profile for your name, photo and email; Account > Saved '
'Address for your address. Your mobile number is fixed to the one '
'your hub registered — contact support to change it.',
),
@@ -173,7 +174,7 @@ class _FaqPageState extends State<FaqPage> {
onChanged: (v) => setState(() => _query = v),
style: TextStyle(
fontSize: 14.5.sp,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
),
@@ -186,7 +187,7 @@ class _FaqPageState extends State<FaqPage> {
fontFamily: FontConstants.fontFamily,
),
prefixIcon: Icon(
Icons.search_rounded,
LucideIcons.search,
size: 20.sp,
color: ColorConstants.onSurfaceVariant,
),
@@ -195,7 +196,7 @@ class _FaqPageState extends State<FaqPage> {
: IconButton(
tooltip: 'Close',
icon: Icon(
Icons.close_rounded,
LucideIcons.x,
size: 18.sp,
color: ColorConstants.onSurfaceVariant,
),
@@ -222,7 +223,7 @@ class _FaqPageState extends State<FaqPage> {
child: Column(
children: [
Icon(
Icons.search_off_rounded,
LucideIcons.searchX,
size: 34.sp,
color: ColorConstants.onSurfaceVariant,
),
@@ -232,7 +233,7 @@ class _FaqPageState extends State<FaqPage> {
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
),
@@ -265,7 +266,7 @@ class _FaqPageState extends State<FaqPage> {
'Still stuck?',
style: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
),
@@ -283,7 +284,7 @@ class _FaqPageState extends State<FaqPage> {
SizedBox(height: 14.h),
MilerButton(
label: 'Contact support',
icon: Icons.mail_outline_rounded,
icon: LucideIcons.mail,
variant: MilerButtonVariant.tonal,
height: ButtonSizes.secondary,
onPressed: () => openSheet(context, const HelpCenterMessage()),
@@ -329,7 +330,7 @@ class _FaqTileState extends State<_FaqTile> {
widget.faq.question,
style: TextStyle(
fontSize: 14.5.sp,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
height: 1.35,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
@@ -342,7 +343,7 @@ class _FaqTileState extends State<_FaqTile> {
duration: const Duration(milliseconds: 220),
curve: Curves.easeOutCubic,
child: Icon(
Icons.expand_more_rounded,
LucideIcons.chevronDown,
size: 22.sp,
color: _open
? ColorConstants.primary

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -30,6 +31,9 @@ class HelpCenter extends StatelessWidget {
/// The hub's support line. One constant, used by the call row.
static const String _supportPhone = '+918925545000';
/// India's emergency number — see the note on `ProfilePage`.
static const String _emergencyNumber = '112';
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -43,13 +47,13 @@ class HelpCenter extends StatelessWidget {
SettingsGroup(
rows: [
SettingsRow(
icon: Icons.mail_outline_rounded,
icon: LucideIcons.mail,
title: 'Send a message',
subtitle: 'We usually reply within a day',
onTap: () => openSheet(context, const HelpCenterMessage()),
),
SettingsRow(
icon: Icons.call_outlined,
icon: LucideIcons.phone,
title: 'Call the hub',
subtitle: 'For anything that cannot wait',
onTap: () => _dial(context, _supportPhone),
@@ -61,7 +65,7 @@ class HelpCenter extends StatelessWidget {
SettingsGroup(
rows: [
SettingsRow(
icon: Icons.quiz_outlined,
icon: LucideIcons.circleHelp,
title: 'Browse the FAQ',
subtitle: 'Trips, skips, cash, alerts and payments',
onTap: () => openSheet(context, const FaqPage()),
@@ -86,7 +90,7 @@ class HelpCenter extends StatelessWidget {
'How can we help?',
style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
letterSpacing: -0.4,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
@@ -108,19 +112,13 @@ class HelpCenter extends StatelessWidget {
);
}
/// Distinct from support: this is the one for an accident or a threat, and it
/// is deliberately the only red-filled thing on the page.
Widget _emergency(BuildContext context) {
return GestureDetector(
onTap: () => _dial(context, '112'),
child: InfoBanner(
text:
'Emergency — tap to call for help if you are in immediate danger.',
icon: Icons.warning_amber_rounded,
color: ColorConstants.primary,
),
);
}
/// Distinct from support: this is the one for an accident or a threat, not
/// for a customer who will not pay. Same [EmergencyRow] the Account page
/// draws — it used to be a second, differently-worded copy of it.
Widget _emergency(BuildContext context) => EmergencyRow(
number: _emergencyNumber,
onTap: () => _dial(context, _emergencyNumber),
);
/// Says so when the dialer will not open, rather than absorbing the tap.
static Future<void> _dial(BuildContext context, String number) async {
@@ -222,7 +220,7 @@ class _HelpCenterMessageState extends State<HelpCenterMessage> {
'What has happened?',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
),
@@ -284,7 +282,7 @@ class _HelpCenterMessageState extends State<HelpCenterMessage> {
text,
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
),
@@ -304,7 +302,7 @@ class _HelpCenterMessageState extends State<HelpCenterMessage> {
validator: validator,
style: TextStyle(
fontSize: 14.5.sp,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
),

View File

@@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -11,7 +12,6 @@ import 'package:miler/views/helpers/constants/Colorconstants.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/settings_ui.dart';
import 'package:miler/views/helpers/constants/design_constants.dart';
/// ─────────────────────────────────────────────────────────────────────────
@@ -118,7 +118,7 @@ class _NotificationsPageState extends State<NotificationsPage> {
'Clear notifications?',
style: TextStyle(
fontSize: 17.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
),
@@ -140,7 +140,7 @@ class _NotificationsPageState extends State<NotificationsPage> {
'Cancel',
style: TextStyle(
color: ColorConstants.onSurfaceVariant,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
fontFamily: FontConstants.fontFamily,
),
),
@@ -151,7 +151,7 @@ class _NotificationsPageState extends State<NotificationsPage> {
'Clear',
style: TextStyle(
color: ColorConstants.primary,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
fontFamily: FontConstants.fontFamily,
),
),
@@ -181,7 +181,7 @@ class _NotificationsPageState extends State<NotificationsPage> {
'Clear',
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.primary,
fontFamily: FontConstants.fontFamily,
),
@@ -194,7 +194,7 @@ class _NotificationsPageState extends State<NotificationsPage> {
? const SkeletonList(count: 5)
: _items.isEmpty
? const MilerEmptyState(
icon: Icons.notifications_none_rounded,
icon: LucideIcons.bell,
title: 'No notifications yet',
message:
'Updates about your bookings, your shift and your '
@@ -204,17 +204,30 @@ class _NotificationsPageState extends State<NotificationsPage> {
onRefresh: _load,
color: ColorConstants.primary,
backgroundColor: ColorConstants.surfaceContainerLowest,
// ── A ruled list, not a stack of cards ──
//
// Eight shadowed white cards, each holding one sentence behind
// an identical icon disc, was the exact idiom Activity and
// Account were rebuilt to remove — notifications are the most
// list-shaped content in the app and were the last screen
// still wearing boxes. Hairlines separate; the tinted glyph
// anchors; nothing needs a container to be one item.
child: ListView.separated(
physics: const AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.fromLTRB(16.w, 14.h, 16.w, 28.h),
padding: EdgeInsets.fromLTRB(16.w, 6.h, 16.w, 28.h),
itemCount: _items.length,
separatorBuilder: (_, _) => SizedBox(height: 12.h),
separatorBuilder: (_, _) => Divider(
height: 1,
thickness: 1,
indent: 52.w,
color: ColorConstants.borderSubtle,
),
itemBuilder: (context, index) {
final it = _items[index];
return Reveal(
key: ValueKey('notif_${it['id'] ?? index}'),
delay: staggerDelay(index),
child: _NotificationCard(item: it),
child: _NotificationRow(item: it),
);
},
),
@@ -224,41 +237,57 @@ class _NotificationsPageState extends State<NotificationsPage> {
}
}
/// One notification, in the section's card.
/// One notification, as one row of the ruled list.
///
/// The kind badge is the only colour on the card, and it comes from the token
/// set: brand red for work, the money green for earnings, the info blue for
/// everything else. It used to be `Colors.green.shade700` on `green.shade50`,
/// `Colors.blue.shade700` on `blue.shade50` and a raw `0xFFFEE2E2` — three
/// palettes that appear nowhere else in the app.
class _NotificationCard extends StatelessWidget {
/// The kind glyph is the only colour on the row, and it comes from the token
/// set: brand red for work arriving, the app's done-green for work finished,
/// the money green for earnings, the info blue for everything else. A
/// completed booking used to wear the same truck as a new assignment — one
/// glyph for opposite facts — which is what made eight rows read as one row
/// eight times.
class _NotificationRow extends StatelessWidget {
final Map<String, dynamic> item;
const _NotificationCard({required this.item});
const _NotificationRow({required this.item});
({IconData icon, Color accent}) _kind() {
final type = (item['type'] ?? '').toString().toLowerCase();
final title = (item['title'] ?? '').toString().toLowerCase();
// Finished work first — its titles also contain 'booking', so the
// completed test must run before the assignment test or it never fires.
if (title.contains('completed') ||
title.contains('delivered') ||
type == 'completed') {
return (
icon: LucideIcons.circleCheck,
accent: ColorConstants.acceptGreen,
);
}
if (type == 'earning' ||
title.contains('payment') ||
title.contains('payout') ||
title.contains('earning')) {
return (
icon: Icons.account_balance_wallet_outlined,
accent: ColorConstants.moneyGreen,
);
return (icon: LucideIcons.wallet, accent: ColorConstants.moneyGreen);
}
if (type == 'task' ||
title.contains('pickup') ||
title.contains('booking') ||
title.contains('trip')) {
return (
icon: Icons.local_shipping_outlined,
accent: ColorConstants.primary,
);
return (icon: LucideIcons.truck, accent: ColorConstants.primary);
}
return (icon: Icons.info_outline_rounded, accent: ColorConstants.tertiary);
return (icon: LucideIcons.info, accent: ColorConstants.tertiary);
}
/// The server's relative stamps arrive ungrammatical — `1 hrs ago`,
/// `1 mins ago` — and were printed verbatim. Singular units drop the s;
/// everything else passes through untouched.
static String tidyTime(String raw) {
final t = raw.trim();
final m = RegExp(r'^1\s+(hrs|mins|secs|days)\s+ago$').firstMatch(t);
if (m == null) return t;
final unit = m.group(1)!;
return '1 ${unit.substring(0, unit.length - 1)} ago';
}
@override
@@ -266,12 +295,12 @@ class _NotificationCard extends StatelessWidget {
final kind = _kind();
final title = (item['title'] ?? 'Miler').toString();
final body = (item['body'] ?? '').toString();
final time = (item['time'] ?? 'Just now').toString();
final time = tidyTime((item['time'] ?? 'Just now').toString());
final imageUrl = (item['imageUrl'] ?? '').toString();
final imagePath = (item['imagePath'] ?? '').toString();
return SettingsCard(
padding: EdgeInsets.all(16.w),
return Padding(
padding: EdgeInsets.symmetric(vertical: 14.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -279,13 +308,13 @@ class _NotificationCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 42.w,
height: 42.w,
width: 38.w,
height: 38.w,
decoration: BoxDecoration(
color: kind.accent.withValues(alpha: 0.10),
shape: BoxShape.circle,
),
child: Icon(kind.icon, color: kind.accent, size: 21.sp),
child: Icon(kind.icon, color: kind.accent, size: 19.sp),
),
SizedBox(width: 14.w),
Expanded(
@@ -300,7 +329,7 @@ class _NotificationCard extends StatelessWidget {
title,
style: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
height: 1.3,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
@@ -312,7 +341,7 @@ class _NotificationCard extends StatelessWidget {
time,
style: TextStyle(
fontSize: 11.sp,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.onSurfaceVariant,
fontFamily: FontConstants.fontFamily,
),

View File

@@ -1,5 +1,6 @@
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -152,7 +153,7 @@ class _OrderAlertSoundPageState extends State<OrderAlertSoundPage> {
shape: BoxShape.circle,
),
child: Icon(
Icons.notifications_active_rounded,
LucideIcons.bellRing,
color: ColorConstants.primary,
size: 23.sp,
),
@@ -166,7 +167,7 @@ class _OrderAlertSoundPageState extends State<OrderAlertSoundPage> {
_currentLabel,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
),
@@ -197,9 +198,7 @@ class _OrderAlertSoundPageState extends State<OrderAlertSoundPage> {
Widget _soundRow(_SoundOption opt) {
final bool isSelected = _selected == opt.assetPath;
return SettingsRow(
icon: isSelected
? Icons.check_circle_rounded
: Icons.radio_button_unchecked_rounded,
icon: isSelected ? LucideIcons.circleCheck : LucideIcons.circle,
iconColor: isSelected
? ColorConstants.primary
: ColorConstants.onSurfaceVariant,
@@ -224,7 +223,7 @@ class _OrderAlertSoundPageState extends State<OrderAlertSoundPage> {
shape: BoxShape.circle,
),
child: Icon(
isPlaying ? Icons.graphic_eq_rounded : Icons.play_arrow_rounded,
isPlaying ? LucideIcons.audioLines : LucideIcons.play,
size: 20.sp,
color: isPlaying
? ColorConstants.onPrimary

View File

@@ -1,5 +1,6 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
@@ -146,7 +147,7 @@ class _ProfileState extends State<Profile> {
child: TextButton.icon(
onPressed: _pickImage,
icon: Icon(
Icons.photo_camera_outlined,
LucideIcons.camera,
size: 16.sp,
color: ColorConstants.primary,
),
@@ -154,7 +155,7 @@ class _ProfileState extends State<Profile> {
'Change Photo',
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.primary,
fontFamily: FontConstants.fontFamily,
),
@@ -165,14 +166,14 @@ class _ProfileState extends State<Profile> {
_field(
label: 'Full name',
controller: _nameCtrl,
icon: Icons.person_outline_rounded,
icon: LucideIcons.user,
hint: 'Enter your name',
),
SizedBox(height: 16.h),
_field(
label: 'Mobile number',
controller: _contactCtrl,
icon: Icons.phone_outlined,
icon: LucideIcons.phone,
hint: 'Mobile number',
readOnly: true,
prefix: '+91 ',
@@ -181,7 +182,7 @@ class _ProfileState extends State<Profile> {
_field(
label: 'Email address',
controller: _emailCtrl,
icon: Icons.mail_outline_rounded,
icon: LucideIcons.mail,
hint: 'you@example.com',
keyboardType: TextInputType.emailAddress,
),
@@ -189,7 +190,7 @@ class _ProfileState extends State<Profile> {
_field(
label: 'Address',
controller: _addressCtrl,
icon: Icons.location_on_outlined,
icon: LucideIcons.mapPin,
hint: 'Your address',
maxLines: 2,
),
@@ -282,7 +283,7 @@ class _ProfileState extends State<Profile> {
),
child: !hasImage
? Icon(
Icons.person_rounded,
LucideIcons.user,
color: ColorConstants.onSurfaceVariant,
size: 52.sp,
)
@@ -303,7 +304,7 @@ class _ProfileState extends State<Profile> {
),
),
child: Icon(
Icons.photo_camera_rounded,
LucideIcons.camera,
color: ColorConstants.onPrimary,
size: 15.sp,
),
@@ -332,7 +333,7 @@ class _ProfileState extends State<Profile> {
label,
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.onSurface,
letterSpacing: 0.2,
fontFamily: FontConstants.fontFamily,
@@ -346,7 +347,7 @@ class _ProfileState extends State<Profile> {
keyboardType: keyboardType,
style: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: readOnly
? ColorConstants.onSurfaceVariant
: ColorConstants.onSurface,
@@ -357,7 +358,7 @@ class _ProfileState extends State<Profile> {
prefixText: prefix,
prefixStyle: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
),
@@ -396,7 +397,7 @@ class _ProfileState extends State<Profile> {
),
suffixIcon: readOnly
? Icon(
Icons.lock_outline_rounded,
LucideIcons.lock,
size: 16.sp,
color: ColorConstants.secondaryText,
)

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
@@ -176,7 +177,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
'${s.tier.label} status',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
letterSpacing: -0.3,
color: Colors.white,
fontFamily: FontConstants.fontFamily,
@@ -188,7 +189,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
'$daysLeft ${daysLeft == 1 ? 'day' : 'days'} left',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: Colors.white.withValues(alpha: 0.78),
fontFamily: FontConstants.fontFamily,
),
@@ -204,7 +205,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
loading ? '—' : '${s.points}',
style: TextStyle(
fontSize: 30.sp,
fontWeight: FontWeight.w900,
fontWeight: FontWeight.w800,
letterSpacing: -1,
height: 1,
color: Colors.white,
@@ -216,7 +217,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
'points',
style: TextStyle(
fontSize: 11.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
letterSpacing: 0.4,
color: Colors.white.withValues(alpha: 0.78),
fontFamily: FontConstants.fontFamily,
@@ -243,7 +244,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
'${s.nextTier!.label}',
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: Colors.white,
fontFamily: FontConstants.fontFamily,
),
@@ -283,7 +284,9 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
color: tier.index <= s.tier.index
? ColorConstants.primary
: ColorConstants.borderStrong,
borderRadius: BorderRadius.circular(DesignConstants.radiusFull),
borderRadius: BorderRadius.circular(
DesignConstants.radiusFull,
),
),
),
SizedBox(height: 6.h),
@@ -294,8 +297,8 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
style: TextStyle(
fontSize: 10.5.sp,
fontWeight: tier == s.tier
? FontWeight.w800
: FontWeight.w600,
? FontWeight.w700
: FontWeight.w500,
color: tier == s.tier
? ColorConstants.primary
: ColorConstants.secondaryText,
@@ -306,7 +309,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
'${tier.threshold}',
style: TextStyle(
fontSize: 9.5.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
@@ -375,7 +378,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
Divider(height: 1, color: ColorConstants.dividerColor),
SizedBox(height: 16.h),
_meter(
icon: Icons.schedule_rounded,
icon: LucideIcons.clock,
label: 'Reached inside the ETA',
hit: s.onTime,
total: s.measured,
@@ -383,7 +386,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
),
SizedBox(height: 16.h),
_meter(
icon: Icons.alt_route_rounded,
icon: LucideIcons.route,
label: 'Rode the assigned route',
hit: s.onRoute,
total: s.measured,
@@ -401,7 +404,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
'cleared both checks',
style: TextStyle(
fontSize: 12.5.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.slateText,
fontFamily: FontConstants.fontFamily,
),
@@ -410,7 +413,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
SizedBox(width: 8.w),
MilerButton(
label: 'See stops',
icon: Icons.history_rounded,
icon: LucideIcons.history,
color: ColorConstants.primary,
height: ButtonSizes.compact,
expand: false,
@@ -430,7 +433,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
Icons.info_outline_rounded,
LucideIcons.info,
size: 13.sp,
color: ColorConstants.secondaryText,
),
@@ -444,7 +447,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
style: TextStyle(
fontSize: 11.5.sp,
height: 1.4,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
@@ -477,7 +480,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
child: Column(
children: [
_rule(
icon: Icons.timer_rounded,
icon: LucideIcons.timer,
title: 'Close a stop before its ETA',
body:
'Points scale with the distance you rode to reach it. '
@@ -485,7 +488,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
),
_ruleDivider(),
_rule(
icon: Icons.alt_route_rounded,
icon: LucideIcons.route,
title: 'Ride the route your hub assigned',
body:
'Wandering well past the planned distance breaks the '
@@ -493,7 +496,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
),
_ruleDivider(),
_rule(
icon: Icons.verified_rounded,
icon: LucideIcons.badgeCheck,
title: 'Clear both checks for a compliance point',
body:
'A stop that makes its ETA and stays on route earns one '
@@ -502,7 +505,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
),
_ruleDivider(),
_rule(
icon: Icons.event_repeat_rounded,
icon: LucideIcons.calendarSync,
title: 'Points reset each month',
body:
'Your status for the next period is set by the points '
@@ -524,7 +527,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
Icons.storefront_rounded,
LucideIcons.store,
size: 14.sp,
color: ColorConstants.secondaryText,
),
@@ -536,7 +539,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
style: TextStyle(
fontSize: 11.5.sp,
height: 1.45,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
@@ -552,7 +555,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
text,
style: TextStyle(
fontSize: 11.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
letterSpacing: 0.8,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
@@ -562,7 +565,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
TextStyle get _bodyStyle => TextStyle(
fontSize: 12.5.sp,
height: 1.45,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
);
@@ -577,7 +580,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
return Row(
children: [
Icon(
Icons.pending_actions_rounded,
LucideIcons.clipboardClock,
size: 22.sp,
color: ColorConstants.secondaryText,
),
@@ -624,7 +627,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
'$score%',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w900,
fontWeight: FontWeight.w800,
letterSpacing: -0.5,
height: 1,
color: accent,
@@ -636,7 +639,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
'SCORE',
style: TextStyle(
fontSize: 8.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
letterSpacing: 0.6,
height: 1,
color: accent,
@@ -654,7 +657,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
Row(
children: [
Icon(
Icons.workspace_premium_rounded,
LucideIcons.award,
size: 17.sp,
color: ColorConstants.acceptGreen,
),
@@ -667,7 +670,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
letterSpacing: -0.3,
color: ColorConstants.slateText,
fontFamily: FontConstants.fontFamily,
@@ -684,7 +687,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
style: TextStyle(
fontSize: 11.5.sp,
height: 1.35,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
@@ -725,7 +728,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.slateText,
fontFamily: FontConstants.fontFamily,
),
@@ -736,7 +739,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
'$hit/$total · $percent%',
style: TextStyle(
fontSize: 12.5.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
letterSpacing: -0.2,
color: accent,
fontFamily: FontConstants.fontFamily,
@@ -753,7 +756,9 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(DesignConstants.radiusFull),
borderRadius: BorderRadius.circular(
DesignConstants.radiusFull,
),
child: LinearProgressIndicator(
value: total == 0 ? 0 : hit / total,
minHeight: 8.h,
@@ -805,7 +810,7 @@ class _RiderRewardsPageState extends State<RiderRewardsPage> {
title,
style: TextStyle(
fontSize: 13.5.sp,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
letterSpacing: -0.2,
color: ColorConstants.slateText,
fontFamily: FontConstants.fontFamily,

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -116,7 +117,7 @@ class _SavedAddressPageState extends State<SavedAddressPage> {
'Edit',
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.primary,
fontFamily: FontConstants.fontFamily,
),
@@ -172,7 +173,7 @@ class _SavedAddressPageState extends State<SavedAddressPage> {
shape: BoxShape.circle,
),
child: Icon(
Icons.home_outlined,
LucideIcons.house,
color: ColorConstants.primary,
size: 22.sp,
),
@@ -186,7 +187,7 @@ class _SavedAddressPageState extends State<SavedAddressPage> {
'Home address',
style: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
),
@@ -209,7 +210,7 @@ class _SavedAddressPageState extends State<SavedAddressPage> {
SizedBox(height: 14.h),
MilerButton(
label: hasAddress ? 'Change address' : 'Add your address',
icon: hasAddress ? Icons.edit_outlined : Icons.add_rounded,
icon: hasAddress ? LucideIcons.pencil : LucideIcons.plus,
variant: MilerButtonVariant.tonal,
height: ButtonSizes.secondary,
onPressed: _startEditing,
@@ -232,7 +233,7 @@ class _SavedAddressPageState extends State<SavedAddressPage> {
'Home address',
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
),
@@ -248,7 +249,7 @@ class _SavedAddressPageState extends State<SavedAddressPage> {
style: TextStyle(
fontSize: 15.sp,
height: 1.45,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
),
@@ -297,7 +298,7 @@ class _SavedAddressPageState extends State<SavedAddressPage> {
'Cancel',
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: ColorConstants.onSurfaceVariant,
fontFamily: FontConstants.fontFamily,
),