Связанное Чтение
На основе Navigator1.0:
- Аннотации легальной маршрутизации Flutter
- Маршрутизация аннотаций метода Flutter 2.0
- Маршрутизация аннотаций метода Flutter 4.0
предисловие
Flutter 1.22В выпуске реализован Navigator 2.0, который дает разработчикам больше возможностей для выбора: вы можете гибко управлять стеком маршрутизации, обрабатывать ввод в браузере и вкладывать несколькоNavigator, хотя недочеты еще есть, но в принципе можно сделать路由我定
. Давайте вместе со мной погрузимся в мир маршрутизации Flutter. Версия исходного кода этой статьиFlutter Stable 1.22.6.
Основы маршрутизации
Navigator
Отвечает за весь стек маршрутизации, который фактически являетсяOverlayнесколько похожеStack
, мы часто используем его, чтобы сделатьtoast
,Напримерoktoast, по сути, каждая страница представляет собойOverlayEntry
.
RouteSettings
Используется для хранения имен маршрутов и параметров
Page
Появился в Navigator2.0, унаследован отRouteSettings
. В основном ответственны за собственное творениеRoute
И упомяните ключ, этот ключ сзадиPage
Основа для изменения суждения, обратите внимание, что этот ключ должен быть единственным ключом при нормальных обстоятельствах.
Route
В основном отвечает за обработку анимации прыжка, сохранениеRouteSettings
(т.е. имя и параметры маршрута). Это также связь наложения, навигатор, _roouteentry.
- существует
push
метод,Route
Передайте его напрямую, назначьте его_RouteEntry
увеличить до_history
середина
Future<T> push<T extends Object>(Route<T> route) {
_history.add(_RouteEntry(route, initialState: _RouteLifecycle.push));
_flushHistoryUpdates();
_afterNavigation(route);
return route.popped;
}
-
NavigatorState.build
Значение initialEntries в Overlay равно OverlayEntry всех _RouteEntry.route в _history.
Iterable<OverlayEntry> get _allRouteOverlayEntries sync* {
for (final _RouteEntry entry in _history)
yield* entry.route.overlayEntries;
}
Overlay(
key: _overlayKey,
initialEntries: overlay == null ? _allRouteOverlayEntries.toList(growable: false) : const <OverlayEntry>[],
),
RouteTransitionRecord / _RouteEntry
Последний наследует первый, записывая состояние каждого маршрута.
RouteTransitionRecord
имеет следующие свойства и методы, которые находятся вTransitionDelegate.resolve
установить в методе
isWaitingForEnteringDecision
отметьте, ожидает ли маршрут выхода на экранisWaitingForExitingDecision
Ожидает ли отмеченный маршрут выхода с экрана
метод | и из | анимация | возвращаемый параметр |
---|---|---|---|
markForPush() | Войти | имеют | N/A |
markForAdd() | Войти | без | N/A |
markForPop([dynamic result]) | вне | имеют | имеют |
markForComplete([dynamic result]) | вне | без | имеют |
markForRemove() | вне | без | без |
TransitionDelegate / DefaultTransitionDelegate
RouteTransitionRecord
существуетTransitionDelegate.resolve
в настройках.
1. КогдаPages
Инициировать обновление при его изменении, выполнитьNavigatorState.didUpdateWidget
2. В этом методе сравните старый и новыйPages
(К, о котором мы упоминали ранее, добавил ключ, который здесь сыграл роль)
3.resolve
Определите, какие из них добавляются, а какие удаляются.
Примерная логика в DefaultTransitionDelegate следующая
Новые страницы | старые страницы | условие |
---|---|---|
A=>B | A=>B=>C | C markForPop |
A=>C | A=>B=>C | B markForComplete |
A=>B=>C=>D | A=>B=>C | D markForPush |
A=>B=>D=>C | A=>B=>C | D markForAdd |
NavigatorObserver
Используется для отслеживания push, pop, replace, удаления маршрутизации и смахивания влево для выхода со страницы на платформе ios. Обычно мы можем сделать вход на страницу и выход из скрытых точек в этом, а также решить конфликт между Flutter и родным ios выходом влево в смешанной разработке.
class NavigatorObserver {
NavigatorState get navigator => _navigator;
NavigatorState _navigator;
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) { }
void didPop(Route<dynamic> route, Route<dynamic> previousRoute) { }
void didRemove(Route<dynamic> route, Route<dynamic> previousRoute) { }
void didReplace({ Route<dynamic> newRoute, Route<dynamic> oldRoute }) { }
void didStartUserGesture(Route<dynamic> route, Route<dynamic> previousRoute) { }
void didStopUserGesture() { }
}
Navigator 1.0
Все используют это уже давно, и я думаю, что все это прекрасно знают.Рекомендуется использовать именованную маршрутизацию, а вonGenerateRoute
Унифицированное управление маршрутизацией в обратных вызовах. Ранее правовая аннотация маршрутизации также основывалась наNavigator 1.0
Да, если вы не понимаете, вы можете просмотреть его.
- Аннотации легальной маршрутизации Flutter
- Маршрутизация аннотаций метода Flutter 2.0
- Маршрутизация аннотаций метода Flutter 4.0
Navigator 2.0
Первый взглядNavigatorЧто изменилось в последней конструкции .
const Navigator({
Key key,
// Navigator 2.0 的新东西,由它间接把路由栈暴露给用户
this.pages = const <Page<dynamic>>[],
// 当使用代码 pop 或者按浏览器后退按钮的时候的回调,这个时候用户可以自行处理逻辑
this.onPopPage,
this.initialRoute,
this.onGenerateInitialRoutes = Navigator.defaultGenerateInitialRoutes,
this.onGenerateRoute,
this.onUnknownRoute,
// 由于 pages 是暴露给用户了,所以这里可以自己设置页面的过度动画状态,见上面 [TransitionDelegate] 部分。一般来说直接用默认的就好了
this.transitionDelegate = const DefaultTransitionDelegate<dynamic>(),
// 是否通知给引擎,主要是在 Web 上面,告诉浏览器 URL 的改变,同步地址
this.reportsRouteUpdateToEngine = false,
this.observers = const <NavigatorObserver>[],
})
Навигатор Каштаны
- давай подготовим
Page
, простая реализацияcreateRoute
метод
class MyPage extends Page<void> {
const MyPage({
@required LocalKey key,
@required String name,
@required this.widget,
Object arguments,
}) : super(
key: key,
name: name,
arguments: arguments,
);
final Widget widget;
@override
Route<void> createRoute(BuildContext context) {
return MaterialPageRoute<void>(
settings: this,
builder: (BuildContext context) => widget,
);
}
}
- Подготовить
pages
, это наш стек маршрутизации, инициализированная страница MainPage. Обратите внимание, что ключ должен быть уникальным.
final List<MyPage> _pages = <MyPage>[
MyPage(
name: 'MainPage', widget: const TestPage('MainPage'), key: UniqueKey()),
];
- Используя Navigator, стоит отметить, что
-
pages
должна быть новая коллекция, такая, что вNavigatorState.didUpdateWidget
оценит разницу и обновит -
onPopPage
Обратный вызов может работать в соответствии со своей собственной ситуацией и, наконец,setState
,уведомлятьpages
Разнообразие. позвонивnavigatorKey.currentState.pop()
Или нажатие кнопки «Назад» на панели приложений вызовет обратный вызов.
Navigator(
reportsRouteUpdateToEngine: true,
key: navigatorKey,
pages: _pages.toList(),
onPopPage: (Route<dynamic> route, dynamic result) {
if (_pages.length > 1) {
_pages.removeLast();
setState(() {});
return route.didPop(result);
}
return false;
},
),
- Теперь вы можете манипулировать стеком маршрутизации по своему усмотрению, вот несколько примеров.
Добавить новую страницу, эквивалентнуюpush
_pages.add(
MyPage(
name: 'MainPageA',
widget: const TestPage('MainPageA'),
key: UniqueKey()),
);
setState(() {});
удалить последний, что эквивалентноpop
if (_pages.length > 1) {
_pages.removeLast();
setState(() {});
}
Использовать напрямуюNavigatorState.pop()
метод, триггерonPopPage
Перезвоните
navigatorKey.currentState.pop();
Теперь кажется, что мы можем отлично контролировать весь стек маршрутизации, этого достаточно? Ответа точно недостаточно, мы с ним еще не разобрались浏览器输入修改URL
, 浏览器返回键
,安卓物理返回键
,так же какNavigator 嵌套
Эта проблема.
Router
Что нового в Navigator 2.0, на первый взгляд все новое.
const Router({
Key key,
this.routeInformationProvider,
this.routeInformationParser,
@required this.routerDelegate,
this.backButtonDispatcher,
})
RouteInformation
Возможны следующие 2 сценария:
- RouteInformationProvider => Router , что происходит, когда доступен новый маршрут, например, при вводе нового URL-адреса в браузере или инициализации маршрута в настройках кода.
- Router => RouteInformationProvider, это происходит только тогда, когда механизм уведомлений меняет URL-адрес браузера.
class RouteInformation {
const RouteInformation({this.location, this.state});
/// 比如: `/`, `/path`, `/path/to/the/app`.
final String location;
/// 当前页面的状态,比如滚动位置,必须是可以序列化的对象.
final Object state;
}
RouteInformationParser
В основном отвечает за анализRouteInformation
,здесьT
в общемString
илиRouteSettings
, что нам удобно анализировать.
abstract class RouteInformationParser<T> {
const RouteInformationParser();
/// 浏览器中输入一个新URL,或者在代码设置初始化路由
Future<T> parseRouteInformation(RouteInformation routeInformation);
/// 注意如果 reportsRouteUpdateToEngine 设置为true了,这个必须实现,不能返回 null。
/// 传入的 T 从 RouterDelegate.currentConfiguration 获得
RouteInformation restoreRouteInformation(T configuration) => null;
}
RouteInformationProvider
В основном отвечает за уведомление об изменениях RouteInformation.
abstract class RouteInformationProvider extends ValueListenable<RouteInformation> {
void routerReportsNewRouteInformation(RouteInformation routeInformation) {}
}
Обычно используется для инициализации маршрутизации.
routeInformationProvider: PlatformRouteInformationProvider(
initialRouteInformation: const RouteInformation(
location: '/mainpage',
),
),
RouterDelegate
Создать и настроитьNavigator
прокси, как писалось ранееNavigator demoпочти. Разница в том, чтобы увеличить пару浏览器输入修改URL
, 浏览器返回键
,安卓物理返回键
обработка.
abstract class RouterDelegate<T> extends Listenable {
/// 初始化路由会调用该方法
Future<void> setInitialRoutePath(T configuration) {
return setNewRoutePath(configuration);
}
/// 新增路由比如 浏览器中输入一个新URL,或者在代码设置初始化路由
Future<void> setNewRoutePath(T configuration);
/// `浏览器返回键`,`安卓物理返回键` 会调用该方法
Future<bool> popRoute();
/// RouteInformationParser.restoreRouteInformation 会
/// 获取该值用于报告给引擎,特别是在 Web 应用中
T get currentConfiguration => null;
/// 返回 Navigator
Widget build(BuildContext context);
}
PopNavigatorRouterDelegateMixin
Чтобы помочь вам реализовать RouterDelegatepopRoute
метод, не требуется.
mixin PopNavigatorRouterDelegateMixin<T> on RouterDelegate<T> {
/// The key used for retrieving the current navigator.
///
/// When using this mixin, be sure to use this key to create the navigator.
GlobalKey<NavigatorState> get navigatorKey;
@override
Future<bool> popRoute() {
final NavigatorState navigator = navigatorKey?.currentState;
if (navigator == null)
return SynchronousFuture<bool>(false);
return navigator.maybePop();
}
}
Анализ исходного кода
Я просто говорил о том, какие API решают какие задачи. Здесь мы будем отслеживать, как реализуется официальная реализация. Я ввожу новый URL в браузере.
-
Из последнего шага на картинке видно, что это нативный метод от движка
-
К методу _handleNavigationInvocation:
pop
а такжеpush
Все это здесь, здесь получаются уведомления от движка, включая ввод браузера, нажатия браузером и физическим нажатием кнопки «Назад» в Android.
Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) {
switch (methodCall.method) {
case 'popRoute':
return handlePopRoute();
case 'pushRoute':
return handlePushRoute(methodCall.arguments as String);
case 'pushRouteInformation':
return _handlePushRouteInformation(methodCall.arguments as Map<dynamic, dynamic>);
}
return Future<dynamic>.value();
}
- В методе зарегистрирован
WidgetsBindingObserver
Отправьте событие и верните его, когда оно будет найдено и обработано. (гнездо заранее закапывается здесьNavigator
яма)
Future<void> handlePushRoute(String route) async {
for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
if (await observer.didPushRoute(route))
return;
}
}
Future<void> handlePopRoute() async {
for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
if (await observer.didPopRoute())
return;
}
SystemNavigator.pop();
}
Future<void> _handlePushRouteInformation(Map<dynamic, dynamic> routeArguments) async {
for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
if (
await observer.didPushRouteInformation(
RouteInformation(
location: routeArguments['location'] as String,
state: routeArguments['state'] as Object,
)
)
)
return;
}
}
- WidgetsApp наследует WidgetsBindingObserver, в
WidgetsBinding.instance.addObserver(this)
добавить себя в_observers
в
class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
@override
void initState() {
WidgetsBinding.instance.addObserver(this);
}
@override
Future<bool> didPushRoute(String route) async {
}
- а также
PlatformRouteInformationProvider
Добавьте себя в addListener при инициализации маршрутизатора_observers
середина.
class _RouterState<T> extends State<Router<T>> {
@override
void initState() {
super.initState();
widget.routeInformationProvider?.addListener(_handleRouteInformationProviderNotification);
class PlatformRouteInformationProvider extends RouteInformationProvider with WidgetsBindingObserver, ChangeNotifier {
@override
void addListener(VoidCallback listener) {
if (!hasListeners)
WidgetsBinding.instance.addObserver(this);
super.addListener(listener);
}
Наконец-то мы можем получить изменения маршрутизации, которые нам сообщает движок!
BackButtonDispatcher
включатьRootBackButtonDispatcher
а такжеChildBackButtonDispatcher
.
главным образом для решенияNavigator 嵌套
, вернемся к проблеме приоритета кнопок.
Возьмите каштан:
1.MyApp
ЯвляетсяNavigator
, начальная страницаNestedMainPage
, после нажатия средней кнопкиpush
прибытьChildRouterPage
. теперь первыйNavigator
Там 2 страницы(NestedMainPage
,ChildRouterPage
)
2.ChildRouterPage
такжеNavigator
, начальная страницаNestedTestPage
, после нажатия средней кнопкиpush
прибытьTestPageA
. теперь первыйNavigator
Там 2 страницы(NestedTestPage
,TestPageA
)
3. Теперь мы видим, чтоTestPageA
, то теперь нажмите安卓物理返回键
или浏览器返回键
Что такое феномен?
4. Страница возвращается кNestedMainPage
, это не должен быть результат, которого все хотят, верно?
Так как же решить эту проблему? Ранее мы знали, что у нас есть способ контролировать安卓物理返回键
или浏览器返回键
да, это_handleNavigationInvocation
в методеpopRoute
обратный вызов, но первый, имеющий приоритет, можетpop
изNavigator
(Помните яму, когда ее раздавали). На самом деле мы можем решить для себяpopRoute
На какой из них действует обратный вызовNavigator
. Нам просто нужно сделать следующее на втором маршрутизаторе.
добраться до предыдущегоRouter
серединаbackButtonDispatcher
, и получить приоритет.
Widget build(BuildContext context) {
final ChildBackButtonDispatcher childBackButtonDispatcher =
Router.of(context)
.backButtonDispatcher
.createChildBackButtonDispatcher();
childBackButtonDispatcher.takePriority();
return Router<RouteSettings>(
backButtonDispatcher: childBackButtonDispatcher,
);
}
Анализ исходного кода:
- очень знакомый
WidgetsBindingObserver
,addCallback
добавляйте себя в монитор когда_observers
, ожидая, пока движок доставит событие,WidgetsBinding
распространяется в.
class RootBackButtonDispatcher extends BackButtonDispatcher with WidgetsBindingObserver {
RootBackButtonDispatcher();
@override
void addCallback(ValueGetter<Future<bool>> callback) {
if (!hasCallbacks)
WidgetsBinding.instance.addObserver(this);
super.addCallback(callback);
}
@override
void removeCallback(ValueGetter<Future<bool>> callback) {
super.removeCallback(callback);
if (!hasCallbacks)
WidgetsBinding.instance.removeObserver(this);
}
@override
Future<bool> didPopRoute() => invokeCallback(Future<bool>.value(false));
}
- вызов
takePriority
Говоря о методе, добавьте его вparent
изchildren
Среди них убедитесь, что вы последний, и опустошите свой собственныйchildren
.
class ChildBackButtonDispatcher extends BackButtonDispatcher {
ChildBackButtonDispatcher(this.parent) : assert(parent != null);
final BackButtonDispatcher parent;
@protected
Future<bool> notifiedByParent(Future<bool> defaultValue) {
return invokeCallback(defaultValue);
}
@override
void takePriority() {
parent.deferTo(this);
super.takePriority();
}
/// BackButtonDispatcher 中的实现,方便讲解
void deferTo(ChildBackButtonDispatcher child) {
assert(hasCallbacks);
_children ??= <ChildBackButtonDispatcher>{} as LinkedHashSet<ChildBackButtonDispatcher>;
_children.remove(child); // child may or may not be in the set already
_children.add(child);
}
/// BackButtonDispatcher 中的实现,方便讲解
void takePriority() {
if (_children != null)
_children.clear();
}
}
- существует
invokeCallback
метод, изchildren
Последний начинает обход (поэтому вdeferTo
метод первыйremove
,назадadd
), посмотрите, кто занимаетсяdidPopRoute
Событие, если оно обрабатывается, останавливается.
Future<bool> invokeCallback(Future<bool> defaultValue) {
if (_children != null && _children.isNotEmpty) {
final List<ChildBackButtonDispatcher> children = _children.toList();
int childIndex = children.length - 1;
Future<bool> notifyNextChild(bool result) {
// If the previous child handles the callback, we returns the result.
if (result)
return SynchronousFuture<bool>(result);
// If the previous child did not handle the callback, we ask the next
// child to handle the it.
if (childIndex > 0) {
childIndex -= 1;
return children[childIndex]
.notifiedByParent(defaultValue)
.then<bool>(notifyNextChild);
}
// If none of the child handles the callback, the parent will then handle it.
return super.invokeCallback(defaultValue);
}
return children[childIndex]
.notifiedByParent(defaultValue)
.then<bool>(notifyNextChild);
}
return super.invokeCallback(defaultValue);
}
-
Потому что
Router
Есть добавленные парыBackButtonDispatcher
монитор (расположение в исходном коде), поэтому в конечном итоге уведомлениеRouterDelegate.popRoute
.
Обзор Навигатора 2.0
- через пару
Navigator.pages
управления, чтобы полностью овладеть стеком маршрутизации. - пройти через
Router
и связанные с нимиApi
решить浏览器输入修改URL
,浏览器返回键
,安卓物理返回键
проблемы с взаимодействием с нативным, иNavigator
Прокси и настройка. - пройти через
BackButtonDispatcher
обработанныйNavigator 嵌套
Эта проблема.
Кажется, что толстая кишка Навигатора 2.0 идеальна, но все же есть некоторые недостатки в реальном использовании.
- Нам предстоит многого достичь, не так ли?
duang
Вы можете использовать его сразу? - Проблемы с разбором входных параметров вручную в веб-браузерах
- из-за
Route
существуетPage
изcreateRoute
генерируется в методе, поэтому мы не можем получить к нему прямой доступRoute
. Если мы хотим написать аналогичныйpush
Как насчет методов с параметрами обратного вызова?
Future<T> push<T extends Object>(Route<T> route) {
_history.add(_RouteEntry(route, initialState: _RouteLifecycle.push));
_flushHistoryUpdates();
_afterNavigation(route);
return route.popped;
}
эм, да,法法路由注解 5.0
Навигатор уже отлично поддерживается1.0
а также2.0
Да, разбросанные цветы!
Правовая аннотация маршрутизации 5.0
добавить цитаты
добавить ссылку наdependencies
, и вам нужно аннотировать проект/пакеты дляpubspec.yaml
середина
dev_dependencies:
ff_annotation_route_core: any
ff_annotation_route_library: any
воплощать в жизньflutter packages get
скачать
добавить аннотацию
пустая конструкция
import 'package:ff_annotation_route/ff_annotation_route.dart';
@FFRoute(
name: "fluttercandies://mainpage",
routeName: "MainPage",
)
class MainPage extends StatelessWidget
{
// ...
}
Построить с параметрами
Инструмент автоматически обрабатывает конструкции с параметрами, и никакой специальной обработки не требуется. Единственная оговорка заключается в том, что вам нужно использоватьargumentImports
Укажите адрес импорта для параметра class/enum. Теперь вместо этого вы можете использовать @FFArgumentImport().
@FFArgumentImport('hide TestMode2')
import 'package:example1/src/model/test_model.dart';
@FFArgumentImport()
import 'package:example1/src/model/test_model1.dart' hide TestMode3;
import 'package:ff_annotation_route_library/ff_annotation_route_library.dart';
@FFRoute(
name: 'flutterCandies://testPageE',
routeName: 'testPageE',
description: 'Show how to push new page with arguments(class)',
// 为了防止 @FFArgumentImport() 不能完全表达的情况, 依然保留 argumentImports。
// argumentImports: <String>[
// 'import \'package:example1/src/model/test_model.dart\';',
// 'import \'package:example1/src/model/test_model1.dart\';',
// ],
exts: <String, dynamic>{
'group': 'Complex',
'order': 1,
},
)
class TestPageE extends StatelessWidget {
const TestPageE({
this.testMode = const TestMode(
id: 2,
isTest: false,
),
this.testMode1,
});
factory TestPageE.deafult() => TestPageE(
testMode: TestMode.deafult(),
);
factory TestPageE.required({@required TestMode testMode}) => TestPageE(
testMode: testMode,
);
final TestMode testMode;
final TestMode1 testMode1;
}
FFRoute
Parameter | Description | Default |
---|---|---|
name | Имя маршрутизации (например, «/ Настройки») | required |
showStatusBar | Показывать ли строку состояния | true |
routeName | Название страницы, используемой для сбора данных | '' |
pageRouteType | Тип трассы (материальная, купертино, прозрачная) | - |
description | описание маршрута | '' |
exts | Дополнительные параметры расширения. | - |
argumentImports | Импорт некоторых параметров Некоторые параметры являются классами или перечислениями, нужно указать адрес их импорта, теперь вместо них можно использовать @FFArgumentImport() | - |
makefile
окружающая обстановка
Добавьте путь к корзине dart в вашу систему$PATH
.
cache\dart-sdk\bin
Если вы не знаете, вы можете увидетьНаггетс
активация
pub global activate ff_annotation_route
Выполнение заказа
Перейдите в корневой каталог вашего проекта и выполните.
ff_route <command> [arguments]
Параметры команды
Доступные команды:
-h, --[no-]help 帮助信息。
-p, --path 执行命令的目录,默认当前目录。
-o, --output route 和 helper 文件的输出目录路径,路径相对于主项目的 lib 文件夹。
-n, --name 路由常量类的名称,默认为 `Routes`。
-g, --git 扫描 git 引用的 package,你需要指定 package 的名字,多个用 `,` 分开
--routes-file-output routes 文件的输出目录路径,路径相对于主项目的lib文件夹
--const-ignore 使用正则表达式忽略一些const(不是全部const都希望生成)
--[no-]route-constants 是否在根项目中的 `xxx_route.dart` 生成全部路由的静态常量
--[no-]package 这个是否是一个 package
--[no-]supper-arguments 是否生成路由参数帮助类
-s, --[no-]save 是否保存命令到本地。如果保存了,下一次就只需要执行 `ff_route` 就可以了。
Навигатор аннотаций 1.0
Полный код находится наexampleсередина
Main.dart
import 'package:ff_annotation_route_library/ff_annotation_route_library.dart';
import 'package:flutter/material.dart';
import 'example_route.dart';
import 'example_routes.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ff_annotation_route demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: Routes.fluttercandiesMainpage,
onGenerateRoute: (RouteSettings settings) {
return onGenerateRoute(
settings: settings,
getRouteSettings: getRouteSettings,
routeSettingsWrapper: (FFRouteSettings ffRouteSettings) {
if (ffRouteSettings.name == Routes.fluttercandiesMainpage ||
ffRouteSettings.name ==
Routes.fluttercandiesDemogrouppage.name) {
return ffRouteSettings;
}
return ffRouteSettings.copyWith(
widget: CommonWidget(
child: ffRouteSettings.widget,
title: ffRouteSettings.routeName,
));
},
);
},
);
}
}
Push
Push name
Navigator.pushNamed(context, Routes.fluttercandiesMainpage /* fluttercandies://mainpage */);
Push name with arguments
- Аргумент должен быть
Map<String, dynamic>
Navigator.pushNamed(
context,
Routes.flutterCandiesTestPageE,
arguments: <String, dynamic>{
constructorName: 'required',
'testMode': const TestMode(
id: 100,
isTest: true,
),
},
);
- включить --supper-аргументы
Navigator.pushNamed(
context,
Routes.flutterCandiesTestPageE.name,
arguments: Routes.flutterCandiesTestPageE.requiredC(
testMode: const TestMode(
id: 100,
isTest: true,
),
),
);
Навигатор аннотаций 2.0
полный код в полный код вexample1середина
Main.dart
import 'dart:convert';
import 'package:example1/src/model/test_model.dart';
import 'package:ff_annotation_route_library/ff_annotation_route_library.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'example1_route.dart';
import 'example1_routes.dart';
void main() {
// 工具将处理简单的类型,但是没法处理全部的
// 比如在浏览器中输入以下地址
// http://localhost:64916/#flutterCandies://testPageF?list=[4,5,6]&map={"ddd":123}&testMode={"id":2,"isTest":true}
// queryParameters 将会根据你自身的情况转换成你对应的类型
FFConvert.convert = <T>(dynamic value) {
if (value == null) {
return null;
}
print(T);
final dynamic output = json.decode(value.toString());
if (<int>[] is T && output is List<dynamic>) {
return output.map<int>((dynamic e) => asT<int>(e)).toList() as T;
} else if (<String, String>{} is T && output is Map<dynamic, dynamic>) {
return output.map<String, String>((dynamic key, dynamic value) =>
MapEntry<String, String>(key.toString(), value.toString())) as T;
} else if (const TestMode() is T && output is Map<dynamic, dynamic>) {
return TestMode.fromJson(output) as T;
}
return json.decode(value.toString()) as T;
};
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final FFRouteInformationParser _ffRouteInformationParser =
FFRouteInformationParser();
final FFRouterDelegate _ffRouterDelegate = FFRouterDelegate(
getRouteSettings: getRouteSettings,
pageWrapper: <T>(FFPage<T> ffPage) {
return ffPage.copyWith(
widget: ffPage.name == Routes.fluttercandiesMainpage ||
ffPage.name == Routes.fluttercandiesDemogrouppage.name
? ffPage.widget
: CommonWidget(
child: ffPage.widget,
routeName: ffPage.routeName,
),
);
},
);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'ff_annotation_route demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
// 初始化第一个页面
routeInformationProvider: PlatformRouteInformationProvider(
initialRouteInformation: const RouteInformation(
location: Routes.fluttercandiesMainpage,
),
),
routeInformationParser: _ffRouteInformationParser,
routerDelegate: _ffRouterDelegate,
);
}
}
FFRouteInformationParser
В основном используется на веб-платформе, когда вы вводите в браузере, конфигурация маршрутизации преобразуется вRouteSettings
, или при возврате в браузер будетRouteSettings
Преобразование в конфигурацию маршрутизации
Например:
xxx?a=1&b=2
<=> RouteSettings(name:'xxx',arguments:<String, dynamic>{'a':'1','b':'2'})
FFRouterDelegate
Делегат для создания и настройки навигации, который обеспечиваетNavigator
аналогичные методы в .
FFRouterDelegate.of(context).pushNamed<void>(
Routes.flutterCandiesTestPageF.name,
arguments: Routes.flutterCandiesTestPageF.d(
<int>[1, 2, 3],
map: <String, String>{'ddd': 'dddd'},
testMode: const TestMode(id: 1, isTest: true),
),
);
ты сможешьtest_page_c.dartНайдите больше примеров на странице
Push
Push name
FFRouterDelegate.of(context).pushNamed<void>(
Routes.flutterCandiesTestPageA,
);
Push name with arguments
- Аргумент должен быть
Map<String, dynamic>
FFRouterDelegate.of(context).pushNamed<void>(
Routes.flutterCandiesTestPageF.name,
arguments: Routes.flutterCandiesTestPageF.d(
<int>[1, 2, 3],
map: <String, String>{'ddd': 'dddd'},
testMode: const TestMode(id: 1, isTest: true),
),
);
- включить --supper-аргументы
FFRouterDelegate.of(context).pushNamed<void>(
Routes.flutterCandiesTestPageF.name,
arguments: <String, dynamic>{
'list': <int>[1, 2, 3],
'map': <String, String>{'ddd': 'dddd'},
'testMode': const TestMode(id: 1, isTest: true),
}
)
Code Hints
Вы можете использовать маршрут «Routes.flutterCandiesTestPageE» следующим образом и увидеть подсказку кода в редакторе. Включая описание страницы, структуру, тип параметра, имя параметра, требуется ли параметр.
- По умолчанию
/// 'This is test page E.'
///
/// [name] : 'flutterCandies://testPageE'
///
/// [routeName] : 'testPageE'
///
/// [description] : 'This is test page E.'
///
/// [constructors] :
///
/// TestPageE : [TestMode testMode, TestMode1 testMode1]
///
/// TestPageE.deafult : []
///
/// TestPageE.required : [TestMode(required) testMode]
///
/// [exts] : {group: Complex, order: 1}
static const String flutterCandiesTestPageE = 'flutterCandies://testPageE';
- включить --supper-аргументы
/// 'This is test page E.'
///
/// [name] : 'flutterCandies://testPageE'
///
/// [routeName] : 'testPageE'
///
/// [description] : 'This is test page E.'
///
/// [constructors] :
///
/// TestPageE : [TestMode testMode, TestMode1 testMode1]
///
/// TestPageE.test : []
///
/// TestPageE.requiredC : [TestMode(required) testMode]
///
/// [exts] : {group: Complex, order: 1}
static const _FlutterCandiesTestPageE flutterCandiesTestPageE =
_FlutterCandiesTestPageE();
class _FlutterCandiesTestPageE {
const _FlutterCandiesTestPageE();
String get name => 'flutterCandies://testPageE';
Map<String, dynamic> d(
{TestMode testMode = const TestMode(id: 2, isTest: false),
TestMode1 testMode1}) =>
<String, dynamic>{
'testMode': testMode,
'testMode1': testMode1,
};
Map<String, dynamic> test() => const <String, dynamic>{
'constructorName': 'test',
};
Map<String, dynamic> requiredC({@required TestMode testMode}) =>
<String, dynamic>{
'testMode': testMode,
'constructorName': 'requiredC',
};
@override
String toString() => name;
}
Эпилог
-
路由我定
, В жизни много перекрестков, и нам нужно сделать выбор. Выбор больше, чем усилия.Иногда вы думаете, что есть только один путь, но на самом деле есть и другие варианты. Иногда думаешь, что есть и другие варианты, но на самом деле остается только один путь. Стоя на перекрестке жизни, я надеюсь, что мы больше не будем колебаться, То ли я сейчас, то ли ты в будущем, я не хочу знать это потом. - Делать
Flutter
Тоже прошло два года.С тех пор, как я начал заниматься один, появилось много мелких партнеров, с которыми можно обсудить,Flutter
Я многому научился и познакомился с большим количеством людей. Я не знаю, как долго программист может писать代码
, но от работы над коммуникационным проектом в легкорельсовом транспорте Чунцина до смены карьеры, чтобы писать код, это действительно потому, что мне это нравится. Я считаю, что человек может заниматься тем, что ему нравится, это должно быть некое счастье. - Спасибо, что дочитали до этого места, и я надеюсь, что эта статья была вам полезна. Love Flutter, люблю конфеты, добро пожаловать, присоединяйтесьFlutter Candies, чтобы вместе производить милые конфеты FlutterГруппа QQ: 181398081. надевать в последнюю очередьFlutter CandiesВся семейная бочка, действительно ароматная.