Reputation: 11
In the past few days, I'm working on a project and got stuck with gorouter's behaviour. Below, I'm going to explain what it is -
class ABCScreen extends StatelessWidget {
final String id;
const ABCScreen({super.key, required this.id});
@override
Widget build(BuildContext context) {
return Consumer<ABCState>(builder: (context, future, child) {
return FutureBuilder(
future: future.call(id: id),
builder: ((context, snapshot) {
if (snapshot.data != null &&
snapshot.connectionState == ConnectionState.done) {
return XYZScreen(model: snapshot.data!);
} else {
return const ShimmerScreen();
}
}),
);
});
}
}
So ABCScreen basically responsible for fetching data from a REST API Endpoint using a provider and passes the data to XYZ Screen for build. Also it uses FutureBuilder. This is how the State looks -
class ABCState extends ChangeNotifierProvider {
Future<Model?> call({required String id}) async {
Model? data = await ABCServices().fetchData(id: id); // REST API call
if (data != null) {
return data;
}
return null;
}
}
Now, Here I'm aware that I'm not storing the API result into some variable in the provider and then use these variable to build the Screen, because I'm getting my desired results with future builder and I don't like to change things.
So, the major problem with GoRouter is arised form the XYZScreen (child screen of the ABCScreen ), this screen contains a navigation route to another QWEScreen(having defined path in gorouter route configuration, let's say /path),
Also, since I've used goRouter as my app's primary route method, I want to stick to it and not want to use Navigator.push(). Seems like I've some error while implementing the code. Please have a look, any help will be appreciated. Thank you in advance :)
class XYZScreen extends StatelessWidget {
const XYZScreen({
super.key,
required this.model,
});
final Model model ;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
if (context.mounted) {
context.push('/path');
}
},
child: SomeContainer(model: model),
);
}
}
The exepectation is just to use goRouter in navigation while ensuring that no extra calls/ rebuild of the widget takes place.
Upvotes: 0
Views: 191
Reputation: 552
I've ask the Flutter members, and according to the issue, if the view widget is not a const class, the GoRouter would rebuild the view as a expect behavior.
You have two solutions to solved this question:
ABCScreen
to be a const class, and if your still need to pass a parameter into widget, consider using:
Provider
for global state management in your case.Provider
(less suitable for complex data).ABCScreen
a const class (e.g., requires dynamic initialization). GoRoute(
path: ABCScreen.routePath,
pageBuilder: (BuildContext context, GoRouterState state) {
final String parameter = state.extra as String;
return MaterialPage<void>(
child: Builder(builder: (BuildContext context) {
// FIXME:https://stackoverflow.com/a/56666801/5643911
// FIXME:https://github.com/flutter/flutter/issues/152906
if (state.matchedLocation != GoRouter.of(context).location) {
return const SizedBox.shrink();
}
return ABCScreen(id: parameter);
}),
);
},
),
------------
extension GoRouterExtensions on GoRouter {
String get location {
final RouteMatch lastMatch = routerDelegate.currentConfiguration.last;
final RouteMatchList matchList =
lastMatch is ImperativeRouteMatch ? lastMatch.matches : routerDelegate.currentConfiguration;
return matchList.uri.toString();
}
}
Hope the answer providers clear solution for your Flutter project!
Upvotes: 0