
Update Notice!!!
The explaination below is somewhat outdated. Now, I recommends you to usego_router, a routing package using Flutter Navigation 2.0!
Intro
When I’m on a flutter web, I faced with routing issue. BothMaterialApp
andGetMaterialApp
had the same issues, which was routing acts weird when browser’s back button and refresh button are clicked. So I’ve tried some test project with flutter web and I found the solution. Now, it works just like native web project. I’d like to share this solution with you.
Flutter Navigation 2.0
There is this thing calledFlutter Navigation 2.0
. I didn’t dig much about it, but to simply put, it’s navigation system is now more customizable with this Navigation 2.0.
Since the original routing system had so many problems in web platform, I had to try this one. And it worked, thankfully. You can use withMaterialApp.router
instead of justMaterialApp
. But if you do that you need to implement some override method which involves in navigation logic. Instead overriding each methods, I usedGetMaterialApp.router
which comes with default methods, and therefore, only extra thing to do is just build method.
Code Implementation
Install Getx
At this moment, I used below version ofGetx
package.
dependencies:...get:^4.6.1
Basic Routing Stuff
For convenience, I used simple routing path and getPages with snippet.
abstractclassRoutes{staticconstHOME='/';staticconstLOGIN='/login';staticconstSIGNUP='/signup';}
abstractclassAppPages{staticfinalpages=[GetPage(name:Routes.HOME,page:()=>Home(),),GetPage(name:Routes.LOGIN,page:()=>Login(),),GetPage(name:Routes.SIGNUP,page:()=>Signup(),),];}
CreateGetMaterialApp.router
When you useGetMaterialApp.router
instead of justGetMaterialApp
, you don’t need to provide(and you can’t)initialRoute
. But you need to providegetPages
androuterDelegate
fields with values.
import'package:flutter/material.dart';import'package:get/get.dart';voidmain(){runApp(GetMaterialApp.router(debugShowCheckedModeBanner:false,defaultTransition:Transition.fade,getPages:AppPages.pages,routerDelegate:AppRouterDelegate(),));}
Create Sample Pages
For testing, I created some simple test pages with different background colors.
- Note that when you navigate between pages, you need to use
Get.rootDelegate
instead ofGet
because we’re usingrouterDelegate
to handle routing.
classHomeextendsStatelessWidget{constHome({Key?key}):super(key:key);@overrideWidgetbuild(BuildContextcontext){returnContainer(color:Colors.red,child:TextButton(child:Text('Home',style:TextStyle(color:Colors.white),),onPressed:()=>Get.rootDelegate.toNamed(Routes.LOGIN),),);}}classLoginextendsStatelessWidget{constLogin({Key?key}):super(key:key);@overrideWidgetbuild(BuildContextcontext){returnContainer(color:Colors.orange,child:TextButton(child:Text('Login',style:TextStyle(color:Colors.white),),onPressed:()=>Get.rootDelegate.toNamed(Routes.SIGNUP),),);}}classSignupextendsStatelessWidget{constSignup({Key?key}):super(key:key);@overrideWidgetbuild(BuildContextcontext){returnContainer(color:Colors.blue,child:TextButton(child:Text('Signup',style:TextStyle(color:Colors.white),),onPressed:()=>Get.rootDelegate.toNamed(Routes.HOME),),);}}
Create router delegate withGetDelegate
So, this is the main part.
- Create
AppRouterDelegate
or what ever you like to call withGetDelegate
extended. - Override
build
method and returnNavigator
withonPopPage
andpages
.onPopPage
→ This is invoked when pop action happened, and return wheather pop is successful or not. If you don’t want to block pop action, just leave it there with default code.pages
→ This is different fromgetPages
. This is more related to what page to show in current moment. I’m not fully figured out yet, but it’s also different from navigation history stack. So, in order to show the right page according to current url, I usedcurrentConfiguration.currentPage
. Note that this field can’t be empty since it related to current view. WhencurrentConfiguration
is null(which means first entered the page), you can just simply return initial page.
classAppRouterDelegateextendsGetDelegate{@overrideWidgetbuild(BuildContextcontext){returnNavigator(onPopPage:(route,result)=>route.didPop(result),pages:currentConfiguration!=null?[currentConfiguration!.currentPage!]:[GetNavConfig.fromRoute(Routes.HOME)!.currentPage!],);}}
And plus, since you can writebuild
method and other override method(if you want to customize login) you can easily add user authentication status check before navigate to any page.
Conclusion
I think Flutter Web has a long way to go, but I’m glad Flutter is keeping up there work! I hope this article can help those who struggles with Flutter Web routing issue.
Cheers!
Top comments(22)

Hello!! This article was really helpful! Thank you
But how did you handle it likeGet.back()
? An error occurs in this process. Same asGet.rootDelegate.popRoute()
.
I would ultimately like to implement the following elements.
- Go back, go forward after refresh in a web browser. -> Success
- Widgets that return to the previous page when the
<-
button is pressed (previously useGet.back()
)

- LocationSeoul, South Korea
- EducationUniv
- PronounsShe / Her
- WorkIndie Developer
- Joined
You can do that by usingGet.roodDelegate.popHistory
with custom logic.
- First, override
popHistory
method inAppRouterDelegate
classAppRouterDelegateextendsGetDelegate{GetNavConfiggetprevRoute=>// herehistory.length<2?history.last:history[history.length-2];@overrideFuture<GetNavConfig>popHistory()async{// and herefinalresult=prevRoute;Get.rootDelegate.offNamed(prevRoute.currentPage!.name);returnresult;}@overrideWidgetbuild(BuildContextcontext){returnNavigator(......);}}
- And call it like this
Get.rootDelegate.popHistory();
I've tested it and it works for me. Hope this can help you :-)

Hi, thanks for the article. I tested the code and found out the browser back history did not really get back to the previous page state. For example when i scroll to certain location and navigate back again, instead of getting back new page was loaded. Is there any way we can preserve the page state using your code?

- LocationSeoul, South Korea
- EducationUniv
- PronounsShe / Her
- WorkIndie Developer
- Joined
Yes, I also found some issue of that approach. As of now, the simplest way to solve this problem is using@kkimj 's method. (window.history.back()
) If you want to manually manage your history stack, then check outpushHistory
method in Delegete class. Maybe you can cache those records and write your own back method logic.

when i did it your way on the flutter web,Get.rootDelegate.toNamed("/")
works, butGet.rootDelegate.offToNamed("/")
throws an Unexpected null value error. I think there is an error related to the key, but I can't figure it out.
AlsoGet.snackbar()
throws the same error.

class AppRouterDelegate extends GetDelegate {
@override
Widget build(BuildContext context) {
return Navigator(
key: Get.key, //Please add this line to get snackbar,dialog,etc.
onPopPage: (route, result) => route.didPop(result),
pages: currentConfiguration != null
? [currentConfiguration!.currentPage!]
: [GetNavConfig.fromRoute(Routes.HOME)!.currentPage!],
);
}
}

hi my friends i use this article for my app route but im using this code on flutter web and when refresh or back,forward button in browser clicked nothing happend in my app.. how can handle this ?
my code like this
class AppRouterDelegate extends GetDelegate {
GetNavConfig get prevRoute => // here
history.length < 2 ? history.last : history[history.length - 2];
@override
Future popHistory() async {
// and here
final result = prevRoute;
Get.rootDelegate.offNamed(prevRoute.currentPage!.name);
return result;
}
@override
Widget build(BuildContext context) {
GlobalKey mainNavigatorKey = GlobalKey();
return Navigator( key: mainNavigatorKey, onPopPage: (route, result) => route.didPop(result), pages: currentConfiguration != null ? [currentConfiguration!.currentPage!] : [GetNavConfig.fromRoute(Routes.LOGIN)!.currentPage!],);
}
}

- LocationEgypt
- WorkSoftware Engineer
- Joined
Hello !! the solution of overridingpopHistory()
not working, I can't go back to the previous page on mobile, is there any solution can handle it likeGet.back()
but in navigator 2.0

@swimmingkiim why you recommend to user go_router?

Yes, you can mix GetX pages and GoRouter.
Like this sample.
Future<void> main() async { usePathUrlStrategy(); GoRouter.optionURLReflectsImperativeAPIs = true; var router = GoRouter(navigatorKey: Get.key, initialLocation: '/', routes: [ GoRoute( name: 'home', path: '/', builder: (context, state) => YourHomePage(), ), GoRoute( name: 'page2', path: '/page2', builder: (context, state) => YourPage2(), ) ]); runApp(GetMaterialApp.router( debugShowCheckedModeBanner: false, routeInformationParser: router.routeInformationParser, routerDelegate: router.routerDelegate, routeInformationProvider: router.routeInformationProvider, builder: (BuildContext context, Widget? child) { return child ?? Container(); }, ));}.....static toPage2() { BuildContext? context = Get.context; if(context==null) return; context.pushNamed('page2'); }
For further actions, you may consider blocking this person and/orreporting abuse