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>
243 lines
7.7 KiB
Dart
243 lines
7.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:miler/views/helpers/constants/Font_constant.dart';
|
|
import 'dart:io';
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
import 'package:miler/views/helpers/constants/Colorconstants.dart';
|
|
import 'package:miler/views/helpers/widgets/app_widgets.dart';
|
|
import 'package:new_version_plus/new_version_plus.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:miler/views/introscreens/introscreen.dart';
|
|
import 'package:miler/helpers/rider_shell.dart';
|
|
|
|
class UpdateScreen extends StatefulWidget {
|
|
final bool mIsForceUpdate;
|
|
final String mCurrentVersion;
|
|
final String mUpdateVersion;
|
|
|
|
const UpdateScreen({
|
|
super.key,
|
|
this.mIsForceUpdate = true,
|
|
required this.mCurrentVersion,
|
|
required this.mUpdateVersion,
|
|
});
|
|
|
|
@override
|
|
State<UpdateScreen> createState() => _UpdateScreenState();
|
|
}
|
|
|
|
class _UpdateScreenState extends State<UpdateScreen>
|
|
with WidgetsBindingObserver {
|
|
bool _isCheckingVersion = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsBinding.instance.addObserver(this);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
WidgetsBinding.instance.removeObserver(this);
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
if (state == AppLifecycleState.resumed && !_isCheckingVersion) {
|
|
checkVersionAndNavigate();
|
|
}
|
|
}
|
|
|
|
Future<void> checkVersionAndNavigate() async {
|
|
if (_isCheckingVersion) return;
|
|
_isCheckingVersion = true;
|
|
|
|
try {
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
|
|
final newVersion = NewVersionPlus(
|
|
iOSId: '284882215',
|
|
androidId: "com.doormile.partner",
|
|
);
|
|
|
|
final status = await newVersion.getVersionStatus();
|
|
|
|
if (status != null) {
|
|
if (!status.canUpdate) {
|
|
if (mounted) {
|
|
_navigateToNextScreen();
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
debugPrint("Error checking version: $e");
|
|
} finally {
|
|
_isCheckingVersion = false;
|
|
}
|
|
}
|
|
|
|
Future<void> _navigateToNextScreen() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final isLoggedOut = prefs.getBool('logged_out') == true;
|
|
final savedUserId = prefs.getInt('userid');
|
|
|
|
if (!mounted) return;
|
|
|
|
if (!isLoggedOut && savedUserId != null && savedUserId > 0) {
|
|
// No duty gate — see the note in `app_bootstrap.dart`. Parcel or the
|
|
// ported delivery dashboard, per tenant — see [riderShell].
|
|
Get.offAll(() => riderShell());
|
|
} else {
|
|
Get.offAll(() => Introscreen());
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
double h = Get.height;
|
|
double w = Get.width;
|
|
|
|
return WillPopScope(
|
|
onWillPop: () async {
|
|
if (widget.mIsForceUpdate) {
|
|
SystemNavigator.pop();
|
|
return false;
|
|
}
|
|
return false;
|
|
},
|
|
child: Scaffold(
|
|
backgroundColor: const Color(0xFFF8F9FB),
|
|
extendBodyBehindAppBar: true,
|
|
appBar: AppBar(
|
|
backgroundColor: Colors.transparent,
|
|
elevation: 0,
|
|
scrolledUnderElevation: 0,
|
|
surfaceTintColor: Colors.transparent,
|
|
systemOverlayStyle: const SystemUiOverlayStyle(
|
|
statusBarColor: Colors.transparent,
|
|
statusBarIconBrightness: Brightness.dark,
|
|
statusBarBrightness: Brightness.light,
|
|
),
|
|
),
|
|
|
|
body: SafeArea(
|
|
top: true,
|
|
bottom: false, // Bottom safe area handled in bottomNavigationBar
|
|
child: LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
return SingleChildScrollView(
|
|
child: ConstrainedBox(
|
|
constraints: BoxConstraints(minHeight: constraints.maxHeight),
|
|
child: IntrinsicHeight(
|
|
child: Column(
|
|
children: [
|
|
/// top spacing (responsive)
|
|
SizedBox(height: h * 0.06),
|
|
|
|
/// 🚀 Image (auto scales)
|
|
Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: w * 0.08),
|
|
child: Image.asset(
|
|
"assets/images/update.png",
|
|
height: h * 0.28,
|
|
fit: BoxFit.contain,
|
|
),
|
|
),
|
|
|
|
SizedBox(height: h * 0.03),
|
|
|
|
/// 🔥 Title
|
|
Text(
|
|
"A New Update is Available!",
|
|
style: TextStyle(
|
|
fontSize: h * 0.025,
|
|
fontWeight: FontWeight.w600,
|
|
fontFamily: FontConstants.fontFamily,
|
|
color: ColorConstants.onSurface,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
|
|
SizedBox(height: h * 0.015),
|
|
|
|
/// 📄 Subtitle
|
|
Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: w * 0.08),
|
|
child: Text(
|
|
"New features are here to make your app experience even smoother and more user-friendly!",
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(
|
|
fontSize: h * 0.018,
|
|
color: ColorConstants.secondaryText,
|
|
height: 1.4,
|
|
fontFamily: FontConstants.fontFamily,
|
|
),
|
|
),
|
|
),
|
|
|
|
SizedBox(height: h * 0.03),
|
|
|
|
/// Version Text
|
|
Text(
|
|
"Available version: ${widget.mUpdateVersion}",
|
|
style: TextStyle(
|
|
fontSize: h * 0.017,
|
|
color: ColorConstants.secondaryText,
|
|
fontFamily: FontConstants.fontFamily,
|
|
),
|
|
),
|
|
|
|
Spacer(), // pushes content for large screens
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
|
|
/// ⭐ Bottom Button (fixed, responsive) with bottom safe area
|
|
bottomNavigationBar: SafeArea(
|
|
top: false,
|
|
bottom: true,
|
|
child: Padding(
|
|
padding: EdgeInsets.fromLTRB(w * 0.06, 0, w * 0.06, h * 0.03),
|
|
// Height and label size used to be `h * 0.065` and `h * 0.022`,
|
|
// so this button was a different size on every device — ~42dp on a
|
|
// small phone, ~52 on a large one, and far taller on a tablet. It
|
|
// is the same button doing the same job either way, so it takes the
|
|
// same fixed size as every other primary action.
|
|
child: MilerButton(
|
|
label: "Update now",
|
|
onPressed: () => downloadActions(),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void downloadActions() async {
|
|
String url;
|
|
var s = Platform.isAndroid ? "Android" : "Ios";
|
|
|
|
if (s == "Android") {
|
|
url =
|
|
'https://play.google.com/store/apps/details?id=com.doormile.partner';
|
|
} else {
|
|
url = 'https://apps.apple.com/us/app/doormile/id1596895375ls=1';
|
|
}
|
|
|
|
final uri = Uri.parse(url);
|
|
|
|
if (await canLaunchUrl(uri)) {
|
|
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
|
} else {
|
|
throw 'Could not launch App';
|
|
}
|
|
}
|
|
}
|