modular
modular copied to clipboard
TabBar + RouterOutlet + pushReplacementNamed not working correctly together
Bug description
We have a page with a TabBar and a RouterOutlet. Each tab has a route that is rendered inside this RouterOutlet. I can navigate with pushNamed to an inner page where there's a similar TabBar+RouterOutlet, but with different menu options. After enter this inner page and navigate in TabBar using Modular.to.navigate, the entire stack is cleared. When I try to call Modular.to.pop(), the app is closed, because the stack is empty.
I tried to change this navigation to use Modular.to.pushReplacementNamed, because the correct TabBar behavior is to change just the last route in the stack, but the RouterOutlet content doesn't changed correctly. I was looking to the navigation history and it looks like pushReplacementNamed is not replacing the last page correctly.
Environment
➜ flutter --version
Flutter 3.0.1 • channel stable • https://github.com/flutter/flutter.git
Framework • revision fb57da5f94 (3 weeks ago) • 2022-05-19 15:50:29 -0700
Engine • revision caaafc5604
Tools • Dart 2.17.1 • DevTools 2.12.2
Modular version: 5.0.2
To Reproduce
Add a TabBar rendering the content inside a RouterOutlet in two pages, navigate to the second page and navigate using the second TabBar.
Tab bar menu fired with Modular.to.navigate:
- Navigation works well in the first page
- Go to an inner page with other TabBar
- After change tabs, the current stack is cleared and the back button closes the app instead of getting back to the previous page
Tab bar menu fired with Modular.to.pushReplacementNamed:
- Navigation works well in the first time each tab is accessed.
- Trying to get back to a previous tab, the content is not rendered
- Looking at the stack, the previous pages are not replaced with Modular.to.pushReplacementNamed
Expected behavior
Modular.to.pushReplacementNamed should works correctly within the described context.
Hi @emersonsiega, See my example for a similar case: https://gist.github.com/eduardoflorence/4d33da7bdb262ef349641fcfac9e10ca
Hi @emersonsiega,
See my example for a similar case: https://gist.github.com/eduardoflorence/4d33da7bdb262ef349641fcfac9e10ca
Nice, I'll try using relative path for navigation and post the result here...
Thanks!
@eduardoflorence your example works because the RouterOutlet is using just ChildRoutes. In my case, some of the children are ModuleRoute.
I changed your example in a way that doesn't work... https://gist.github.com/emersonsiega/3d0e26003aa5bf9babfb502f18490f9d
@jacobaraujo7 can you give me a help with the scenario above?
@emersonsiega, the example below works:
import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';
void main() {
runApp(ModularApp(module: AppModule(), child: AppWidget()));
}
class AppModule extends Module {
@override
List<ModularRoute> get routes => [
ChildRoute('/', child: (_, __) => HomePage()),
ModuleRoute('/products', module: ProductsModule(), transition: TransitionType.noTransition),
];
}
class AppWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
Modular.to.addListener(() {
print('Navigate: ${Modular.to.path}');
print('History: ${Modular.to.navigateHistory.map((e) => e.name)}');
});
return MaterialApp.router(
routeInformationParser: Modular.routeInformationParser,
routerDelegate: Modular.routerDelegate,
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Center(
child: ElevatedButton(
child: const Text('Go Products'),
// navigate to module only
onPressed: () async {
await Modular.to.pushNamed('/products/');
},
),
),
);
}
}
class ProductsModule extends Module {
@override
List<ModularRoute> get routes => [
ChildRoute('/', child: (_, __) => ProductsPage(), children: [
//ChildRoute('/red', child: (_, __) => Container(color: Colors.red)),
ModuleRoute('/red', module: RedModule()),
ChildRoute('/yellow', child: (_, __) => Container(color: Colors.yellow)),
ChildRoute('/green', child: (_, __) => Container(color: Colors.green)),
])
];
}
class ProductsPage extends StatefulWidget {
@override
State<ProductsPage> createState() => _ProductsPageState();
}
class _ProductsPageState extends State<ProductsPage> {
@override
void initState() {
super.initState();
// Set the initial route inside the module
Modular.to.pushNamed('./yellow');
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
// Returns to root product module before pop
Modular.to.pushReplacementNamed(Modular.to.path.endsWith('/red/') ? '../' : './');
return true;
},
child: Scaffold(
appBar: AppBar(title: const Text('Products')),
body: NavigationListener(builder: (context, child) {
return Row(
children: [
SizedBox(
width: 100,
child: Column(children: [
ListTile(
title: const Text('Red'),
onTap: () => Modular.to.pushReplacementNamed('./red/'),
selected: Modular.to.path.endsWith('/red/'),
),
ListTile(
title: const Text('Yellow'),
onTap: () =>
Modular.to.pushReplacementNamed(Modular.to.path.endsWith('/red/') ? '../yellow' : './yellow'),
selected: Modular.to.path.endsWith('/yellow'),
),
ListTile(
title: const Text('Green'),
onTap: () =>
Modular.to.pushReplacementNamed(Modular.to.path.endsWith('/red/') ? '../green' : './green'),
selected: Modular.to.path.endsWith('/green'),
),
]),
),
const Expanded(child: RouterOutlet())
],
);
}),
),
);
}
}
class RedModule extends Module {
@override
List<ModularRoute> get routes => [
ChildRoute(
'/',
child: (_, __) => RedPage(),
),
];
}
class RedPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(color: Colors.red);
}
}
The best solution is use RouteOutlet + RouteOutlet instead TabBar
Hello everyone,
I found an unintuitive behavior in the application's navigation history that I believe is related to this problem while working with a configuration very similar to the following: flutter_modular non-intuitive navigation behavior.
The navigation history, when navigating from the /step_one route to /step_two/type_one using the Modular.to.pushNamed('./step_two/type_one') call exhibited the following behavior:
Before
[log] Current route: /gradual_process/step_one
[log] Navigation history: [/gradual_process/, /gradual_process/step_one]
After
[log] Current route: /gradual_process/step_two
[log] Navigation history: [/gradual_process/, /gradual_process/step_one, /gradual_process/step_two/type_one, /gradual_process/step_two]
Notice how the extra /step_two route has been added to the top of the stack.
Is this behavior expected?