文章目录
前言
路由之间的跳转是任何前端开发绕不开的话题!
一、路由
- Navigator是一个路由管理的组件
- route一个屏幕,或者是指一个Page Widget
二、路由类型
1.静态路由(也叫命名路由)
代码如下(示例):
基础的页面直接的跳转,从homepage 跳转到 onepage,命名好对应的路由的名字,后续跳转直接填写路由的名字就可以跳转到指定的路由页面
void main() {
runApp(
MyApp(),
);
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter route',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
routes: {
// 是个map 的形式,'/' 表示了默认路由,也就是主页面
"/": (context) => const HomePage(),
"/one": (context) => OnePage(),
},
initialRoute: '/',
);
}
}
//命名好之后,可以在跳转的时候直接调用名字:
Navigator.of(context).pushNamed("/one");//跳转到页面one 对应的OnePage()
Navigator.pushNamed(context, "new_page");
2.动态路由
代码如下(示例):
不需要注册,可以直接调用,和跳转到指定的页面
//MaterialPageRoute 是 android 风格页面切换
//CupertinoPageRoute 是ios 风格的页面切换
//
//方式一
Navigator.push(context, MaterialPageRoute(builder: (context){
return OnePage();
}));
//方式二
Navigator.of(context).push(
MaterialPageRoute(builder: (context){
return OnePage();
})
);
PageRouteBuilder:实现自定义页面切换的动画功能
Navigator.push(
context,
PageRouteBuilder(pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return _TwoPage();
}, transitionsBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
return SlideTransition(
position: Tween(begin: Offset(-1, 0), end: Offset(0, 0))
.animate(animation),
child: child,
);
}));
Navigator
提示:Navigator是一个路由管理的组件
- Future push(BuildContext context, Route route):将给定的路由入栈,返回值是一个Future对象,用以接收新路由出栈(即关闭)时的返回数据。
exp:
Navigator.push(context, MaterialPageRoute(builder: (context){
return OnePage();
})) - pop:出栈,顶出顶部的栈。Navigator.pop(context);返回上一个路由页面:
- 其他的方法 pushName等
路由传值的实现
动态路由跳转传递参数:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
MyApp(),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter route',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
// routes: {
// // 是个map 的形式,'/' 表示了默认路由,也就是主页面
// "/": (context) => const HomePage(),
// },
// initialRoute: '/',
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// List<Widget> listWidget = [OnePage()];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('route 功能演示'),
),
body: SingleChildScrollView(
child: Column(
children: [
TextButton(
onPressed: () async {
var result = await Navigator.of(context)
.push(MaterialPageRoute(builder: (contest) {
return OnePage(
text: '我是传递进去的数值',
);
}));
print("路由返回值: $result");
},
child: const Text('跳转带参数'),
),
],
),
),
);
}
}
class OnePage extends StatelessWidget {
OnePage({Key? key, required this.text}) : super(key: key);
final String text;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('data'),
),
body: Center(
child: Container(
child: Column(
children: [
Text(text),
IconButton(
onPressed: () => Navigator.pop(context, '我是返回的数值'),
icon: const Icon(Icons.backpack),
),
],
)),
),
);
}
}
静态路由页面跳转传递参数:
//Navigator.of(context).pushNamed("/onapge",arguments:{"argms":"这是传入A的参数"});
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
MyApp(),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter route',
theme: ThemeData(
primarySwatch: Colors.blue,
),
// home: HomePage(),
routes: {
// 是个map 的形式,'/' 表示了默认路由,也就是主页面
"/": (context) => const HomePage(),
'/onapge': (context) => OnePage(
text: 'first title',
)
},
initialRoute: '/',
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// List<Widget> listWidget = [OnePage()];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('route 功能演示'),
),
body: SingleChildScrollView(
child: Column(
children: [
TextButton(
onPressed: () async {
var result = await Navigator.of(context)
.pushNamed('/onapge', arguments: 'two title');
print("路由返回值: $result");
},
child: const Text('跳转带参数'),
),
],
),
),
);
}
}
class OnePage extends StatelessWidget {
OnePage({Key? key, required this.text}) : super(key: key);
final String text;
@override
Widget build(BuildContext context) {
//获取到传递进来的参数
var args = ModalRoute.of(context)?.settings.arguments;
return Scaffold(
appBar: AppBar(
title: Text('data'),
),
body: Center(
child: Container(
child: Column(
children: [
Text(args.toString() ?? 'ddd'),
IconButton(
onPressed: () => Navigator.pop(context, '我是返回的数值'),
icon: const Icon(Icons.backpack),
),
],
)),
),
);
}
}
onGenerateRoute
可以通过onGenerateRoute控制路由页面跳转的权限,比如哪些页面需要登陆之后才能查看和查阅的,就可以在onGenerateRoute里面进行处理,避免在每个page 的页面里面进行判断处理,路由拦截的功能
## 抽离main.dart里面的路由使代码更加简单
```dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'myroute.dart';
void main() {
runApp(
MyApp(),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter route',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: MYRouter.initialRoute,
routes: MYRouter.routes,
onGenerateRoute: MYRouter.generateRoute,
onUnknownRoute: MYRouter.unknownRoute,
);
}
}
class HomePage extends StatefulWidget {
static const String routeName = "/homepage";
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// List<Widget> listWidget = [OnePage()];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('route 功能演示'),
),
body: SingleChildScrollView(
child: Column(
children: [
TextButton(
onPressed: () {
},
child: const Text('跳转带参数'),
),
],
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:testcopy/main.dart';
import 'one.dart';
import 'two.dart';
import 'main.dart';
class MYRouter {
static final Map<String, WidgetBuilder> routes = {
One.routeName: (ctx) =>
One(), // 记得在页面里面 声明静态变量 static const String routeName = "/";
Two.routeName: (ctx) =>
Two() // 声明静态变量 static const String routeName = "/about";
};
static const String initialRoute = HomePage.routeName;
static final RouteFactory generateRoute = (settings) {
//判断当前的路由使什么名字,路由页面是否需要登陆之后才能查看可以在这边做操作
if (settings.name == NewPage.routeName) {
return MaterialPageRoute(builder: (ctx) {
return NewPage(settings.arguments);
});
}
return null;
};
//路由跳转不到指定的路由页面的时候,就显示该缺省页面
static final RouteFactory unknownRoute = (settings) {
return MaterialPageRoute(builder: (ctx) {
return UnkonwPage();
});
};
}
import 'package:flutter/material.dart';
class One extends StatefulWidget {
static const String routeName = "/one";
const One({Key? key}) : super(key: key);
_OneState createState() => _OneState();
}
class _OneState extends State<One> {
Widget build(BuildContext context) {
return Container();
}
}
import 'package:flutter/material.dart';
class Two extends StatefulWidget {
static const String routeName = "/two";
const Two({Key? key}) : super(key: key);
_TwoState createState() => _TwoState();
}
class _TwoState extends State<Two> {
Widget build(BuildContext context) {
return Container();
}
}
三.go_router
go_router是flutter.dev 官方推出的路由管理插件,非内置,有需要导入
push 和go的区别
- push到的页面会被压入栈,将目标路由添加到现有路由之上,go会丢弃前page,替换为go到的页面
- 假如在当前页面,点击A跳转到A页面,使用push。点击B跳转到B页面,使用GO。那么点击A的返回按键会回到刚才的页面,在B 页面点击返回不能直接,需要在backbutton 添加 go到主页面的功能
参数的传递 go_router
例子:
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() => runApp(App());
class App extends StatelessWidget {
App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routeInformationProvider: _router.routeInformationProvider,
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
title: 'GoRouter Example',
theme: ThemeData(primaryColor: Colors.cyanAccent),
);
}
final GoRouter _router = GoRouter(
// errorBuilder: (context, state) => ScreenA(state.error),//遇到错误的页面或者是没有路径的路由,会导入到当前页面
initialLocation: '/',
routes: <GoRoute>[
GoRoute(
path: '/sp',
name: 'sp',
builder: (BuildContext context, GoRouterState state) {
// state.queryParams 接收用问号隔开的参数
final query = state.queryParams['query'];
return SearchPage(query: query!);
},
),
GoRoute(
path: '/md/:id',
name: 'md',
builder: (BuildContext context, GoRouterState state) {
// state.params 接收 `/` 隔开的参数(按位置)
final movieid = state.params['id']!;
return MovieDetailPage(
id: movieid,
);
},
),
GoRoute(
name: 'setpage',
path: '/setpage',
builder: (BuildContext context, GoRouterState state) {
return SettingPage();
},
),
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return const HomePage();
},
),
],
);
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('go_router: ^4.4.1'),
),
body: Column(
children: [
ListTile(
leading: const Icon(Icons.settings),
title: const Text('setting'),
onTap: () {
context.push('/setpage');
},
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('search page'),
onTap: () {
context.push('/sp?query=flutter');
},
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('search page pushname'),
onTap: () {
GoRouter.of(context)
.pushNamed('sp', queryParams: {'query': 'look'});
},
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('movie detail path'),
onTap: () {
GoRouter.of(context).push('/md/50000');
},
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('movie detail named'),
onTap: () {
GoRouter.of(context).pushNamed(
'md',
params: {'id': '123456'},
);
},
),
],
),
);
}
}
class MovieDetailPage extends StatelessWidget {
final String id;
const MovieDetailPage({Key? key, required this.id}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Movie detail page'),
),
body: Center(
child: Text('当前ID 是 $id'),
),
);
}
}
class SettingPage extends StatelessWidget {
const SettingPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('setting page'),
),
);
}
}
class SearchPage extends StatelessWidget {
final String query;
const SearchPage({Key? key, required this.query}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('setting page'),
),
body: Center(child: Text('search info is $query')),
);
}
}
路由之间传递参数
1.没有参数的路由之间的跳转
//定义GoRoute路由
GoRoute(
name: 'setpage',
path: '/setpage',
builder: (BuildContext context, GoRouterState state) {
return SettingPage();
},
),
//需要跳转到该页面
GoRouter.of(context).push('/setpage');
GoRouter.of(context).pushNamed('setpage');
context.push('/setpage');
context.pushNamed('setpage');
context.go('/setpage');
context.goNamed('setpage');
2.通过在路由传递参数
//定义GoRoute路由
GoRoute(
name: 'setpage',
path: '/setpage',//传递的时候用?
builder: (BuildContext context, GoRouterState state) {
//读取参数传递进去
final username= state.queryParams['username'];
return SettingPage(username:username);
},
),
//需要跳转到该页面
GoRouter.of(context).push('/setpage?usernam=JACK');
context.push('/setpage?usernam=JACK');
context.go('/setpage?usernam=JACK');
GoRouter.of(context).pushNamed('setpage',queryParams{"username:username":"JACK"});
context.goNamed('setpage',queryParams{"username:username":"JACK"});
context.pushNamed('setpage',queryParams{"username:username":"JACK"});
3.在定义路由的时候 配置path 来传递参数
path_to_regexp包可以匹配相对应的path
path_to_regexp 包
//定义GoRoute路由
GoRoute(
name: 'setpage',
path: '/setpage/:username',//位置,如同url
builder: (BuildContext context, GoRouterState state) {
//读取参数传递进去
final username= state.params['username'];
return SettingPage(username:username);
},
),
//需要跳转到该页面
GoRouter.of(context).push('/setpage/JACK');
context.push('/setpage/JACK');
context.go('/setpage/JACK');
GoRouter.of(context).pushNamed('setpage',params{"username":"JACK"});
context.goNamed('setpage',params{"username":"JACK"});
context.pushNamed('setpage',params{"username":"JACK"});