[compass_app] Unnecessary screen rebuild on navigation
I've cloned the compass_app project to investigate a behavior I'm also encountering in my own application. Surprisingly, compass_app exhibits the same issue: when navigating between screens, the previous screen is rebuilt, triggering its data fetching logic and resulting in an unnecessary backend call.
Here's how to reproduce the behavior:
- Start the application in development mode.
- Clear the app logs for easier analysis.
- From the HomeScreen, tap the first activity. This will navigate to the ActivitiesScreen.
- Examine the logs. You'll see the HomeViewModel logs reappear, indicating that the
_loadmethod has been called again.
It appears that this issue might be related to the go_router package. There are several open issues in the Flutter repository that seem to be related:
While Iām not entirely sure if these issues are directly related to this specific problem, they might provide some useful context or overlap. It would be great to investigate further to determine if this is a known issue or a new edge case.
do we have any workaround/suggestion to prevent re-fetch data when page is loading if possible? thks
do we have any workaround/suggestion to prevent re-fetch data when page is loading if possible? thks
I guess using DI for the viewModel will solve the issue. I tried the following configuration on my routes:
GoRoute(
path: Routes.cart, // '/cart'
builder: (context, state) => CartScreen(viewModel: context.read()), // here the DI
routes: [
GoRoute(
path: ':id', // '/cart/:id'
builder: (context, state) {
final viewModel = CartItemViewModel(cart: context.read());
final id = state.pathParameters['id']!;
viewModel.load.execute(id);
return CartItemScreen(viewModel: viewModel);
},
),
],
),
And instead of loading the data when the viewmodel's created,
class CartViewModel extends ChangeNotifier {
CartViewModel({required CartRepository cart}) : _cartRepository = cart {
load = Command0(_load)..execute(); // move this execution
}
...
}
I moved it to the screen state:
class _CartScreenState extends State<CartScreen> {
...
@override
void initState() {
super.initState();
widget.viewModel.load.execute(); // moved here
}
...
}
With this configuration, when user navigate from /cart to /cart/:id I don't experience the "parent-rebuilt" again.
When user navigate back to home and navigate again to /cart, the CartScreen will be built as expected .
Use ā ChangeNotifierProvider to prevent the view model from rebuilding. It works for me.
GoRouter router() => GoRouter(
initialLocation: Routes.settings,
debugLogDiagnostics: true,
routes: [
ShellRoute(
builder: (context, state, child) {
return MainLayout(child: child);
},
routes: [
GoRoute(
path: Routes.settings,
builder: (context, state) {
return ChangeNotifierProvider(
create: (context) => SettingsViewModel(context.read()), // here
child: Consumer<SettingsViewModel>(
builder: (context, viewModel, child) {
return SettingsPage(viewModel: viewModel);
},
),
);
},
routes: [
GoRoute(
path: Routes.aboutRelative,
builder: (context, state) {
final viewModel = AboutViewModel();
return AboutPage(viewModel: viewModel);
}),
]),
])
],
);
@shadowfish07 I tried your solution but what I observe is since the builder runs again the SettingsPage is recreated with a new view model again. Am I missing something?
@shadowfish07 I tried your solution but what I observe is since the builder runs again the SettingsPage is recreated with a new view model again. Am I missing something?
Did you wrap the child in Consumer? I recall this prevents creating a new SettingsPage view model when opening the AboutPage. I'm currently traveling ā perhaps in a few days I can double-check this.
Yes Consumer solution did not help me. Maybe it was shell route in your case but that did not help me either š
https://github.com/flutter/flutter/issues/141281#issuecomment-2970010792