Update Notice!!!
The explaination below is somewhat outdated. Now, I recommends you to use go_router, a routing package using Flutter Navigation 2.0!
Intro
When I’m on a flutter web, I faced with routing issue. Both MaterialApp
and GetMaterialApp
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 called Flutter 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 with MaterialApp.router
instead of just MaterialApp
. But if you do that you need to implement some override method which involves in navigation logic. Instead overriding each methods, I used GetMaterialApp.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 of Getx
package.
dependencies:
...
get: ^4.6.1
Basic Routing Stuff
For convenience, I used simple routing path and getPages with snippet.
abstract class Routes {
static const HOME = '/';
static const LOGIN = '/login';
static const SIGNUP = '/signup';
}
abstract class AppPages {
static final pages = [
GetPage(
name: Routes.HOME,
page: () => Home(),
),
GetPage(
name: Routes.LOGIN,
page: () => Login(),
),
GetPage(
name: Routes.SIGNUP,
page: () => Signup(),
),
];
}
Create GetMaterialApp.router
When you use GetMaterialApp.router
instead of just GetMaterialApp
, you don’t need to provide(and you can’t) initialRoute
. But you need to provide getPages
and routerDelegate
fields with values.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
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.
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
child: TextButton(
child: Text(
'Home',
style: TextStyle(color: Colors.white),
),
onPressed: () => Get.rootDelegate.toNamed(Routes.LOGIN),
),
);
}
}
class Login extends StatelessWidget {
const Login({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.orange,
child: TextButton(
child: Text(
'Login',
style: TextStyle(color: Colors.white),
),
onPressed: () => Get.rootDelegate.toNamed(Routes.SIGNUP),
),
);
}
}
class Signup extends StatelessWidget {
const Signup({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
child: TextButton(
child: Text(
'Signup',
style: TextStyle(color: Colors.white),
),
onPressed: () => Get.rootDelegate.toNamed(Routes.HOME),
),
);
}
}
Create router delegate with GetDelegate
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.
-
class AppRouterDelegate extends GetDelegate {
@override
Widget build(BuildContext context) {
return Navigator(
onPopPage: (route, result) => route.didPop(result),
pages: currentConfiguration != null
? [currentConfiguration!.currentPage!]
: [GetNavConfig.fromRoute(Routes.HOME)!.currentPage!],
);
}
}
And plus, since you can write build
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 like
Get.back()
? An error occurs in this process. Same asGet.rootDelegate.popRoute()
.I would ultimately like to implement the following elements.
<-
button is pressed (previously useGet.back()
)You can do that by using
Get.roodDelegate.popHistory
with custom logic.popHistory
method inAppRouterDelegate
I've tested it and it works for me. Hope this can help you :-)
Thank you!! That was really helpful
If you refresh it, it doesn't work
popHistory
, but I think we can solve it in a different way!How you have implemented this? Even without overriding, getting same result.
Hello!
We can go back via html API, dart:html
Thanks!
HANW!
Helloo Buddies~
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?
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.Also
Get.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!],
);
}
}
stackoverflow.com/questions/734005...
Hey, i tried to open your stackoverflow link but its say already removed. Do you already find the solution for this problem? Thanks
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();
}
}
Hi, It was very helpful. But I realized that with this fix, WillPopScope widget (or new PopScope) don't work (Flutter Web)
Case like this:
Hello, how do I make Getx compatible with Go router? My project depends heavily on GetX. How can I quickly migrate to go router
have you found any solution?
i have same problem. please let me know
Yes, you can mix GetX pages and GoRouter.
Like this sample.
@swimmingkiim why you recommend to user go_router?
Hello !! the solution of overriding
popHistory()
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.0How to logout ? After logout, still going to previous page on back press.
How to go home from login so that login page will not open when we press back button ?
Try with arguments. It's not working. As it use root where no arguments are passed.
You can try: