I need help with using either Go Router or Auto Route for deep nested navigation. I can navigate just fine to say the child route of the tab bar but if I want to go to the edit profile route it will remove the previous pages. Also I seem to have an issue where if from profile route I want to view a post then select the profile picture for that post and go to the profile I get an error because its not a sub route of post even though post is a child of profile route. Does anyone have a recommended way to do this. I know in swift it would just work but I really just want to use flutter.
class NavKeys {
final _rootNavigatorKey = GlobalKey<NavigatorState>();
final _shellNavigatorHomeKey =
GlobalKey<NavigatorState>(debugLabel: 'shellHome');
final _shellNavigatorChatKey =
GlobalKey<NavigatorState>(debugLabel: 'shellChat');
final _shellNavigatorNotificationKey =
GlobalKey<NavigatorState>(debugLabel: 'shellNotification');
final _shellNavigatorLivestreamKey =
GlobalKey<NavigatorState>(debugLabel: 'shellLivestream');
final _shellNavigatorSearchKey =
GlobalKey<NavigatorState>(debugLabel: 'shellSearch');
}
class PostRouter {
GoRoute postFiles = GoRoute(
path: RouterNames.postFilesRoute,
builder: (context, state) {
return CarouselFullScreen(
key: state.pageKey,
index: state.extra as int,
);
},
);
GoRoute reactions = GoRoute(
path: RouterNames.reactionsViewRoute,
builder: (context, state) {
return ReactionListFullScreen(
key: state.pageKey,
isComment: state.extra as bool,
parentId: state.uri.queryParameters['parentId'] as String,
);
},
routes: [
ProfileRouter().profileRoute,
],
);
GoRoute postRoute = GoRoute(
path: RouterNames.postRoute,
builder: (context, state) {
return PostFullView(
key: state.pageKey,
postId: state.pathParameters['id'] as String,
);
},
routes: [
ProfileRouter().profileRoute,
PostRouter().postFiles,
PostRouter().postFiles,
PostRouter().reactions,
],
);
}
class ProfileRouter {
GoRoute editProfile = GoRoute(
path: RouterNames.editProfileRoute,
builder: (context, state) {
return EditProfile(
key: state.pageKey,
);
},
);
GoRoute profileRoute = GoRoute(
path: RouterNames.profileRoute,
builder: (context, state) {
return ProfileView(
id: state.pathParameters['id'] as String,
key: state.pageKey,
);
},
routes: [
ProfileRouter().editProfile,
PostRouter().postRoute,
ChatRouter().chatRoute,
],
);
}
class ChatRouter {
GoRoute threads = GoRoute(
path: RouterNames.chatThreadViewRoute,
builder: (context, state) {
return ThreadPage(
key: state.pageKey,
);
},
routes: [
ProfileRouter().profileRoute,
PostRouter().postRoute,
],
);
GoRoute addMembers = GoRoute(
path: RouterNames.addChatMemberRoute,
builder: (context, state) {
return AddMembers(
key: state.pageKey,
isAuthorized: state.extra as bool,
);
},
routes: [
ProfileRouter().profileRoute,
],
);
GoRoute pending = GoRoute(
path: RouterNames.pendingChatMembersRoute,
builder: (context, state) {
return MemberRequest(
key: state.pageKey,
isAuthorized: state.extra as bool,
);
},
routes: [
ProfileRouter().profileRoute,
],
);
GoRoute media = GoRoute(
path: RouterNames.chatMediaViewerRoute,
builder: (context, state) {
return ChatMedia(
key: state.pageKey,
);
},
routes: [
ChatRouter().chatRoute,
],
);
GoRoute memberList = GoRoute(
path: RouterNames.chatMemberListRoute,
builder: (context, state) {
return MemberList(
key: state.pageKey,
);
},
routes: [
ProfileRouter().profileRoute,
],
);
GoRoute pinned = GoRoute(
path: RouterNames.pinnedMessagesRoute,
builder: (context, state) {
return PinnedMessages(
key: state.pageKey,
);
},
routes: [
ChatRouter().chatRoute,
],
);
GoRoute videoCall = GoRoute(
path: RouterNames.videoCallRoute,
builder: (context, state) {
final Map data = state.extra as Map;
return VideoCallView(
key: state.pageKey,
host: data['host'],
);
},
routes: [
ProfileRouter().profileRoute,
],
);
GoRoute chatRoute = GoRoute(
path: RouterNames.chatViewRoute,
builder: (context, state) {
return MessagesView(
key: state.pageKey,
);
},
routes: [
ChatRouter().threads,
GoRoute(
path: RouterNames.chatDetailsRoute,
builder: (context, state) {
return ChatDetails(key: state.pageKey);
},
routes: [
ChatRouter().addMembers,
// GoRoute(
// path: RouterNames.newChatMemberRoute,
// builder: (context, state) {
// final Map data = state.extra as Map;
// return AddNewMembers(
// key: state.pageKey,
// isAuthorized: data['isAuthorized'],
// channel: data['channel'],
// );
// },
// ),
ChatRouter().pending,
ChatRouter().media,
ChatRouter().memberList,
ChatRouter().pinned,
],
),
ChatRouter().videoCall,
],
);
}
class SocialRouter {
GoRouter router = GoRouter(
initialLocation: RouterNames.homeRoute,
navigatorKey: NavKeys()._rootNavigatorKey,
debugLogDiagnostics: true,
redirect: (context, state) {
final isLoggingin = state.matchedLocation == RouterNames.loginRoute;
final isSignup = state.matchedLocation == RouterNames.signupRoute;
final loggedIn = supabase.auth.currentUser != null;
if (!loggedIn && !isLoggingin && !isSignup) return RouterNames.loginRoute;
if (loggedIn && (isLoggingin || isSignup)) return RouterNames.homeRoute;
if (loggedIn && state.matchedLocation == '/') {
return RouterNames.homeRoute;
}
return null;
},
routes: [
StatefulShellRoute.indexedStack(
builder: (context, state, navigationShell) {
return TabbarViewScaffold(navigationShell);
},
branches: [
// HOME
StatefulShellBranch(
navigatorKey: NavKeys()._shellNavigatorHomeKey,
routes: [
GoRoute(
path: RouterNames.homeRoute,
builder: (context, state) {
return Home(
key: state.pageKey,
);
},
routes: [
ProfileRouter().profileRoute,
GoRoute(
path: RouterNames.bookmarkRoute,
builder: (context, state) {
return Bookmarks(
key: state.pageKey,
);
},
routes: [
ProfileRouter().profileRoute,
],
),
GoRoute(
path: RouterNames.settingsRoute,
builder: (context, state) {
return Settings(
key: state.pageKey,
);
},
),
GoRoute(
path: RouterNames.privacyRoute,
builder: (context, state) {
return Privacy(
key: state.pageKey,
);
},
),
GoRoute(
path: RouterNames.blockListRoute,
builder: (context, state) {
return BlockedList(
key: state.pageKey,
);
},
routes: [
ProfileRouter().profileRoute,
],
),
GoRoute(
path: RouterNames.verificationRoute,
builder: (context, state) {
return VerificationHome(
key: state.pageKey,
);
},
),
GoRoute(
path: RouterNames.storyViewRoute,
builder: (context, state) {
final Map data = state.extra as Map;
return StoryViewPage(
key: state.pageKey,
stories: data['stories'],
story: data['story'],
updateState: data['updateState'],
);
},
routes: [
ProfileRouter().profileRoute,
],
),
PostRouter().postRoute,
],
),
],
),
// Chat
StatefulShellBranch(
navigatorKey: NavKeys()._shellNavigatorChatKey,
routes: [
GoRoute(
path: RouterNames.chatRoute,
builder: (context, state) {
return Chat(
key: state.pageKey,
);
},
routes: [
GoRoute(
path: RouterNames.createChatRoute,
builder: (context, state) {
return CreateChat(
key: state.pageKey,
);
},
),
GoRoute(
path: RouterNames.chatSearchRoute,
builder: (context, state) {
return ChatSearchList(
key: state.pageKey,
);
},
),
ChatRouter().chatRoute,
],
),
],
),
// Notifications
StatefulShellBranch(
navigatorKey: NavKeys()._shellNavigatorNotificationKey,
routes: [
GoRoute(
path: RouterNames.notificationRoute,
builder: (context, state) {
return Notifications(
key: state.pageKey,
);
},
),
],
),
// Livestream
StatefulShellBranch(
navigatorKey: NavKeys()._shellNavigatorLivestreamKey,
routes: [
GoRoute(
path: RouterNames.livestreamsRoute,
builder: (context, state) {
return Livestreams(
key: state.pageKey,
);
},
routes: [
GoRoute(
path: RouterNames.livestreamViewRoute,
builder: (context, state) {
final Map data = state.extra as Map;
return LiveStreamView(
key: state.pageKey,
isHost: data['isHost'],
liveId: state.pathParameters['id'] as String,
);
},
),
GoRoute(
path: RouterNames.lobbyViewRoute,
builder: (context, state) {
final Map data = state.extra as Map;
return CallLobby(
key: state.pageKey,
isLivestream: data['isLivestream'],
host: data['host'],
);
},
),
],
),
],
),
// Search
StatefulShellBranch(
navigatorKey: NavKeys()._shellNavigatorSearchKey,
routes: [
GoRoute(
path: RouterNames.searchRoute,
builder: (context, state) {
return SearchPage(
key: state.pageKey,
query: state.uri.queryParameters['query'],
);
},
routes: [
ProfileRouter().profileRoute,
PostRouter().postRoute,
],
),
],
),
],
),
GoRoute(
path: RouterNames.createPostRoute,
parentNavigatorKey: NavKeys()._rootNavigatorKey,
pageBuilder: (context, state) {
final Map data = state.extra as Map;
return MaterialPage(
fullscreenDialog: true,
child: CreatePost(
fromProfile: data['fromProfile'],
userId: data['userId'],
username: data['username'],
shared: data['shared'],
edit: data['edit'],
communityId: data['communityId'],
),
);
},
),
GoRoute(
path: RouterNames.galleryPickerRoute,
parentNavigatorKey: NavKeys()._rootNavigatorKey,
pageBuilder: (context, state) {
return MaterialPage(
fullscreenDialog: true,
child: GalleryPicker(
fromStory: state.extra as bool,
),
);
},
routes: [
GoRoute(
path: RouterNames.cameraViewRoute,
builder: (context, state) {
return CameraView(
fromStory: state.extra as bool,
);
},
),
],
),
GoRoute(
path: RouterNames.loginRoute,
builder: (context, state) => const Login(),
),
GoRoute(
path: RouterNames.signupRoute,
builder: (context, state) => const Signup(),
),
GoRoute(
path: '/*',
builder: (context, state) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('404 ${state.fullPath} is not a valid url'),
OutlinedButton(
onPressed: () {
context.go('/home');
},
child: const Text('Go Home'))
],
),
);
},
)
],
);
}
GoRouter and AutoRoute use declarative routing (introduced as Flutter Navigator 2.0) which is an approach where developers define the routes available within the application along with their associated destinations in an hierarchical structure where routes are mapped to specific screens (widgets). In contrast with imperative navigation where navigation consists of directly pushing or popping routes onto the navigation stack, developers describe navigation actions which "go" to a destination.
Navigating to a destination using declarative routing will replace the current stack of "screens" with the stack of "screens" configured to be displayed for the destination route. Upon navigation, GoRouter constructs the stack of routes based on the routing configuration and the requested route.
For example, if you define the following routes hierarchy:
When navigating usiong "go" to
/profiles/421654215it will build the stack of routes bellow: |- ProfilesPage--->|- ProfileDetailsPage
Which means that if you "pop back" from the ProfileDetailsPage screen it will show the ProfilePage screen, and not the screen that was previously in the view.
If you still want/need to use imperative navigation you can do the following: