flutter-route
Route & Navigator
Route:一个页面要想被路由统一管理,必须包装为一个Route。
但是Route是一个抽象类,所以它是不能实例化的
MaterialPageRoute -> PageRoute -> ModalRoute -> TransitionRoute -> OverlayRoute -> Route
1: Navigator
Navigator:管理所有的Route的Widget,通过一个Stack来进行管理的
那么我们开发中需要手动去创建一个Navigator吗?
并不需要,我们开发中使用的MaterialApp、CupertinoApp、WidgetsApp它们默认是有插入Navigator的
所以,我们在需要的时候,只需要直接使用即可
Navigator.of(context)
1.1: Navigator有几个最常见的方法:
// 路由跳转:传入一个路由对象
Future<T> push<T extends Object>(Route<T> route)
// 路由跳转:传入一个名称(命名路由)
Future<T> pushNamed<T extends Object>(
String routeName, {
Object arguments,
})
// 路由返回:可以传入一个参数
bool pop<T extends Object>([ T result ])
2: 路由的使用
- 1-测试push跳转(无参数)
- 2-测试push跳转(有参数)
- 3-测试push跳转(有参数)+Pop返回监听 一
- 4-测试push跳转(有参数)+Pop返回监听 二
- 5-测试pushName跳转(无参数)(命名路由)
- 6-测试pushName跳转(有参数)(命名路由)
- 7-测试onGenerateRoute+传参数
// main.dart
import 'package:flutter/material.dart';
import 'package:flutter_route_navigator/tabbar/home.dart';
import 'package:flutter_route_navigator/tabbar/profile.dart';
import 'package:flutter_route_navigator/push/route_name.dart';
import 'package:flutter_route_navigator/push/test_push_route.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Content2();
}
}
class Content extends StatefulWidget {
@override
_ContentState createState() => _ContentState();
}
class _ContentState extends State<Content> {
var _selectedIndex = 0;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Route')),
body: IndexedStack(
index: _selectedIndex,
children: [
Home(),
Profile(),
],
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _selectedIndex,
items: [
_createItem(Icons.home, '首页'),
_createItem(Icons.center_focus_strong, '我的'),
],
onTap: (index) {
print('index: $index');
setState(() {
_selectedIndex = index;
});
},
),
),
);
}
BottomNavigationBarItem _createItem(IconData? icon, String lable) {
return BottomNavigationBarItem(
icon: Icon(icon, size: 20),
label: lable,
);
}
}
class Content2 extends StatefulWidget {
@override
_Content2State createState() => _Content2State();
}
class _Content2State extends State<Content2> {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'route',
theme: ThemeData(
primarySwatch: Colors.blue,
splashColor: Colors.transparent,
),
initialRoute: '/main',
routes: {
RouteName.mainRouteName: (context) => Main(),
RouteName.homeRouteName: (context) => Home(),
RouteName.profileRouteName: (context) => Profile(),
RouteName.pushNameByRouteName: (context) => PushNameByRoute(),
RouteName.pushNameByRouteAndParamesName: (context) =>
PushNameByRouteAndParames(),
// onGenerateRoute不需要这里映射关系
},
);
}
}
class Main extends StatefulWidget {
static const String routeName = '/main';
@override
_MainState createState() => _MainState();
}
class _MainState extends State<Main> {
var _selectedIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Route')),
body: IndexedStack(
index: _selectedIndex,
children: [
Home(),
Profile(),
],
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _selectedIndex,
items: [
_createItem(Icons.home, '首页'),
_createItem(Icons.center_focus_strong, '我的'),
],
onTap: (index) {
print('index: $index');
setState(() {
_selectedIndex = index;
});
},
),
);
}
BottomNavigationBarItem _createItem(IconData? icon, String lable) {
return BottomNavigationBarItem(
icon: Icon(icon, size: 20),
label: lable,
);
}
}
// home.dart
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_route_navigator/push/generate_route.dart';
import 'package:flutter_route_navigator/push/route_name.dart';
import 'package:flutter_route_navigator/push/test_push_route.dart';
class Home extends StatefulWidget {
static const String routeName = '/home';
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
String _home2Arg = '/';
String _home3Arg = '/';
String _home4Arg = '/';
String _home6Arg = '/';
String _home7Arg = '/';
@override
Widget build(BuildContext context) {
return ListView(
children: [
// 测试1
ListTile(
title: Text('1-测试push跳转(无参数)'),
subtitle: Text('接收pop参数: /'),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return PushByRoute();
}));
},
),
// 测试2
ListTile(
title: Text('2-测试push跳转(有参数)'),
subtitle: Text('接收pop参数: $_home2Arg'),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return PushByRouteAndParames('参数2');
})).then((value) {
setState(() {
_home2Arg = value;
});
});
// 跳转过去清空变量值
setState(() {
_home2Arg = '/';
});
},
),
// 测试3
ListTile(
title: Text('3-测试push跳转(有参数)+Pop返回监听 一'),
subtitle: Text('接收pop 返回来的参数: $_home3Arg'),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return PushByRouteAndParamesPopListen1('参数3');
})).then((value) {
setState(() {
_home3Arg = value;
});
});
// 跳转过去清空变量值
setState(() {
_home3Arg = '/';
});
},
),
// 测试4
ListTile(
title: Text('4-测试push跳转(有参数)+Pop返回监听 二'),
subtitle: Text('接收pop 返回来的参数: $_home4Arg'),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return PushByRouteAndParamesPopListen2('参数4');
})).then((value) {
setState(() {
_home4Arg = value;
});
});
// 跳转过去清空变量值
setState(() {
_home4Arg = '/';
});
},
),
// 测试5
ListTile(
title: Text('5-测试pushName跳转(无参数)(命名路由)'),
subtitle: Text('接收pop参数: /'),
onTap: () {
Navigator.of(context).pushNamed(RouteName.pushNameByRouteName);
},
),
// 测试6
ListTile(
title: Text('6-测试pushName跳转(有参数)(命名路由)'),
subtitle: Text('接收pop参数: $_home6Arg'),
onTap: () {
Navigator.of(context)
.pushNamed(RouteName.pushNameByRouteAndParamesName,
arguments: '参数6')
.then((value) {
setState(() {
_home6Arg = value.toString();
});
});
setState(() {
_home6Arg = '/';
});
},
),
// 测试7
ListTile(
title: Text('7-测试onGenerateRoute+传参数'),
subtitle: Text('接收pop的参数: $_home7Arg'),
onTap: () {
final settings = RouteSettings(
name: RouteName.generateRouteName,
arguments: '参数7',
);
Navigator.of(context)
.push(GenerateRoute.onGenerateRoute(settings)!)
.then((value) {
setState(() {
_home7Arg = value;
});
});
setState(() {
_home7Arg = '/';
});
},
),
],
);
}
}
class Home1 extends StatelessWidget {
static const String routeName = '/home';
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment(0, 0),
child: ElevatedButton(
child: Text('-> 跳转到首页详情detail page'),
onPressed: () {
print('click-Elevated');
/* 方式1
final route = MaterialPageRoute(builder: (context) {
return HomeDetail('100');
});
Navigator.of(context).push(route).then((value) => {
print('接收返回的数据: \'$value\''),
});
*/
/* 方式2
*/
_tapPushName(context);
},
),
);
}
_tapPushName(BuildContext context) {
Navigator.of(context)
.pushNamed('/detail', arguments: '我是参数来自home页面')
.then((value) => {
print('接收参数通过name: $value'),
});
}
}
// 1. 路由管理:
/* Route
> 一个页面要想被路由统一管理】、必须包装成一个Route
> 但是Route是一个抽象类】不能被实例化
MaterialPageRoute
> 事实上MaterialPageRoute并不是Route的直接子类
MaterialPageRoute在不同的平台有不同的表现
> 【上下】对Android平台,打开一个页面会从屏幕底部滑动到屏幕的顶部,关闭页面时从顶部滑动到底部消失
> 【左右】对iOS平台,打开一个页面会从屏幕右侧滑动到屏幕的左侧,关闭页面时从左侧滑动到右侧消失
> 当然,iOS平台我们也可以使用CupertinoPageRoute
MaterialPageRoute -> PageRoute -> ModalRoute -> TransitionRoute -> OverlayRoute -> Route
*/
/* Navigator
> 管理所有Route的Widget、通过一个Stack来管理的。
那么我们开发中需要手动去创建一个Navigator吗?
- 并不需要,我们开发中使用的MaterialApp、CupertinoApp、WidgetsApp它们默认是有插入Navigator的
- 所以,我们在需要的时候,只需要直接使用即可:
Navigator.of(context)
Navigator 几个常见的方法:
- 路由跳转: 传入一个路由对象
Future<T> push<T extends object>(Route<T> route)
- 路由跳转:传入一个名称(命名路由)
Future<T> pushNamed<T extends object>(String route, {Object arguments})
- 路由返回:可以传入一个参数
bool pop<T extends Object>( [T result] )
*/
// 2. 路由使用
/*
需求1:
1. 从首页跳转到详情页
2. 从详情页返回到首页
需求2:
1. 基于需求1 + 传递参数
路由返回的处理:
- 如果用户是点击左上角的返回箭头返回上一页的话、我们怎么处理:
- 方法1: 我们自定义返回箭头的监听
- 方法2: 给Scaffold包裹一层 WillPopScope
WillPopScope
- WillPopScope 有一个回调函数onWillPop、当我们点击返回按钮时会执行
- 这个函数要求有一个Future的返回值:
- true:那么系统会自动帮我们执行pop操作
- false:系统不再执行pop操作,需要我们自己来执行
注意:
两个方法实现有一个小差异:方法1 还会保留侧滑功能; 方法2会禁用侧滑功能、侧滑功能失效。
*/
// route_name.dart
class RouteName {
static const String homeRouteName = '/home';
static const String mainRouteName = '/main';
static const String detailRouteName = '/detail';
static const String profileRouteName = '/profile';
static const String pushNameByRouteName = '/pushNameByRoute';
static const String pushNameByRouteAndParamesName =
'/pushNameByRouteAndParames';
static const String generateRouteName = '/generateRouteName';
}
// test_push_route.dart
import 'package:flutter/material.dart';
/* Pop1: 无参数
*/
class PushByRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('测试push1'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('/'),
),
),
);
}
}
/* Pop2: 有参数
*/
class PushByRouteAndParames extends StatelessWidget {
final String arg;
PushByRouteAndParames(this.arg);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('测试push2'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pop('pop2');
},
child: Text('$arg'),
),
),
);
}
}
/* Pop3: 有参数 + 监听Pop
- AppBar.leading
*/
class PushByRouteAndParamesPopListen1 extends StatelessWidget {
final String arg;
PushByRouteAndParamesPopListen1(this.arg);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('测试push3'),
leading: IconButton(
onPressed: () {
Navigator.of(context).pop('pop-3-pop');
},
icon: Icon(Icons.arrow_back_ios),
),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pop('pop3');
},
child: Text('$arg'),
),
),
);
}
}
/* Pop4: 有参数 + 监听Pop2
- WillPopScope
这个函数要求有一个Future的返回值:
true:那么系统会自动帮我们执行pop操作
false:系统不再执行pop操作,需要我们自己来执行
*/
class PushByRouteAndParamesPopListen2 extends StatelessWidget {
final String arg;
PushByRouteAndParamesPopListen2(this.arg);
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
Navigator.of(context).pop('pop-4-pop-WillPopScope');
return Future.value(false);
},
child: Scaffold(
appBar: AppBar(
title: Text('测试push4'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pop('pop4');
},
child: Text('$arg'),
),
),
),
);
}
}
class PushNameByRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('测试pushName-5'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('/'),
),
),
);
}
}
class PushNameByRouteAndParames extends StatelessWidget {
@override
Widget build(BuildContext context) {
// PushName 获取传来的参数
final args = ModalRoute.of(context)?.settings.arguments.toString();
return WillPopScope(
onWillPop: () {
Navigator.of(context).pop('pop-6-pop');
return Future.value(false);
},
child: Scaffold(
appBar: AppBar(
title: Text('测试pushName-6'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pop('pop6');
},
child: Text('$args'),
),
),
),
);
}
}
// generate_route
import 'package:flutter/material.dart';
import 'package:flutter_route_navigator/push/route_name.dart';
/* onGenerateRoute
- onGenerateRoute不需要这里映射关系
*/
class GenerateRoute {
static MaterialPageRoute? onGenerateRoute(RouteSettings settings) {
final routeName = settings.name;
final arg = settings.arguments.toString();
switch (routeName) {
case RouteName.generateRouteName:
return MaterialPageRoute(
builder: (context) {
return TestGenerateRoute(arg);
},
);
default:
return null;
}
}
}
class TestGenerateRoute extends StatelessWidget {
final String message;
TestGenerateRoute(this.message);
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
Navigator.of(context).pop('pop-7-pop');
return Future.value(false);
},
child: Scaffold(
appBar: AppBar(title: Text('Generate Route Test')),
body: Center(
child: GestureDetector(
onTap: () {
Navigator.of(context).pop('pop-7');
},
child: Text('$message'),
),
),
),
);
}
}