Sub routes with extra object in go_router - flutter

Is there any ways to pass parameters(or extra Object) with sub routes?
When navigating PageA from PageB, type 'Null' is not a subtype of type 'int' error has occurred.
final _router = GoRouter(
debugLogDiagnostics: true,
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) => TopPage(),
routes: [
GoRoute(
path: 'a',
builder: (context, state) {
final res = state.extra as Map<String, dynamic>;
return PageA(
num: res['num'],
);
},
routes: [
GoRoute(
path: 'b',
builder: (context, state) {
final res = state.extra as Map<String, dynamic>;
return PageB(
numFromA: res['numFromA'],
);
},
),
],
),
],
),
],
);
enum AppRouter {
pageA,
pageB;
}

Related

Flutter GoRouter not navigating to selected rouye

Router config returns the correct path upon selecting a category from the homescreen but not navigating to the desired CategoriesFeedScreen. Here are the 3 code files guigind this navigation and the issues. Please assist as I have tried to resolve this issue for 3 weeks now on my own and no luck. Thanks
Router config (goRouterProvider.dart)
final GlobalKey<NavigatorState> _rootNavigator = GlobalKey(debugLabel: 'root');
final GlobalKey<NavigatorState> _shellNavigator =
GlobalKey(debugLabel: 'shell');
final goRouterProvider = Provider<GoRouter>(
(ref) {
bool isDuplicate = false;
final notifier = ref.read(goRouterNotifierProvider);
return GoRouter(
navigatorKey: _rootNavigator,
debugLogDiagnostics: true,
initialLocation: '/',
refreshListenable: notifier,
redirect: (context, state) {
final isLoggedIn = notifier.value;
final isGoingToLogin = state.subloc == '/auth-screen';
if (!isLoggedIn && !isGoingToLogin && !isDuplicate) {
isDuplicate = true;
return '/auth-screen';
}
if (isLoggedIn && isGoingToLogin && !isDuplicate) {
isDuplicate = true;
return '/';
}
if (isDuplicate) {
isDuplicate = false;
}
return null;
},
routes: [
GoRoute(
path: '/landing',
name: 'landing',
builder: (context, state) => LandingScreen(key: state.pageKey),
),
GoRoute(
path: '/auth-screen',
name: 'auth-screen',
builder: (context, state) => AuthScreen(key: state.pageKey),
),
ShellRoute(
navigatorKey: _shellNavigator,
builder: (context, state, child) =>
BottomNavScreen(key: state.pageKey, child: child),
routes: [
GoRoute(
path: '/',
name: RouteConstants.homeRouteName,
pageBuilder: (context, state) {
return NoTransitionPage(
child: HomeScreen(
key: state.pageKey,
),
);
},
routes: [
GoRoute(
parentNavigatorKey: _shellNavigator,
path: 'categories/:name',
name: RouteConstants.categoriesRouteName,
pageBuilder: (context, state) {
return NoTransitionPage(
child: CategoriesFeedScreen(
name: state.params['name']!,
key: state.pageKey,
),
);
},
),
GoRoute(
parentNavigatorKey: _shellNavigator,
path: 'brands/:id',
name: RouteConstants.brandsRoutename,
pageBuilder: (context, state) {
return NoTransitionPage(
child: BrandsNavRail(
id: int.parse(state.params['id']!),
key: state.pageKey,
),
);
},
),
GoRoute(
parentNavigatorKey: _shellNavigator,
path: 'popular-products/:id',
name: RouteConstants.popularProductsRouteName,
pageBuilder: (context, state) {
return NoTransitionPage(
child: PopularProducts(
id: state.params['id']!,
key: state.pageKey,
),
);
},
)
],
),
GoRoute(
path: '/feeds',
name: RouteConstants.feedsRouteName,
pageBuilder: (context, state) {
return NoTransitionPage(
child: FeedsScreen(
key: state.pageKey,
),
);
},
routes: [
GoRoute(
parentNavigatorKey: _shellNavigator,
path: 'details/:id',
name: RouteConstants.detailsRouteName,
pageBuilder: (context, state) {
// final id = state.params['id'].toString();
return NoTransitionPage(
child: ProductDetailsScreen(
id: state.params['id']!,
key: state.pageKey,
),
);
},
)
],
),
GoRoute(
path: '/cart',
name: RouteConstants.cartRouteName,
pageBuilder: (context, state) {
return NoTransitionPage(
child: CartScreen(
key: state.pageKey,
),
);
},
),
GoRoute(
path: '/account',
name: RouteConstants.accountRouteName,
pageBuilder: (context, state) {
return NoTransitionPage(
child: AccountScreen(
key: state.pageKey,
),
);
},
)
],
)
],
errorBuilder: (context, state) => RouteErrorScreen(
errorMsg: state.error.toString(),
key: state.pageKey,
),
);
},
);
CategoriesFeedScreen:
class CategoriesFeedScreen extends HookConsumerWidget {
final String name;
const CategoriesFeedScreen({Key? key, required this.name}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final categoryName = ref.watch(categoryProvider);
final productList = ref.watch(productProvider.notifier);
final catProductList = productList.findByCatName(
categoryName.toString(),
);
return Scaffold(
appBar: AppBar(
title: Text(name),
),
body: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
childAspectRatio: 2 / 3,
mainAxisSpacing: 10,
),
itemCount: catProductList.length,
itemBuilder: (context, i) {
return FeedsProduct(
id: catProductList[i].id,
);
},
),
);
}
}
CategoryWidget:
class CategoryWidget extends HookConsumerWidget {
CategoryWidget({Key? key, required this.i}) : super(key: key);
final int i;
#override
Widget build(BuildContext context, WidgetRef ref) {
final categoryLabel = ref.watch(categoryProvider);
return InkWell(
onTap: () {
return context.goNamed(
RouteConstants.categoriesRouteName,
params: {'name': categoryLabel[i].name.toLowerCase()},
);
},
child: Container(
main.dart:
Widget build(BuildContext context, WidgetRef ref) {
final router = ref.watch(goRouterProvider);
final categoryLabels = ref.watch(categoryProvider.notifier);
final productList = ref.watch(productProvider.notifier);
// final themeStatus = ref.watch(
// themeProvider.notifier); // used to switch between dark and light modes
useEffect(() {
resetNewLaunch();
productList.fetchProducts();
categoryLabels.getCategoryLabels();
return;
}, const []);
return ref.watch(authStateChangeProvider).when(
data: (data) => ResponsiveSizer(
builder: (context, orientation, screenType) {
return MaterialApp.router(
debugShowCheckedModeBanner: false,
builder: BotToastInit(), // call BotToastInit
// navigatorObservers: [BotToastNavigatorObserver()],
// theme: AppTheme.myTheme(themeStatus.getTheme(), context),
darkTheme: ThemeData.dark(),
routeInformationParser: router.routeInformationParser,
routeInformationProvider: router.routeInformationProvider,
routerDelegate: router.routerDelegate,
);
},
),
error: (error, stackTrace) => ErrorText(error: error.toString()),
loading: () => const Loader(),
);
}
}
I am running the latest flutter version and goRouter package. Thanks a bunch for your help
Fixed the non-navigation issue. Changed the parentNavigatorKey in the sub-routes of the routerConfig file (goRouterProvider) from _shellNavigator to _rootNavigator which allows the new screens to display outside of the bottomwnavigator shell. The sub-routes simply refused to open inside of the bottomNav tabs even though the correct params and paths were being registered.
.

Flutter go_router parameters within ShellRoute not working

I am using go_router and need have setup a ShellRoute. It is working fine so far. Now one of the routes inside that ShellRoute should have a route with params. And I can not make it work..
This is my setup:
final rootNavigatorKey = GlobalKey<NavigatorState>();
class AppRouter {
static final _shellNavigatorKey = GlobalKey<NavigatorState>();
static final router = GoRouter(
initialLocation: IntroView.path,
debugLogDiagnostics: true,
navigatorKey: rootNavigatorKey,
routes: [
ShellRoute(
navigatorKey: _shellNavigatorKey,
pageBuilder: (context, state, child) {
return NoTransitionPage(
child: ScaffoldView(
initLocation: state.location,
child: child,
),
);
},
routes: [
GoRoute(
name: ProjectsView.name,
path: ProjectsView.path,
parentNavigatorKey: _shellNavigatorKey,
pageBuilder: (context, state) {
return const FadePageTransition(
page: ProjectsView(),
);
},
routes: [
GoRoute(
parentNavigatorKey: rootNavigatorKey,
path: 'projects/:projectTitle',
pageBuilder: (context, state) {
return FadePageTransition(
page: ProjectDetailScaffold(
project: Projects.values.byName(
state.params['projectTitle']!,
),
),
);
},
),
],
),
...
And I am trying to push to ProjectDetailScaffold like this:
rootNavigatorKey.currentContext!.push(
'/projects/wishlists',
);
But I get an error that no route exists for /projects/wishlists...
What am I missing here?
Let me know if you need any more info.
I figured it out by myself:
The problem was that I specified path of the subRoute wrong. I added it's parent path as well when I only need to add :projectTitle. Here is the working solution:
GoRoute(
parentNavigatorKey: rootNavigatorKey,
path: ':projectTitle',
pageBuilder: (context, state) {
return FadePageTransition(
page: ProjectDetailScaffold(
project: Projects.values.byName(
state.params['projectTitle']!,
),
),
);
},
),

Go_Router Pass Object to new route

I want to pass object from the listview to the sub route. It seems no way to pass the object.
Is there any how to do it?
GoRouter routes(AuthBloc bloc) {
return GoRouter(
navigatorKey: _rootNavigatorKey,
routes: <RouteBase>[
GoRoute(
routes: <RouteBase>[
GoRoute(
path: loginURLPagePath,
builder: (BuildContext context, GoRouterState state) {
return const LoginPage();
},
),
GoRoute(
path: homeURLPagePath,
builder: (BuildContext context, GoRouterState state) =>
const HomePage(),
routes: <RouteBase>[
GoRoute(
path: feeURLPagePath,
name: 'a',
builder: (context, state) => FeePage(),
routes: [
/// Fee Details page
GoRoute(
name: 'b',
path: feeDetailsURLPagePath,
builder: (BuildContext context, GoRouterState state) =>
const FeeDetails(),
),
]),
],
),
],
path: welcomeURLPagePath,
builder: (BuildContext context, GoRouterState state) =>
const SplashPage(),
),
],
refreshListenable: GoRouterRefreshStream(bloc.stream),
debugLogDiagnostics: true,
initialLocation: welcomeURLPagePath,
},
);
}
The error says no initial match found for feeDetails!
Use extra parameter in context.goNamed()
Example:
Object:
class Sample {
String attributeA;
String attributeB;
bool boolValue;
Sample(
{required this.attributeA,
required this.attributeB,
required this.boolValue});}
Define GoRoute as
GoRoute(
path: '/sample',
name: 'sample',
builder: (context, state) {
Sample sample = state.extra as Sample; // -> casting is important
return GoToScreen(object: sample);
},
),
Call it as:
Sample sample = Sample(attributeA: "True",attributeB: "False",boolValue: false)
context.goNamed("sample",extra:sample );
Receive it as:
class GoToScreen extends StatelessWidget {
Sample? object;
GoToScreen({super.key, this.object});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(
object.toString(),
style: const TextStyle(fontSize: 24),
)),
);
}
}

How can i get Uri params in Flutter Mobile when open with Deeplink

I configured Navigator through GoRouter and I want to know how to receive a parameter through a deep link.
Instead of configuring arguments in Widget corresponding to the destination,
For iOS simulators, on the connected terminal,
xcrun simctl openurl booted "customScheme://myHost.name.com/login?p=0"
If I enter , I want to enter the login screen and use the parameter p=0 in Login Widget at the same time. Is there any other way?
I considered using uni_links, but I do not want to use it because it interferes with Flutter's basic Deeplinking activities.
(The reason is that if you run the command from the terminal as shown above, if you have uni_links, you will only move to the root and not reach the sub-destination.)
Below is the routerConfig of GoRouter.
final _router = GoRouter(
initialLocation: '/home',
navigatorKey: _rootNavigatorKey,
observers: [
GoRouterObserver(),
routeObserver
],
routes: [
GoRoute(
path: "/",
builder: (context, state) {
return const Root();
},
routes: [
ShellRoute(
navigatorKey: _shellNavigatorKey,
builder: (context, state, child) {
return ScaffoldWithNavBar(child: child);
},
routes: [
GoRoute(
path: 'home',
builder: (context, state) {
return const Home();
},
),
GoRoute(
path: 'discover',
builder: (context, state) {
return const Discover();
},
),
GoRoute(
path: 'shop',
builder: (context, state) {
return const Shop();
}),
],
),
GoRoute(
path: 'login',
parentNavigatorKey: _rootNavigatorKey,
builder: (context, state) {
return const Login();
},
routes: loginRouter),
],
)
],
);

How to get route history and queryParameters

My goRouter like:
GoRouter(initialLocation: '/', routes: [
GoRoute(
path: '/',
builder: (context, state) => HomeScreen(),
routes: [
GoRoute(
path: 'page1',
name: 'page1',
builder: (context, state) => Page1Screen(),
),
GoRoute(
path: 'page2',
name: 'page2',
builder: (context, state) => Page2Screen(),
),
GoRoute(
path: 'page3',
name: 'page3',
builder: (context, state) => Page3Screen(),
),
],
),
]);
: HomeScreen use go to page1.
: Page1Screen use push to page2
: Page2Screen use push to page3
: page3 wants use go to return page1
In go_router 5.1.10 version i can use this code get route history to return page1.
final matches = goRouter.routerDelegate.currentConfiguration.matches;
final route = matches.lastWhereOrNull((value) => value.fullpath.contains('page1'));
context.go(goRouter.namedLocation(kRouteHotel, params: route.encodedParams, queryParams: route.queryParams));
but 5.2.0 has remove encodedParams and queryParams.
How should I do to have the same function?