本教程前半部分属于入门,后半部分属于进阶
1.routes 和navigator 的关系 ?
大部分应用程序都包含多个页面,并希望用户能从当前屏幕平滑过渡到另一个屏幕。移动应用程序通常通过被称为“屏幕”或“页面”的全屏元素来显示内容Flutter 中,这些元素被称为路由(Route),它们由导航器(Navigator)控件管理。导航器管理着路由对象的堆栈并提供管理堆栈的方法,如
Navigator.push
和Navigator.pop
,通过路由对象的进出栈来使用户从一个页面跳转到另一个页面。
2. 怎么使用 ?
(1)push方法
设置一个构造函数 (第二个page的初始方法)
final String title;
Page({this.title});
跳转传值
Navigator.push(context,
MaterialPageRoute(builder: (context) => Page(title: "题目")));
返回
Navigator.pop(context);
(2) pushName方法
在初始化APP的地方MaterialApp,设置一个路由列表
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp( // 这个是标记
home: MainPage(),
debugShowCheckedModeBanner: false,
routes: { // 就是它
'/page':(context) => Page()
'/home':(context) => Home()
},
);
}
}
在需要跳转的时候
Navigator.pushNamed(context, '/page');
这里有一个问题就是使用 Navigator.pushNamed 时无法直接给新页面传参数,目前官方还没有标准解决方案,我知道的方案是在 onGenerateRoute 回调方法和arguments参数设置。
完整事例
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
//监听导航routes 数据 PassArguments
onGenerateRoute: (settings) {
//对比路由名称
if (settings.name == PassArgumentsScreen.routeName) {
final ScreenArguments args = settings.arguments;
//返回 MaterialPageRoute
return MaterialPageRoute(
builder: (context) {
return PassArgumentsScreen(
title: args.title,
message: args.message,
);
},
);
}
},
title: 'Navigation with Arguments',
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Screen'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//设置一个button
RaisedButton(
child: Text("Navigate to screen that extracts arguments"),
onPressed: () {
// 设置传值参数 arguments /settings
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ExtractArgumentsScreen(),
settings: RouteSettings(
arguments: ScreenArguments(
'Extract Arguments Screen',
'This message is extracted in the build method.',
),
),
),
);
},
),
RaisedButton(
child: Text("Navigate to a named that accepts arguments"),
onPressed: () {
Navigator.pushNamed(
context,
PassArgumentsScreen.routeName,
arguments: ScreenArguments(
'Accept Arguments Screen',
'This message is extracted in the onGenerateRoute function.',
),
);
},
),
],
),
),
);
}
}
// push界面
class ExtractArgumentsScreen extends StatelessWidget {
static const routeName = '/extractArguments';
@override
Widget build(BuildContext context) {
final ScreenArguments args = ModalRoute.of(context).settings.arguments;
return Scaffold(
appBar: AppBar(
title: Text(args.title),
),
body: Center(
child: Text(args.message),
),
);
}
}
//接受界面
class PassArgumentsScreen extends StatelessWidget {
static const routeName = '/passArguments';
final String title;
final String message;
// MaterialApp widget.
const PassArgumentsScreen({
Key key,
@required this.title,
@required this.message,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Text(message),
),
);
}
}
// 设置任意参数
class ScreenArguments {
final String title;
final String message;
ScreenArguments(this.title, this.message);
}
看懂了上面的原理当然可以简化了。
定义要接受参数的界面的时候
// 定义接受的构造函数
final arguments;
Page({this.arguments};
在上一界面传值的时候
Navigator.pushNamed(context,'/search',argument:{
'id':'1123'; // 参数随便写
'name':'sunny'
})
当然路由配置的时候要这样
final routes = {
'page':(context,{arguments})=>Page(arguments:arguments);
'page':(context,{arguments})=>Page(arguments:arguments);
'page':(context,{arguments})=>Page(arguments:arguments);
'page':(context,{arguments})=>Page(arguments:arguments);
}
final String name = setting.name;
final Function pagContentBuilder = this.routes[name];
当然你也可以吧参数和路由名称都封装到一个类中去 。说说思路,全局单利路由文件设置好 ,设置路由对象 ,对象的个数和名称提前固定 ,可以根据名称随时更改需要传值的参数 。 在界面跳转前更改参数 ,传入参数 ,在界面跳转后接受数据。
面对逆向的时候还可以加上加密措施和代码混淆
//Routes.dart
import 'pa/material' import '所有界面'
final routes = {
'page':(context,{arguments})=>Page(arguments:arguments);
'page':(context,{arguments})=>Page(arguments:arguments);
'page':(context,{arguments})=>Page(arguments:arguments);
'page':(context,{arguments})=>Page(arguments:arguments);
}
// 生成路由回调的的固定写法
var onGenerateroute = (RounteSettings setting){
final String name = setting.name;
final Function pagContentBuilder = routes[name];
if (pagContentBuilder != null){
if(setting.arguments != null){
final Route route = MaterialpageRoute(
buider:(context) =>
PageContentBuilder(context,arguments:setting.arguments);
)
return route;
}else{
final Route route = MaterialpageRoute(
buider:(context) =>
PageContentBuilder(context);
)
return route;
}
}
}
3.路由的命名规范 ?
树状结构命名法
'\' #主页
'\home' #主页 下 home界面
'\my' #主页 下 my界面
'\home\name' #主页 下 home界面 下name界面
3.initialRoute 的作用 ?
initialRoute
同样是MaterialApp
的属性 ,也就是相当于iOS里面的appdelegate部分,在全局只能设置一次 。 在routes
里面设置的路由都可以通过initialRoute
来设置,也就是第一次启动app时候,我们的初始界面是那个。
路由的返回功能
Navigator.of(context).pop();
返回根路由/ 替换路由 在跳转的时候使用替换路由,在返回的时候会直接返回到根目录
Navigator.of(context).pushReplacementNamed('/firstPage');
或者直接返回根的代码/ 没有的话直接更改界面 。
Navigator.of(context).pushAndRemoveUntil(
// 中间的界面置为空,直到我们返回的页面。
newMaterialPageRutes(builder:context)=>new Tab bar(index : 1),
(route)=>route==null;
)
(看到这里的都很厉害,给你们点终极福利)
fluro的封装使用
国外都流行用这个现在,下面是最权威的封装
1设置一个全局的router,
形式多样,单例都可以,此处就放到Application类中了。
//application.dart
import 'package:fluro/fluro.dart';
class Application {
static Router router;
}
2. 设置router 信息
引入handle文件,具体界面目录
//routers.dart
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
import 'package:~/route_handlers.dart';
class Routes {
static String root = '/';
//添加路由命名
static String chatDetail = '/chat';
static void configureRoutes(Router router) {
router.notFoundHandler = new Handler(
handlerFunc: (BuildContext context, Map> params) {
print('Route was not found.');
}
);
router.define(root, handler: rootHandler);
//一条一条往后加处理方式
router.define(chatDetail, handler: chatDetailHandler);
}
}
3. 设置routerHandle 信息
具体每个页面怎么处理都在这个文件中
//route_handlers.dart
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
import 'package:flutter_whatsapp/src/home.dart';
// 一条一条往后加,此处没做任何处理
var rootHandler = new Handler(
handlerFunc: (BuildContext context, Map> params) {
return new Home();
}
);
//此处解析了一个参数profileId
var chatDetailHandler = new Handler(
handlerFunc: (BuildContext context, Map> params) {
int profileId = int.tryParse(params['profileId']?.first) ?? null;
// 此处调用构造方法
return new DetailChatScreen(
id: profileId,
);
}
);
4.使用
在MaterialApp
中初始化
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
import 'package:config/application.dart';
import 'package:config/routes.dart';
import 'package:/src/home.dart';
import 'package:/src/values/colors.dart';
class FlutteredApp extends StatefulWidget {
@override
_FlutteredAppState createState() => _FlutteredAppState();
}
class _FlutteredAppState extends State {
_FlutteredAppState() {
final router = new Router();
Routes.configureRoutes(router);
Application.router = router;
}
/// Default theme.
static final ThemeData _defaultTheme = new ThemeData(
primaryColor: primaryColor,
accentColor: Colors.white,
scaffoldBackgroundColor: scaffoldBgColor,
);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'setting',
theme: _defaultTheme,
onGenerateRoute: Application.router.generator,
);
}
}
调用
Application.router.navigateTo(
context,
"${Router.chatDetail}?profileId=${id}",
transition: TransitionType.inFromRight,
);
返回
Navigator.of(context).pop();