
Aman Kumar
Flutter Developer, Gigawave
Flutter Developer, Gigawave
You start with GetX. Life seems good. You use Get.to()
and Get.toNamed()
. Suddenly:
Get.snackbar()
: Ghosted. No show.Get.dialog()
: Vanishes faster than your motivation on Monday.Get.bottomSheet()
: Absolutely nothing. Dead.Back Button
: Nope. Stack who? History where?Deep Links and Web Features Are Broken:
/route-name
: direct open = broken or 404Reload
: new app instanceBack/forward
: random behaviorInstead of using GetMaterialApp
, use MaterialApp.router
and let GoRouter do what it does best—ROUTING. And let GetX be the boss of overlays, snackbars, and state management.
1void main() {
2 runApp(MyApp());
3}
4
5class MyApp extends StatelessWidget {
6 final _router = GoRouter(
7 navigatorKey: Get.key,
8 routes: [
9 GoRoute(
10 path: '/',
11 builder: (context, state) => HomePage(),
12 ),
13 GoRoute(
14 path: '/about',
15 builder: (context, state) => AboutPage(),
16 ),
17 ],
18 );
19
20
21 Widget build(BuildContext context) {
22 return MaterialApp.router(
23 routerConfig: _router,
24 navigatorKey: Get.key,
25 );
26 }
27}
Code | Explanation |
---|---|
navigatorKey: Get.key | Enables GetX to show overlays like snackbar, dialog, bottomsheet |
MaterialApp.router | Required by GoRouter for structured web routing |
GoRoute | Defines your routes in a declarative, clean way |
state.extra
in GoRouterNeed to pass arguments across routes, even on web reloads or back/forward presses? Here's how to use state.extra
with fallback logic for a smooth experience.
1class ProductDetailArgs {
2 final ProductPreviewModel? proPreview;
3 final ProductModel? product;
4
5 ProductDetailArgs({this.proPreview, this.product});
6}
extra
1GoRouter.of(context).pushNamed(
2 WebRouteNames.webProductDetailPage,
3 extra: ProductDetailArgs(
4 proPreview: preview,
5 product: product,
6 ),
7);
Important: If you're using back/forward buttons or refreshing the browser, state.extra
will be null. So you must persist the data manually.
1ProductDetailArgs? savedArg;
2
3GoRoute(
4 path: WebRouteNames.webProductDetailPage,
5 builder: (context, state) {
6 ProductDetailArgs? args = state.extra as ProductDetailArgs?;
7 if (args != null) {
8 savedArg = args;
9 }
10
11 return WebProductDetailsPage(
12 proPreview: savedArg?.proPreview,
13 product: savedArg?.product,
14 );
15 },
16);
Reason | When/Why |
---|---|
state.extra is null | on browser refresh |
Back button doesn't re-call | pushNamed() |
Prevents crashes | and allows fallback |
For full persistence (e.g. on refresh), you can save to local/session storage:
1// Save
2window.localStorage.setItem('last_product', jsonEncode(args));
3
4// Retrieve (on null extra)
5var storedData = window.localStorage.getItem('last_product');
1GoRoute(
2 path: WebRouteNames.webOtpPage,
3 builder: (context, state) {
4 final args = state.extra as Map<String, dynamic>?;
5 final OTPType otpType = args?['otpType'];
6 final bool ifNewUser = args?['ifNewUser'];
7
8 return WebOTPPage(
9 otpType: otpType,
10 ifNewUser: ifNewUser,
11 );
12 },
13);
When user presses back/forward in the browser, your page might rebuild without the arguments. Avoid crashes by:
state.extra
1// Safe builder with fallback
2builder: (context, state) {
3 ProductDetailArgs? args = state.extra as ProductDetailArgs?;
4 if (args != null) savedArg = args;
5
6 return WebProductDetailsPage(
7 proPreview: savedArg?.proPreview,
8 product: savedArg?.product,
9 );
10}
Pro tip: Use extra
to keep your routing clean and argument-safe, especially on Flutter Web.
Explore more resources: