一.什么是Flutter的路由
A widget that manages a set of child widgets with a stack discipline.
Flutter 的路由指的是一个页面或者弹窗,本质上都是Widget对象
二.什么是Flutter的导航器
Mobile apps typically reveal their contents via full-screen elements called "screens" or "pages". In Flutter these elements are called routes and they're managed by a Navigator widget. The navigator manages a stack of Routeobjects and provides two ways for managing the stack, the declarative API Navigator.pages or imperative APINavigator.push and Navigator.pop.
Flutter 的导航器是一个负责管理路由堆栈信息的Widget对象
三.例子
A.导航器由MeterialApp提供,home对应的Widget是堆栈底部的一个页面
void main() {
runApp(MaterialApp(home: MyAppHome()));
}
B.我们可以通过导航器向堆栈中压入一个Widget作为路由
Navigator.push(context, MaterialPageRoute<void>(
builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('My Page')),
body: Center(
child: TextButton(
child: Text('POP'),
onPressed: () {
Navigator.pop(context);
},
),
),
);
},
));
C.使用命名路由
void main() {
runApp(MaterialApp(
home: MyAppHome(), // becomes the route named '/'
routes: <String, WidgetBuilder> {
'/a': (BuildContext context) => MyPage(title: 'page A'),
'/b': (BuildContext context) => MyPage(title: 'page B'),
'/c': (BuildContext context) => MyPage(title: 'page C'),
},
));
}
Navigator.pushNamed(context, '/b');
D.接受返回值
bool value = await Navigator.push(context, MaterialPageRoute<bool>(
builder: (BuildContext context) {
return Center(
child: GestureDetector(
child: Text('OK'),
onTap: () { Navigator.pop(context, true); }
),
);
}
));
F.弹窗形式的路由
For example: showDialog, showMenu, and showModalBottomSheet.
G:自定义路由
This route does not obscure the entire screen because it specifies
opaque: false
, just as a popup route does
Navigator.push(context, PageRouteBuilder(
opaque: false,
pageBuilder: (BuildContext context, _, __) {
return Center(child: Text('My PageRoute'));
},
transitionsBuilder: (___, Animation<double> animation, ____, Widget child) {
return FadeTransition(
opacity: animation,
child: RotationTransition(
turns: Tween<double>(begin: 0.5, end: 1.0).animate(animation),
child: child,
),
);
}
))
F.子路由 React和Vue都有类似概念,一个页面内还能有路由
/// Flutter code sample for Navigator
// The following example demonstrates how a nested [Navigator] can be used to
// present a standalone user registration journey.
//
// Even though this example uses two [Navigator]s to demonstrate nested
// [Navigator]s, a similar result is possible using only a single [Navigator].
//
// Run this example with `flutter run --route=/signup` to start it with
// the signup flow instead of on the home page.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Code Sample for Navigator',
// MaterialApp contains our top-level Navigator
initialRoute: '/',
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => const HomePage(),
'/signup': (BuildContext context) => const SignUpPage(),
},
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return DefaultTextStyle(
style: Theme.of(context).textTheme.headline4!,
child: Container(
color: Colors.white,
alignment: Alignment.center,
child: const Text('Home Page'),
),
);
}
}
class CollectPersonalInfoPage extends StatelessWidget {
const CollectPersonalInfoPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return DefaultTextStyle(
style: Theme.of(context).textTheme.headline4!,
child: GestureDetector(
onTap: () {
// This moves from the personal info page to the credentials page,
// replacing this page with that one.
Navigator.of(context)
.pushReplacementNamed('signup/choose_credentials');
},
child: Container(
color: Colors.lightBlue,
alignment: Alignment.center,
child: const Text('Collect Personal Info Page'),
),
),
);
}
}
class ChooseCredentialsPage extends StatelessWidget {
const ChooseCredentialsPage({
Key? key,
required this.onSignupComplete,
}) : super(key: key);
final VoidCallback onSignupComplete;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onSignupComplete,
child: DefaultTextStyle(
style: Theme.of(context).textTheme.headline4!,
child: Container(
color: Colors.pinkAccent,
alignment: Alignment.center,
child: const Text('Choose Credentials Page'),
),
),
);
}
}
class SignUpPage extends StatelessWidget {
const SignUpPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// SignUpPage builds its own Navigator which ends up being a nested
// Navigator in our app.
return Navigator(
initialRoute: 'signup/personal_info',
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
switch (settings.name) {
case 'signup/personal_info':
// Assume CollectPersonalInfoPage collects personal info and then
// navigates to 'signup/choose_credentials'.
builder = (BuildContext context) => const CollectPersonalInfoPage();
break;
case 'signup/choose_credentials':
// Assume ChooseCredentialsPage collects new credentials and then
// invokes 'onSignupComplete()'.
builder = (BuildContext _) => ChooseCredentialsPage(
onSignupComplete: () {
// Referencing Navigator.of(context) from here refers to the
// top level Navigator because SignUpPage is above the
// nested Navigator that it created. Therefore, this pop()
// will pop the entire "sign up" journey and return to the
// "/" route, AKA HomePage.
Navigator.of(context).pop();
},
);
break;
default:
throw Exception('Invalid route: ${settings.name}');
}
return MaterialPageRoute<void>(builder: builder, settings: settings);
},
);
}
}
State Restoration(页面状态恢复暂时不理解实际场景)
- A Route added with the restorable imperative API (restorablePush, restorablePushNamed, and all other imperative methods with "restorable" in their name) restores its state if all routes below it up to and including the first Page-based route below it are restored. If there is no Page-based route below it, it only restores its state if all routes below it restore theirs.
原生承载容器
通过Flutter官方提供的FlutterView,FlutterFragment,FlutterActivity作为绘制承载体,
可以推测其内部必然可以映射到Flutter 导航器上的某个路由,从而绘制承载
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize" />
// 方式一、FutterActivity显示的路由名称为"/",不可设置
startActivity(
FlutterActivity.createDefaultIntent(this)
);
// 方式二、FutterActivity显示的路由名称可设置,每次都创建一个新的FlutterEngine对象
startActivity(
FlutterActivity
.withNewEngine()
.initialRoute("route1")
.build(this)
);
// 方式三、FutterActivity显示的路由名称可设置,使用缓存好的FlutterEngine对象
startActivity(
FlutterActivity
.withCachedEngine("my_engine_id")
.build(this)
);
参考