phase3(critical-path): store + main + routes + nav components null-safe

- store/state + reducers: nullable AppState fields & reducer types
- main.dart: localeResolutionCallback Locale?, settings.name? guard
- routes.dart: fluro 2.0.5 HandlerFunc (BuildContext?) + params['x']!
- navigationbar/breadcrumbs/bottom_nav/text_link: nullable optional params/fields
This commit is contained in:
2026-07-24 13:37:04 +08:00
parent cef00db911
commit ae4c4046c3
6 changed files with 106 additions and 102 deletions

View File

@@ -103,7 +103,11 @@ class MyApp extends StatelessWidget {
S.delegate,
],
supportedLocales: S.delegate.supportedLocales,
localeResolutionCallback: (Locale locale, Iterable<Locale> supportedLocales) {
localeResolutionCallback: (Locale? locale, Iterable<Locale> supportedLocales) {
if (locale == null) {
store.dispatch(UpdateLocale(supportedLocales.first));
return supportedLocales.first;
}
print('Language code: ${locale.languageCode}, Country code: ${locale.countryCode}');
for (final supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale.languageCode) {
@@ -115,9 +119,9 @@ class MyApp extends StatelessWidget {
return supportedLocales.first;
},
onGenerateRoute: (RouteSettings settings) {
final List<String> pathElements = settings.name.split('/');
final List<String> pathElements = settings.name?.split('/') ?? [];
print('path elements: $pathElements');
if (pathElements[0] != '') {
if (pathElements.isEmpty || pathElements[0] != '') {
return null;
}
if (pathElements[1] == 'me') {

View File

@@ -44,169 +44,169 @@ class Routes {
static void configure() {
router.define('/', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return Home(title: Constants.APP_TITLE,);
}),
transitionType: TransitionType.fadeIn
);
router.define('/download', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return Download();
}),
transitionType: TransitionType.inFromRight
);
router.define('/minipos-learn-more', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return MiniPosLearnMore();
}),
transitionType: TransitionType.inFromRight
);
router.define('/igoshow-learn-more', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return IGoShowLearnMore();
}),
transitionType: TransitionType.inFromRight
);
router.define('/login', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return Login();
}),
transitionType: TransitionType.inFromRight
);
router.define('/me', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return Me();
}),
transitionType: TransitionType.inFromRight
);
router.define('/change-password', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return ChangePassword();
}),
transitionType: TransitionType.inFromRight
);
router.define('/user-profile', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return UserProfile();
}),
transitionType: TransitionType.inFromRight
);
router.define('/new-user', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return NewUser();
}),
transitionType: TransitionType.inFromRight
);
router.define('/set-password/:mobile/:code', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return SetPassword(params['mobile'][0], code: params['code'][0]);
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return SetPassword(params['mobile']![0], code: params['code']![0]);
}
));
router.define('/forgot-password', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return ForgotPassword();
}
));
router.define('/reset-password/:mobile/:code', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return ResetPassword(params['mobile'][0], code: params['code'][0]);
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return ResetPassword(params['mobile']![0], code: params['code']![0]);
}
));
router.define('/change-mobile-email/:ismobile', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
if (params['ismobile'][0] == '1') {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
if (params['ismobile']![0] == '1') {
return ChangeMobileOrEmail(true);
}
return ChangeMobileOrEmail(false);
}
));
router.define('/my-addresses/:business_id', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return MyAddresses(businessId: int.parse(params['business_id'][0]),);
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return MyAddresses(businessId: int.parse(params['business_id']![0]),);
}
));
router.define('/my-support/:business_id', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return MySupport(businessId: int.parse(params['business_id'][0]),);
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return MySupport(businessId: int.parse(params['business_id']![0]),);
}
));
router.define('/new-ticket/:business_id', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return NewTicket(businessId: int.parse(params['business_id'][0]),);
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return NewTicket(businessId: int.parse(params['business_id']![0]),);
}
));
router.define('/search-place/:business_id', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return SearchPlace(int.parse(params['business_id'][0]));
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return SearchPlace(int.parse(params['business_id']![0]));
}
));
router.define('/view-ticket/:ticket_id', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return ViewTicket(int.parse(params['ticket_id'][0]),);
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return ViewTicket(int.parse(params['ticket_id']![0]),);
}
));
router.define('/blog/:business_id', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return Blog(businessId: int.parse(params['business_id'][0]),);
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return Blog(businessId: int.parse(params['business_id']![0]),);
}
));
router.define('/view-blog/:bid', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return ViewBlog(int.parse(params['bid'][0]),);
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return ViewBlog(int.parse(params['bid']![0]),);
}
));
router.define('/shop/:business_id', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return Shop(businessId: int.parse(params['business_id'][0]),);
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return Shop(businessId: int.parse(params['business_id']![0]),);
}
));
router.define('/shop', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return Shop();
}
));
// router.define('/ocr-scan', handler: new Handler(
// handlerFunc: (BuildContext context, Map<String, List<String>> params) {
// handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
// return OCRScan();
// }
// ));
router.define('/checkout/:id', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return Checkout(int.parse(params['id'][0]));
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return Checkout(int.parse(params['id']![0]));
}),
);
router.define('/paynow/:orderId', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return PayNow(int.parse(params['orderId'][0]));
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return PayNow(int.parse(params['orderId']![0]));
}),
);
router.define('/orders', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return Orders();
}
));
router.define('/my-cards', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return MyCards();
}
));
router.define('/orderdetail/:orderId', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return OrderDetail(int.parse(params['orderId'][0]));
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return OrderDetail(int.parse(params['orderId']![0]));
}),
);
router.define('/new-comment/:orderId', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return NewComment(int.parse(params['orderId'][0]));
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return NewComment(int.parse(params['orderId']![0]));
}),
);
router.define('/coupons/:contactId', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return Coupons(int.parse(params['contactId'][0]));
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return Coupons(int.parse(params['contactId']![0]));
}),
);
router.define('/service-policy', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return PlainPage(
'service-policy',
// businessId: Constants.BUSINESS_ID,
@@ -215,7 +215,7 @@ class Routes {
}),
);
router.define('/return-policy', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return PlainPage(
'return-policy',
// businessId: Constants.BUSINESS_ID,
@@ -224,7 +224,7 @@ class Routes {
}),
);
router.define('/privacy-policy', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return PlainPage(
'privacy-policy',
// businessId: Constants.BUSINESS_ID,
@@ -233,7 +233,7 @@ class Routes {
}),
);
router.define('/end-user-license-agreement', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return PlainPage(
'end-user-license-agreement',
// businessId: Constants.BUSINESS_ID,
@@ -242,7 +242,7 @@ class Routes {
}),
);
router.define('/about-us', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return PlainPage(
'about-us',
// businessId: Constants.BUSINESS_ID,
@@ -251,29 +251,29 @@ class Routes {
}),
);
router.define('/contact-us', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return ContactUs(
businessId: Constants.BUSINESS_ID,
);
}),
);
router.define('/renew-license', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return RenewLicense();
}
));
router.define('/renew-minioffice/:gid', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return RenewMiniOffice(int.parse(params['gid'][0]));
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return RenewMiniOffice(int.parse(params['gid']![0]));
}
));
router.define('/buy-service/:gid/:servicename', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return BuyService(int.parse(params['gid'][0]), params['servicename'][0]);
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return BuyService(int.parse(params['gid']![0]), params['servicename']![0]);
}
));
router.define('/contact-stores', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return CreateOnlineStore1();
}
));

View File

@@ -2,7 +2,7 @@
import 'package:flutter/material.dart';
class BottomNav extends StatefulWidget {
const BottomNav({Key key}) : super(key: key);
const BottomNav({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() {

View File

@@ -7,10 +7,10 @@ import '../../routes.dart';
import 'navigationbar.dart';
class BreadCrumbs extends StatelessWidget {
final List<BreadCrumb> breadCrumbs;
final List<BreadCrumb>? breadCrumbs;
final bool hasBack;
final OnBackPress onBackPress;
const BreadCrumbs(this.hasBack, {this.breadCrumbs, Key key, this.onBackPress}) : super(key: key);
final OnBackPress? onBackPress;
const BreadCrumbs(this.hasBack, {this.breadCrumbs, Key? key, this.onBackPress}) : super(key: key);
@override
Widget build(BuildContext context) {
@@ -49,11 +49,11 @@ class BreadCrumbs extends StatelessWidget {
));
}
if (breadCrumbs != null) {
for (int i = 0; i < breadCrumbs.length; i++) {
BreadCrumb breadCrumb = breadCrumbs[i];
for (int i = 0; i < breadCrumbs!.length; i++) {
BreadCrumb breadCrumb = breadCrumbs![i];
if (breadCrumb.text == null && breadCrumb.item != null) {
if (breadCrumb.onTap == null) {
widgets.add(breadCrumb.item);
widgets.add(breadCrumb.item!);
} else {
widgets.add(MouseRegion(
cursor: SystemMouseCursors.click,
@@ -186,9 +186,9 @@ class BreadCrumbs extends StatelessWidget {
class BreadCrumb {
final String text;
final String route;
final IconData icon;
final Widget item;
final Function onTap;
final IconData? icon;
final Widget? item;
final GestureTapCallback? onTap;
BreadCrumb(this.text, this.route, {this.icon, this.item, this.onTap});
}

View File

@@ -9,17 +9,17 @@ import 'breadcrumbs.dart';
typedef OnBackPress();
class MiniNavigationBar extends StatefulWidget implements PreferredSizeWidget {
final Key key;
final Key? key;
final String title;
final bool back;
final bool toHome;
final bool showMe;
final List<BreadCrumb> breadCrumbs;
final double breadCrumbHeight;
final Widget shoppingCart;
final List<BreadCrumb>? breadCrumbs;
final double? breadCrumbHeight;
final Widget? shoppingCart;
MiniNavigationBar({Key key, PreferredSizeWidget bottom, String title,
bool back, bool toHome, bool showMe, this.breadCrumbs,
MiniNavigationBar({Key? key, PreferredSizeWidget? bottom, String? title,
bool? back, bool? toHome, bool? showMe, this.breadCrumbs,
this.breadCrumbHeight, this.shoppingCart})
: key = key,
preferredSize = breadCrumbHeight != null ? Size.fromHeight(kToolbarHeight + breadCrumbHeight) :
@@ -47,9 +47,9 @@ class MiniNavigationBarState extends State<MiniNavigationBar> {
mobile: MobileNavigationBar(title: widget.title, back: widget.back,
toHome: widget.toHome, showMe: widget.showMe,),
tablet: DesktopNavigationBar(hasBack: widget.back,
breadCrumbs: widget.breadCrumbs, shoppingCart: widget.shoppingCart,),
breadCrumbs: widget.breadCrumbs ?? [], shoppingCart: widget.shoppingCart ?? const SizedBox.shrink(),),
desktop: DesktopNavigationBar(hasBack: widget.back,
breadCrumbs: widget.breadCrumbs, shoppingCart: widget.shoppingCart,),
breadCrumbs: widget.breadCrumbs ?? [], shoppingCart: widget.shoppingCart ?? const SizedBox.shrink(),),
);
}

View File

@@ -11,22 +11,22 @@ import '../../routes.dart';
class TextLink extends StatelessWidget {
final String title;
final String url;
final Color color;
final Color hoverColor;
final double paddingHorizontal;
final double paddingVertical;
final FontWeight fontWeight;
final bool selected;
final Color? color;
final Color? hoverColor;
final double? paddingHorizontal;
final double? paddingVertical;
final FontWeight? fontWeight;
final bool? selected;
final bool isLink;
final bool replace;
final bool clearStack;
final bool maintainState;
final bool rootNavigator;
final TransitionType transition;
final TransitionType? transition;
final bool closeDrawer;
final bool isEmail;
final bool isPhone;
final double fontSize;
final double? fontSize;
final bool isAddress;
final TextOverflow overflow;
final int maxLines;
@@ -38,19 +38,19 @@ class TextLink extends StatelessWidget {
this.paddingVertical,
this.fontWeight,
this.selected,
bool isLink,
bool replace,
bool clearStack,
bool maintainState,
bool rootNavigator,
bool? isLink,
bool? replace,
bool? clearStack,
bool? maintainState,
bool? rootNavigator,
this.transition,
bool closeDrawer,
bool isEmail,
bool isPhone,
double fontSize,
bool isAddress,
TextOverflow overflow,
int maxLines,
bool? closeDrawer,
bool? isEmail,
bool? isPhone,
double? fontSize,
bool? isAddress,
TextOverflow? overflow,
int? maxLines,
}) :
isLink = isLink ?? false,
replace = replace ?? false,
@@ -60,7 +60,7 @@ class TextLink extends StatelessWidget {
closeDrawer = closeDrawer ?? false,
isEmail = isEmail ?? false,
isPhone = isPhone ?? false,
fontSize = fontSize ?? null,
fontSize = fontSize,
isAddress = isAddress ?? false,
overflow = overflow ?? TextOverflow.ellipsis,
maxLines = maxLines ?? 1;
@@ -101,7 +101,7 @@ class TextLink extends StatelessWidget {
},
),
decoration: BoxDecoration(
border: (selected != null && selected) ? Border(
border: (selected == true) ? Border(
bottom: BorderSide(
color: color ?? Colors.blue,
width: 3.0,
@@ -110,7 +110,7 @@ class TextLink extends StatelessWidget {
),
),
onTap: () async {
if (selected == null || !selected) {
if (selected != true) {
if (isEmail) {
Utils.openEmail(url);
} else if (isPhone) {