Flo*_*ser 20 routes ios dart flutter flutter-go-router
我目前正在努力使用go_router重构我的路由代码。
我已经得到了一些简单的路由,例如/signin& /signup,但是当我尝试使路由与具有多个屏幕的 BottomNavigationBar 一起工作时,问题就出现了。我想为他们每个人都有一条单独的路线,例如/home, /events& /profile。
我发现我必须以某种方式返回具有不同参数的相同小部件,以防止每当按下 BottomNavigationBarItem 时整个屏幕发生变化,而只更新 BottomNavigationBar 上方的部分(即屏幕本身)。
我想出了一个非常棘手的解决方案:
GoRoute(
path: '/:path',
builder: (BuildContext context, GoRouterState state) {
final String path = state.params['path']!;
if (path == 'signin') {
return const SignInScreen();
}
if (path == 'signup') {
return const SignUpScreen();
}
if (path == 'forgot-password') {
return const ForgotPasswordScreen();
}
// Otherwise it has to be the ScreenWithBottomBar
final int index = getIndexFromPath(path);
if (index != -1) {
return MainScreen(selectedIndex: index);
}
return const ErrorScreen();
}
)
Run Code Online (Sandbox Code Playgroud)
这看起来不太好,并且它使得无法添加像/profile/settings/appearance或 之类的子路线/events/:id。
我想要一些容易理解的东西,如下所示:
GoRoute(
path: '/signin',
builder: (BuildContext context, GoRouterState state) {
return const SignInScreen();
}
),
GoRoute(
path: '/signup',
builder: (BuildContext context, GoRouterState state) {
return const SignUpScreen();
}
),
GoRoute(
path: '/home',
builder: (BuildContext context, GoRouterState state) {
return const ScreenWithNavBar(selectedScreen: 1);
}
),
GoRoute(
path: '/events',
builder: (BuildContext context, GoRouterState state) {
return const ScreenWithNavBar(selectedScreen: 2);
},
routes: <GoRoute>[
GoRoute(
path: ':id',
builder: (BuildContext context, GoRouterState state) {
return const EventScreen();
}
)
]
)
Run Code Online (Sandbox Code Playgroud)
有什么办法可以实现该行为吗?
kri*_*yaa 25
现在您可以使用ShellRouterwithGoRouter来创建Navigation Bar
说明:
context.go()使用fromShellRoute到时要记住的事项GoRoute
parentNavigatorKeypropGoRoutecontext.go()替换页面,context.push()将页面压入堆栈代码结构如下:
final _parentKey = GlobalKey<NavigatorState>();
final _shellKey = GlobalKey<NavigatorState>();
|_ GoRoute
|_ parentNavigatorKey = _parentKey Specify key here
|_ ShellRoute
|_ GoRoute // Needs Bottom Navigation
|_ parentNavigatorKey = _shellKey
|_ GoRoute // Needs Bottom Navigation
|_ parentNavigatorKey = _shellKey
|_ GoRoute // Full Screen which doesn't need Bottom Navigation
|_parentNavigatorKey = _parentKey
Run Code Online (Sandbox Code Playgroud)
代码:
路由器
final _rootNavigatorKey = GlobalKey<NavigatorState>();
final _shellNavigatorKey = GlobalKey<NavigatorState>();
final router = GoRouter(
initialLocation: '/',
navigatorKey: _rootNavigatorKey,
routes: [
ShellRoute(
navigatorKey: _shellNavigatorKey,
pageBuilder: (context, state, child) {
print(state.location);
return NoTransitionPage(
child: ScaffoldWithNavBar(
location: state.location,
child: child,
));
},
routes: [
GoRoute(
path: '/',
parentNavigatorKey: _shellNavigatorKey,
pageBuilder: (context, state) {
return const NoTransitionPage(
child: Scaffold(
body: Center(child: Text("Home")),
),
);
},
),
GoRoute(
path: '/discover',
parentNavigatorKey: _shellNavigatorKey,
pageBuilder: (context, state) {
return const NoTransitionPage(
child: Scaffold(
body: Center(child: Text("Discover")),
),
);
},
),
GoRoute(
parentNavigatorKey: _shellNavigatorKey,
path: '/shop',
pageBuilder: (context, state) {
return const NoTransitionPage(
child: Scaffold(
body: Center(child: Text("Shop")),
),
);
}),
],
),
GoRoute(
parentNavigatorKey: _rootNavigatorKey,
path: '/login',
pageBuilder: (context, state) {
return NoTransitionPage(
key: UniqueKey(),
child: Scaffold(
appBar: AppBar(),
body: const Center(
child: Text("Login"),
),
),
);
},
),
],
);
Run Code Online (Sandbox Code Playgroud)
底部导航栏
class ScaffoldWithNavBar extends StatefulWidget {
String location;
ScaffoldWithNavBar({super.key, required this.child, required this.location});
final Widget child;
@override
State<ScaffoldWithNavBar> createState() => _ScaffoldWithNavBarState();
}
class _ScaffoldWithNavBarState extends State<ScaffoldWithNavBar> {
int _currentIndex = 0;
static const List<MyCustomBottomNavBarItem> tabs = [
MyCustomBottomNavBarItem(
icon: Icon(Icons.home),
activeIcon: Icon(Icons.home),
label: 'HOME',
initialLocation: '/',
),
MyCustomBottomNavBarItem(
icon: Icon(Icons.explore_outlined),
activeIcon: Icon(Icons.explore),
label: 'DISCOVER',
initialLocation: '/discover',
),
MyCustomBottomNavBarItem(
icon: Icon(Icons.storefront_outlined),
activeIcon: Icon(Icons.storefront),
label: 'SHOP',
initialLocation: '/shop',
),
MyCustomBottomNavBarItem(
icon: Icon(Icons.account_circle_outlined),
activeIcon: Icon(Icons.account_circle),
label: 'MY',
initialLocation: '/login',
),
];
@override
Widget build(BuildContext context) {
const labelStyle = TextStyle(fontFamily: 'Roboto');
return Scaffold(
body: SafeArea(child: widget.child),
bottomNavigationBar: BottomNavigationBar(
selectedLabelStyle: labelStyle,
unselectedLabelStyle: labelStyle,
selectedItemColor: const Color(0xFF434343),
selectedFontSize: 12,
unselectedItemColor: const Color(0xFF838383),
showUnselectedLabels: true,
type: BottomNavigationBarType.fixed,
onTap: (int index) {
_goOtherTab(context, index);
},
currentIndex: widget.location == '/'
? 0
: widget.location == '/discover'
? 1
: widget.location == '/shop'
? 2
: 3,
items: tabs,
),
);
}
void _goOtherTab(BuildContext context, int index) {
if (index == _currentIndex) return;
GoRouter router = GoRouter.of(context);
String location = tabs[index].initialLocation;
setState(() {
_currentIndex = index;
});
if (index == 3) {
context.push('/login');
} else {
router.go(location);
}
}
}
class MyCustomBottomNavBarItem extends BottomNavigationBarItem {
final String initialLocation;
const MyCustomBottomNavBarItem(
{required this.initialLocation,
required Widget icon,
String? label,
Widget? activeIcon})
: super(icon: icon, label: label, activeIcon: activeIcon ?? icon);
}
Run Code Online (Sandbox Code Playgroud)
输出:
这是完整的工作示例,来自最新的 go_router 更改,使用 ShellRoute:
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
Future<void> main() async {
runApp(
StatefulShellRouteExampleApp(),
);
}
class ScaffoldBottomNavigationBar extends StatelessWidget {
const ScaffoldBottomNavigationBar({
required this.navigationShell,
Key? key,
}) : super(key: key ?? const ValueKey<String>('ScaffoldBottomNavigationBar'));
final StatefulNavigationShell navigationShell;
@override
Widget build(BuildContext context) {
return Scaffold(
body: navigationShell,
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Section_A'),
BottomNavigationBarItem(icon: Icon(Icons.work), label: 'Section_B'),
],
currentIndex: navigationShell.currentIndex,
onTap: (int tappedIndex) {
navigationShell.goBranch(tappedIndex);
},
),
);
}
}
class StatefulShellRouteExampleApp extends StatelessWidget {
StatefulShellRouteExampleApp({super.key});
final GoRouter _router = GoRouter(
initialLocation: '/login',
routes: <RouteBase>[
GoRoute(
path: '/login',
builder: (BuildContext context, GoRouterState state) {
return const LoginScreen();
},
routes: <RouteBase>[
GoRoute(
path: 'detailLogin',
builder: (BuildContext context, GoRouterState state) {
return const DetailLoginScreen();
},
),
],
),
StatefulShellRoute.indexedStack(
builder: (BuildContext context, GoRouterState state,
StatefulNavigationShell navigationShell) {
return ScaffoldBottomNavigationBar(
navigationShell: navigationShell,
);
},
branches: <StatefulShellBranch>[
StatefulShellBranch(
routes: <RouteBase>[
GoRoute(
path: '/sectionA',
builder: (BuildContext context, GoRouterState state) {
return const RootScreen(
label: 'Section A',
detailsPath: '/sectionA/details',
);
},
routes: <RouteBase>[
GoRoute(
path: 'details',
builder: (BuildContext context, GoRouterState state) {
return const DetailsScreen(label: 'A');
},
),
],
),
],
),
StatefulShellBranch(
routes: <RouteBase>[
GoRoute(
path: '/sectionB',
builder: (BuildContext context, GoRouterState state) {
return const RootScreen(
label: 'Section B',
detailsPath: '/sectionB/details',
);
},
routes: <RouteBase>[
GoRoute(
path: 'details',
builder: (BuildContext context, GoRouterState state) {
return const DetailsScreen(label: 'B');
},
),
],
),
],
),
],
),
],
);
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Go_router Complex Demo',
theme: ThemeData(
primarySwatch: Colors.orange,
),
routerConfig: _router,
);
}
}
class RootScreen extends StatelessWidget {
const RootScreen({
required this.label,
required this.detailsPath,
super.key,
});
final String label;
final String detailsPath;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Root of section $label'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
'Screen $label',
style: Theme.of(context).textTheme.titleLarge,
),
const Padding(padding: EdgeInsets.all(4)),
TextButton(
onPressed: () {
GoRouter.of(context).go(detailsPath);
},
child: const Text('View details'),
),
const Padding(padding: EdgeInsets.all(4)),
TextButton(
onPressed: () {
GoRouter.of(context).go('/login');
},
child: const Text('Logout'),
),
],
),
),
);
}
}
class DetailsScreen extends StatefulWidget {
const DetailsScreen({
required this.label,
super.key,
});
final String label;
@override
State<StatefulWidget> createState() => DetailsScreenState();
}
class DetailsScreenState extends State<DetailsScreen> {
int _counter = 0;
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Details Screen - ${widget.label}'),
),
body: _build(context),
);
}
Widget _build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
'Details for ${widget.label} - Counter: $_counter',
style: Theme.of(context).textTheme.titleLarge,
),
const Padding(padding: EdgeInsets.all(4)),
TextButton(
onPressed: () {
setState(() {
_counter++;
});
},
child: const Text('Increment counter'),
),
const Padding(padding: EdgeInsets.all(8)),
const Padding(padding: EdgeInsets.all(4)),
TextButton(
onPressed: () {
GoRouter.of(context).go('/login');
},
child: const Text('Logout'),
),
],
),
);
}
}
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Login Screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () {
context.go('/login/detailLogin');
},
child: const Text('Go to the Details Login screen'),
),
],
),
),
);
}
}
class DetailLoginScreen extends StatelessWidget {
const DetailLoginScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Details Login Screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <ElevatedButton>[
ElevatedButton(
onPressed: () {
// context.go('/sectionA');
context.go('/sectionB');
},
child: const Text('Go to BottomNavBar'),
),
],
),
),
);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
17263 次 |
| 最近记录: |