SchabanBo / qlevar_router

Manage you project Routes. Create nested routes. Simply navigation without context to your pages. Change only one sub widget in your page when navigating to new route.
MIT License
87 stars 22 forks source link

Issue in back navigation when navigating to the child page from different parents #80

Closed tejHackerDEV closed 1 year ago

tejHackerDEV commented 2 years ago

Hi @SchabanBo, There is an issue in Nested navigation when using going to a child page from DifferentParents. Below is the route example

final route = QRoute.withChild( path: '/dashboard', name: dashboard, initRoute: home, builderChild: (router) => DashboardPage(router), children: [ QRoute( path: '/home', name: home, builder: () => const Material(child: HomePage()), children: [ QRoute( path: '/product/:productId', name: productDetail, builder: () => const Material(child: ProductPage()), ), ], ), QRoute( path: '/my-products', name: myProducts, children: [ QRoute( path: '/product/:productId', name: productDetail, builder: () => const Material(child: ProductPage()), ), ], builder: () => const Material(child: MyProducts()), ), ], );

An DashboardPage which contains a bottom nav with Home & MyProduct as bottom nav items & in both Home & MyProducts Page there is a listing of products as cards through which user can enter into that particular ProductDetailPage.

Issue:-

HomePage -> ProductDetail -> (onBack) -> (Landing on) MyProductPage ---------- But should land on HomePage itself MyProductPage -> ProductDetail -> (onBack) -> (Landing on) MyProductPage ---------- Which is fine

So I think no matter from which page I enter into the ProductDetailPage on back from that page returning me to MyProductPage itself.

SchabanBo commented 2 years ago

Hi @tejHackerDEV,

I made an example on what did you describe. And it is work as expected. Can you check this?

Example ```dart import 'package:flutter/material.dart'; import 'package:qlevar_router/qlevar_router.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { static const String dashboard = 'dashboard'; static const String home = 'home'; static const String productDetail = 'productDetail'; static const String myProducts = 'myProducts'; final routes = QRoute.withChild( path: '/dashboard', name: dashboard, initRoute: home, builderChild: (router) => Nested1(router), children: [ QRoute( path: '/home', name: home, builder: () => const Material(child: WelcomePage(index: 1)), children: [ QRoute( path: '/product/:productId', name: productDetail, builder: () => const Material(child: WelcomePage(index: 2)), ), ], ), QRoute( path: '/my-products', name: myProducts, children: [ QRoute( path: '/product/:productId', name: productDetail, builder: () => const Material(child: WelcomePage(index: 4)), ), ], builder: () => const Material(child: WelcomePage(index: 3)), ), ], ); @override Widget build(BuildContext context) => MaterialApp.router( routeInformationParser: const QRouteInformationParser(), routerDelegate: QRouterDelegate([routes], initPath: '/dashboard'), ); } class WelcomePage extends StatelessWidget { final int? index; const WelcomePage({this.index}); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Card(child: Text(index == null ? 'welcome' : index.toString())), ), floatingActionButton: TextButton( onPressed: QR.back, child: const Text('Back'), )); } } class Nested1 extends StatelessWidget { final QRouter router; const Nested1(this.router); @override Widget build(BuildContext context) { final size = MediaQuery.of(context).size; return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton( onPressed: () => QR.to('/dashboard/home'), child: const Text('/home')), TextButton( onPressed: () => QR.to('/dashboard/home/product/2'), child: const Text('/home/product/2')), TextButton( onPressed: () => QR.to('/dashboard/my-products'), child: const Text('/my-products')), TextButton( onPressed: () => QR.to('/dashboard/my-products/product/2'), child: const Text('/my-products/product/2')), Container( decoration: BoxDecoration(border: Border.all(color: Colors.amber)), width: size.width * 0.7, height: size.height * 0.7, child: router, ) ], ), ), ); } } ```
tejHackerDEV commented 2 years ago

Hi @SchabanBo,

As I said before I am using bottomNavigationBar & I am using QR.toName() instead of QR.to(). Also please find the below reproducible code of the issue which is an modification of your bottomNavigation examples which you hosted on your GitHub for qlevar As you can check the url is updating to settings no matter from which page I enter & clicking on address bar of the url it takes me back to the correct page but clicking on the arrow button on the appBar will take me to the settings page itself which is wrong in my manner. So in my observation I feel that QR.toName() will matches the last registered route with that particular name, but in some cases it will be wrong(like the one I posted now). So I think first we need to check the current route children's if no match found then we need to go out of the parent routes & then needs to match.

Reproducable code import 'package:flutter/material.dart'; import 'package:qlevar_router/qlevar_router.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { static bool isAuthed = false; static const String productDetail = 'productDetail'; static List tabs = [ "Home Page", "Store Page", "Settings Page", ]; final routes = [ QRoute.withChild( path: '/home', builderChild: (c) => HomePage(c), children: [ QRoute( name: tabs[0], path: '/', builder: () => Tab('Home', Colors.blueGrey.shade900), children: [ QRoute( path: '/product/:productId', name: productDetail, builder: () => const Material(child: ProductPage()), ), ]), QRoute( name: tabs[1], path: '/store', builder: () => Tab('Store', Colors.blueGrey.shade700), children: [ QRoute( path: '/product/:productId', name: productDetail, builder: () => const Material(child: ProductPage()), ), ]), QRoute( name: tabs[2], path: '/settings', builder: () => Tab('Settings', Colors.blueGrey.shade500), children: [ QRoute( path: '/product/:productId', name: productDetail, builder: () => const Material(child: ProductPage()), ), ]), ]) ]; @override Widget build(BuildContext context) => MaterialApp.router( routeInformationParser: QRouteInformationParser(), routerDelegate: QRouterDelegate(routes, initPath: '/home'), theme: ThemeData.dark(), ); } class HomePage extends StatefulWidget { final QRouter router; HomePage(this.router); @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State { @override void initState() { super.initState(); // We need to add listener here so the bottomNavigationBar // get updated (the selected tab) when we navigate to new page widget.router.navigator.addListener(_update); } void _update() { setState(() {}); } @override void dispose() { widget.router.navigator.removeListener(_update); super.dispose(); } @override Widget build(BuildContext context) => Scaffold( appBar: AppBar( title: Text('My App'), centerTitle: true, leading: MyApp.tabs.indexWhere( (element) => element == widget.router.routeName) < 0 ? IconButton( onPressed: () => QR.back(), icon: Icon(Icons.arrow_back), ) : null, ), body: widget.router, bottomNavigationBar: MyApp.tabs.indexWhere( (element) => element == widget.router.routeName) < 0 ? null : BottomNavigationBar( items: [ BottomNavigationBarItem( icon: Icon(Icons.home), label: 'home'), BottomNavigationBarItem( icon: Icon(Icons.store), label: 'store'), BottomNavigationBarItem( icon: Icon(Icons.settings), label: 'Settings') ], currentIndex: MyApp.tabs.indexWhere( (element) => element == widget.router.routeName), onTap: (v) => QR.toName(MyApp.tabs[v]), ), ); } class Tab extends StatelessWidget { final String name; final Color color; Tab(this.name, this.color); @override Widget build(BuildContext context) => Container( color: color, child: ListView.builder( itemBuilder: (_, index) => ListTile( onTap: () => QR.toName(MyApp.productDetail, params: { 'productId': index, }), title: Text('Hi i am product $index'), ), itemCount: 20, ), ); } class ProductPage extends StatelessWidget { const ProductPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Container( child: Center( child: Text('I am product page with route \n ${QR.currentPath}', style: TextStyle(fontSize: 20))), ); } }
SchabanBo commented 2 years ago

When you run the app, the package adds all name in a unique list as Tree Info to the routes in the app. When you use the same name for multiple routes, the name will be overwritten be the next route with the same name. Screenshot 2022-09-19 at 15 01 27 You can see in the tree Info the route with the name 'productDetail' is '/home/settings/product'. That is why this is happening.

In short, the name of the routes must be unique

tejHackerDEV commented 2 years ago

When you run the app, the package adds all name in a unique list as Tree Info to the routes in the app. When you use the same name for multiple routes, the name will be overwritten be the next route with the same name. Screenshot 2022-09-19 at 15 01 27 You can see in the tree Info the route with the name 'productDetail' is '/home/settings/product'. That is why this is happening.

In short, the name of the routes must be unique

Can't it happen by checking the parents route names too instead of simply checking the current route name because there might be multiple pages in a app but from multiple pages we can enter into one single route, so we can't have multiple route names for the same page right.

So I think we can add this to a feature request

SchabanBo commented 2 years ago

I don't think that would be a good idea, Every page should have a single unique route, if the route is different, that is mean this is a different page.

tejHackerDEV commented 2 years ago

So can you suggest anything for this kind of navigation?

SchabanBo commented 2 years ago

You can use QR.to for this case, or give the productDetail page a different name on every level.