当前位置: 首页 > news >正文

阿里云网站空间做商城流程南沙区做网站公司

阿里云网站空间做商城流程,南沙区做网站公司,网站加载速度优化,兰州网络营销策划公司排名01 背景与动机 在Navigator 2.0推出之前#xff0c;Flutter主要通过Navigator 1.0和其提供的 API#xff08;如push(), pop(), pushNamed()等#xff09;来管理页面路由。然而#xff0c;Navigator 1.0存在一些局限性#xff0c;如难以实现复杂的页面操作#xff08;如移… 01 背景与动机 在Navigator 2.0推出之前Flutter主要通过Navigator 1.0和其提供的 API如push(), pop(), pushNamed()等来管理页面路由。然而Navigator 1.0存在一些局限性如难以实现复杂的页面操作如移除栈内中间页面、交换页面等、不支持嵌套路由以及无法满足全平台尤其是Web平台的新需求。因此Flutter官方团队决定对路由系统进行改造推出了Navigator 2.0。  02 主要特性 声明式API Navigator 2.0提供的声明式API使得路由管理更加直观和易于理解。开发者只需声明页面的配置信息而无需编写复杂的导航逻辑代码。这种方式不仅减少了代码量还提高了代码的可读性和可维护性。嵌套路由 Navigator 2.0满足了嵌套路由的需求场景允许开发者在应用中创建嵌套的路由结构。这使得应用的结构更加清晰同时也提高了页面导航的灵活性。全平台支持 Navigator 2.0提供的API能够满足不同平台如iOS、Android、Web等的导航需求使得开发者能够更加方便地构建跨平台的应用。强大的页面操作能力 Navigator 2.0提供了更加丰富的页面操作能力如移除栈内中间页面、交换页面等。这些操作在Navigator 1.0中很难实现或需要编写复杂的代码而在Navigator 2.0中则变得简单直接。 03 核心组件 Router 在Navigator 2.0中Router组件是路由管理的核心。它负责根据当前的路由信息RouteInformation和路由信息解析器RouteInformationParser来构建和更新UI。Router组件接收三个主要参数1.routeInformationProvider提供当前的路由信息2.routeInformationParser将路由信息解析为路由配置3.routerDelegate根据路由配置构建和更新UI。RouteInformationProvider RouteInformationProvider是一个提供当前路由信息的组件。它通常与平台相关的路由信息源如浏览器的URL、Android的Intent等集成以获取当前的路由信息。RouteInformationParser RouteInformationParser负责将RouteInformation解析为RouteConfiguration。这个过程允许开发者根据路由信息的格式如URL来定义如何将其映射到应用内的路由配置。RouterDelegate RouterDelegate是与UI构建紧密相关的组件。它必须实现RouterDelegate接口并提供两个主要方法 1.build(BuildContext context)根据当前的路由配置构建UI2.setNewRoutePath(List configuration)设置新的路由路径并更新UI3.Future popRoute() 实现后退逻辑。 04 简单实例 首先通过MaterialApp.router()来创建MaterialApp: class MyApp extends StatelessWidget {  override  Widget build(BuildContext context) {  final routerDelegate  MyRouterDelegate();  final routeInformationParser  MyRouteInformationParser();  return MaterialApp.router(  title: Flutter Navigator 2.0 Demo,  theme: ThemeData(  primarySwatch: Colors.blue,  ),  routerDelegate: routerDelegate,  routeInformationParser: routeInformationParser,  );  }   } 需要定义一个RouterDelegate对象和一个RouteInformationParser对象。其中根据路由配置构建和更新UIRouteInformationParser负责将RouteInformation解析为RouteConfiguration。 RouterDelegate可以传个泛型定义其currentConfiguration对象的类型。 class MyRouterDelegate extends RouterDelegateString  with PopNavigatorRouterDelegateMixinString, ChangeNotifier {  final GlobalKeyNavigatorState navigatorKey  GlobalKeyNavigatorState();  private ListString _pages  [/home];  override  Widget build(BuildContext context) {  return Navigator(  key: navigatorKey,  pages: _pages.map((route)  MaterialPage(  key: Key(route),  child: generatePage(route),  )).toList(),  onPopPage: (route, result) {  if (!route.didPop(result)) {  return false;  }  _pages.removeLast();  notifyListeners();  return true;  },  );  }  override  Futurevoid setNewRoutePath(String path) async {  if (!_pages.contains(path)) {  _pages.add(path);  notifyListeners();  }  }  Widget generatePage(String route) {  switch (route) {  case /home:  return HomePage();  case /details:  // 这里可以传递参数例如 DetailsPage(arguments: someData)  return DetailsPage();  default:  return NotFoundPage();  }  }  override  String get currentConfiguration  _pages.last;   } 其中build()一般返回的是一个Navigator对象popRoute()实现后退逻辑setNewRoutePath()实现新页面的逻辑。定义了一个_pages数组对象记录每个路由的path可以理解为是一个路由栈这个路由栈对我们来说非常友好在有复杂的业务逻辑时我们可以自行定义相应的栈管理逻辑。currentConfiguration返回的是栈顶的page信息。创建一个类继承RouteInformationParser主要的作用是包装解析路由信息这里有一个最简单的方式如下 class MyRouteInformationParser extends RouteInformationParserString {  override  FutureString parseRouteInformation(RouteInformation routeInformation) {  final uri  Uri.parse(routeInformation.location);  return SynchronousFuture(uri.path);  }  override  RouteInformation restoreRouteInformation(String configuration) {  return RouteInformation(location: configuration);  }   } 好的接下来我们看一下调用 class HomePage extends StatelessWidget {  override  Widget build(BuildContext context) {  return Scaffold(  appBar: AppBar(title: Text(Home)),  body: Center(  child: ElevatedButton(  onPressed: () {  Router.of(context).routerDelegate.setNewRoutePath(/details);},  child: Text(Go to Details),  ),  ),  );  }   }  class DetailsPage extends StatelessWidget {  override  Widget build(BuildContext context) {  return Scaffold(  appBar: AppBar(title: Text(Details)),  body: Center(  child: Text(This is Details Page),  ),  );  }   } class NotFoundPage extends StatelessWidget {  override  Widget build(BuildContext context) {  return Scaffold(  appBar: AppBar(title: Text(Not Found)),  body: Center(  child: Text(Page not found),  ),  );  }   } 非常简单直接调用Router.of(context).routerDelegate.setNewRoutePath()即可。 到此为止一个使用Navigator2.0的最简单的路由实例就完成了。和Navigator1.0相比看上去繁杂了不少。但是可以根据业务需求自定义路由栈进行管理大大的提升了灵活性。接来看我们看一下Navigator2.0是如何对路由进行实现的。 05 源码简析 我们在使用Navigator2.0时是通过MaterialApp.router()创建的MaterialApp对象之前章节提到过传了RouteInformationParser和RouterDelegate这两个对象。当传递了RouterDelegate对象时_MaterialAppState中的_usesRouter会被设置为true。 bool get _usesRouter  widget.routerDelegate ! null || widget.routerConfig ! null; 在build()时通过WidgetsApp.router()方法创建了一个WidgetsApp对象 if (_usesRouter) {return WidgetsApp.router(key: GlobalObjectKey(this),routeInformationProvider: widget.routeInformationProvider,routeInformationParser: widget.routeInformationParser,routerDelegate: widget.routerDelegate,routerConfig: widget.routerConfig,backButtonDispatcher: widget.backButtonDispatcher,builder: _materialBuilder,title: widget.title,onGenerateTitle: widget.onGenerateTitle,textStyle: _errorTextStyle,color: materialColor,locale: widget.locale,localizationsDelegates: _localizationsDelegates,localeResolutionCallback: widget.localeResolutionCallback,localeListResolutionCallback: widget.localeListResolutionCallback,supportedLocales: widget.supportedLocales,showPerformanceOverlay: widget.showPerformanceOverlay,checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages,checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers,showSemanticsDebugger: widget.showSemanticsDebugger,debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner,inspectorSelectButtonBuilder: _inspectorSelectButtonBuilder,shortcuts: widget.shortcuts,actions: widget.actions,restorationScopeId: widget.restorationScopeId,);} 在_WidgetsAppState中根据routerDelegate设置了成员变量_usesRouterWithDelegates的值 bool get _usesRouterWithDelegates  widget.routerDelegate ! null; 在build()时会创建一个Router对象其中Router继承了StatefulWidget overrideWidget build(BuildContext context) {Widget? routing;if (_usesRouterWithDelegates) {routing  RouterObject(restorationScopeId: router,routeInformationProvider: _effectiveRouteInformationProvider,routeInformationParser: widget.routeInformationParser,routerDelegate: widget.routerDelegate!,backButtonDispatcher: _effectiveBackButtonDispatcher,);}  ......} 在上一章节的实例中我们可得知页面的切换都是依靠RouterDelegate对象进行的。每当切换到新的页面时都会调用setNewRoutePath()方法因此我们来看一下setNewRoutePath()是什么时候被调用的有两处。第一处 void _handleRouteInformationProviderNotification() {_routeParsePending  true;_processRouteInformation(widget.routeInformationProvider!.value, ()  widget.routerDelegate.setNewRoutePath);} _RouteSetterT _processParsedRouteInformation(Object? transaction, ValueGetter_RouteSetterT delegateRouteSetter) {return (T data) async {if (_currentRouterTransaction ! transaction) {return;}await delegateRouteSetter()(data);if (_currentRouterTransaction  transaction) {_rebuild();}};} 我们看看_handleRouteInformationProviderNotification的调用时机 overridevoid initState() {super.initState();widget.routeInformationProvider?.addListener(_handleRouteInformationProviderNotification);widget.backButtonDispatcher?.addCallback(_handleBackButtonDispatcherNotification);widget.routerDelegate.addListener(_handleRouterDelegateNotification);} 我们可以看到在initState()时也就是在Router被初始化的时候由widget.routeInformationProvider来监听一些状态实现新页面的切换。我们来看一下routeInformationProvider。RouteInformationProvider在我们自己没有创建的情况下系统会默认为我们创建一个PlatformRouteInformationProvider对象。它实际上是个ChangeNotifier。系统会监听每一帧的信号发送调用其父类routerReportsNewRouteInformation()方法我们看看它的实现 overridevoid routerReportsNewRouteInformation(RouteInformation routeInformation, {RouteInformationReportingType type  RouteInformationReportingType.none}) {final bool replace type  RouteInformationReportingType.neglect ||(type  RouteInformationReportingType.none _equals(_valueInEngine.uri, routeInformation.uri));SystemNavigator.selectMultiEntryHistory();SystemNavigator.routeInformationUpdated(uri: routeInformation.uri,state: routeInformation.state,replace: replace,);_value  routeInformation;_valueInEngine  routeInformation;} 其中SystemNavigator.selectMultiEntryHistory()的实现如下 /// Selects the multiple-entry history mode.////// On web, this switches the browser history model to one that tracks all/// updates to [routeInformationUpdated] to form a history stack. This is the/// default.////// Currently, this is ignored on other platforms.////// See also://////  * [selectSingleEntryHistory], which forces the history to only have one///    entry.static Futurevoid selectMultiEntryHistory() {return SystemChannels.navigation.invokeMethodvoid(selectMultiEntryHistory);} 这个方法是由各个平台自行实现的。从注释中我们可得知如果是在Web平台下它会切换成history模式并从history stack中追踪所有的变化。在history发生变化时会发送信号给Flutter层等待处理。SystemNavigator.routeInformationUpdated()方法是用来更新路由的我们先不做分析。接着我们回到PlatformRouteInformationProvider看看它什么时候会执行notifyListeners()方法 overrideFuturebool didPushRouteInformation(RouteInformation routeInformation) async {assert(hasListeners);_platformReportsNewRouteInformation(routeInformation);return true;} void _platformReportsNewRouteInformation(RouteInformation routeInformation) {if (_value  routeInformation) {return;}_value  routeInformation;_valueInEngine  routeInformation;notifyListeners();} 在监听到有push路由的情况下时会调用notifyListeners()从而实现页面的切换。我们再来看第二处调用setNewRoutePath()的地方 overridevoid didChangeDependencies() {_routeParsePending  true;super.didChangeDependencies();// The super.didChangeDependencies may have parsed the route information.// This can happen if the didChangeDependencies is triggered by state// restoration or first build.if (widget.routeInformationProvider ! null  _routeParsePending) {_processRouteInformation(widget.routeInformationProvider!.value, ()  widget.routerDelegate.setNewRoutePath);}_routeParsePending  false;_maybeNeedToReportRouteInformation();} void _processRouteInformation(RouteInformation information, ValueGetter_RouteSetterT delegateRouteSetter) {assert(_routeParsePending);_routeParsePending  false;_currentRouterTransaction  Object();widget.routeInformationParser!.parseRouteInformationWithDependencies(information, context).thenvoid(_processParsedRouteInformation(_currentRouterTransaction, delegateRouteSetter));} parseRouteInformationWithDependencies()方法中调用的parseRouteInformation()其实就是我们自定义RouteInformationParser来进行的实现。 FutureT parseRouteInformationWithDependencies(RouteInformation routeInformation, BuildContext context) {return parseRouteInformation(routeInformation);} 看到当其与父的依赖关系被改变的时候会调用setNewRoutePath()。大概率就是App初始化的时候被调用一次。 06 根据狐友业务的Web端实践  我们的Flutter团队会承担一些运营活动的H5需求。在实现时我们对路由有如下需求 1.可以根据业务自由的管理路由栈 2.分享链接只能分享出去默认入口链接不希望中间的路由链接被分享出去 3.不管有多少个路由页面history始终不变在响应浏览器返回键时不响应路由栈的pop操作。 在之前使用Navigator1.0时体验并不太好一个是不够灵活另外还需对分享出去的链接做处理。因此我们利用Navigator2.0设计了一套新的路由 MyRouterDelegate delegate  MyRouterDelegate();overrideWidget build(BuildContext context) {return MaterialApp.router(debugShowCheckedModeBanner: false,routeInformationParser: MyRouteParser(),routerDelegate: delegate,);} Parser实现非常简单 class MyRouteParser extends RouteInformationParserRouteSettings {override///parseRouteInformation() 方法的作用就是接受系统传递给我们的路由信息 routeInformationFutureRouteSettings parseRouteInformation(RouteInformation routeInformation) {// Uri uri  Uri.parse(routeInformation.location??/);return SynchronousFuture(RouteSettings(name: routeInformation.location));}override///恢复路由信息RouteInformation restoreRouteInformation(RouteSettings configuration) {return RouteInformation(location: configuration.name);} } Delegate的实现如下 import package:ai_chatchallenge/router/exit_util.dart; import package:ai_chatchallenge/router/navigator_util.dart; import package:ai_chatchallenge/router/my_router_arg.dart; import package:flutter/material.dart;import route_page_config.dart;class MyRouterDelegate extends RouterDelegateRouteSettingswith PopNavigatorRouterDelegateMixinRouteSettings, ChangeNotifier {///页面栈ListPage _stack  [];//当前的界面信息RouteSettings _setting  RouteSettings(name: RouterName.rootPage,arguments: BaseArgument()..name  RouterName.rootPage);//重写navigatorKeyoverrideGlobalKeyNavigatorState navigatorKey;MyRouterDelegate() : navigatorKey  GlobalKeyNavigatorState() {//初始化两个方法 一个是push页面 另一个是替换页面NavigatorUtil().registerRouteJump(RouteJumpFunction(onJumpTo: (RouteSettings setting) {// _setting  setting;// changePage();addPage(name: setting.name, arguments: setting.arguments);}, onReplaceAndJumpTo: (RouteSettings setting) {if (_stack.isNotEmpty) {_stack.removeLast();}_setting  setting;changePage();}, onClearStack: () {_stack.clear();_setting  RouteSettings(name: RouterName.rootPage,arguments: BaseArgument()..name  RouterName.rootPage);changePage();}, onBack: () {if (_stack.isNotEmpty) {_stack.removeLast();if (_stack.isNotEmpty) {_setting  _stack.last;} else {_setting  RouteSettings(name: RouterName.rootPage,arguments: BaseArgument()..name  RouterName.rootPage);}changePage();}}));}overrideRouteSettings? get currentConfiguration {return _stack.last;}overrideFuturebool popRoute() {if (_stack.length  1) {_stack.removeLast();_setting  _stack.last;changePage();//非最后一个页面return Future.value(true);}//最后一个页面确认退出操作return _confirmExit();}Futurebool _confirmExit() async {bool result  ExitUtil.doubleCheckExit(navigatorKey.currentContext!);// bool result  await ExitUtil.backToDesktop();return !result;}void addPage({required name, arguments}) {_setting  RouteSettings(name: name, arguments: arguments);changePage();}overrideWidget build(BuildContext context) {return WillPopScope(//解决物理返回建无效的问题onWillPop: () async  !await navigatorKey.currentState!.maybePop(),child: Navigator(key: navigatorKey,pages: _stack,onPopPage: _onPopPage,),);}/// 按下返回的回调bool _onPopPage(Routedynamic route, dynamic result) {debugPrint(这里的试试);if (!route.didPop(result)) {return false;}return true;}changePage() {int index  getCurrentIndex(_stack, _setting!);ListPage tempPages  _stack;if (index ! -1) {// 要求栈中只允许有一个同样的页面的实例 否则开发模式热更新会报错// 要打开的页面在栈中已存在则将该页面和它上面的所有页面进行出栈tempPages  tempPages.sublist(0, index);// 或者删除之前存在栈里的页面,重新创建// tempPages.removeAt(index);}Page page;if (_setting?.arguments is BaseArgument) {if ((_setting?.arguments as BaseArgument).name  RouterName.rootPage) {_stack.clear();}} else {if (_setting?.name  RouterName.rootPage) {_stack.clear();}}page  buildPage(name: _setting?.name, arguments: _setting?.arguments);tempPages  [...tempPages, page];NavigatorUtil().notify(tempPages, _stack);_stack  tempPages;notifyListeners();}overrideFuturevoid setInitialRoutePath(RouteSettings configuration) {return super.setInitialRoutePath(_setting);}overrideFuturevoid setNewRoutePath(RouteSettings configuration) async {if (configuration.arguments is BaseArgument) {if ((configuration.arguments as BaseArgument).name RouterName.rootPage) {_stack.clear();}} else {if (configuration.name  RouterName.rootPage) {_stack.clear();}}addPage(name: configuration.name, arguments: configuration.arguments);} } 其中_stack是我们的路由栈_setting是RouteSettings每执行一个新的路由跳转都会创建一个RouteSettings对象并赋值给_setting最终在插入_stack里。buildPage()的实现如下 //建造页面 buildPage({required name, arguments}) {return MaterialPage(child: getPageChild(name: name, arguments: arguments),arguments: arguments,name: name,key: ValueKey(arguments is BaseArgument ? (arguments as BaseArgument).name : name)); } 其中MaterialPage继承了Page。getPageChild()实现如下 Widget getPageChild({required name, arguments}) {Widget page;Map? arg;if (arguments is Map) {arg  arguments;}if (arguments is BaseArgument) {switch ((arguments as BaseArgument).name) {case RouterName.rootPage:page  TestHomePage();break;case RouterName.testChild1Page:page  TestChildPage1(argument: arguments.arguments as TestChild1PageArgument,);break;case RouterName.testChild2Page:page  TestChildPage2();break;default:page  TestHomePage();}} else {page  TestHomePage();}return page; }class RouterName {static const rootPage  /;static const testChild1Page  /testChild1Page;static const testChild2Page  /testChild2Page; } 我们可以看到在真正返回Widget时我们并没有使用传入的name参数而是BaseArgument的name参数这是为什么呢这是在于我们为了实现无论页面怎么跳转从头到尾浏览器只保留一个history因此我们在页面跳转时RouteSettings的name并不发生变化通过其arguments里面的参数变化返回不同的Widget。这样在路由跳转时其实MaterialPage由于name一直会被直接复用从而不会创建新的MaterialPage也就不会产生history。 NavigatorUtil是由业务调用的创建跳转方法的抽象类提供了onJumpTo()onReplaceAndJumpTo()onClearStack()onBack()四个方法供业务调用我们可以看一下onJumpTo()的实现 overridevoid onJumpTo({required name,Object? stackArguments,MapString, dynamic? historyArgMap,BuildContext? context}) {var arg  BaseArgument();arg.name  name;arg.arguments  stackArguments;RouteSettings settings RouteSettings(name: RouterName.rootPage, arguments: arg);return _function!.onJumpTo!(settings);} 可以看到在创建RouteSettings对象时name为RouterName.rootPagearg时由业务传的真正的跳转页面相关的参数。我们看一下业务的调用 overrideWidget build(BuildContext context) {return Scaffold(body: Container(child: Column(children: [Text(TestHomePage),Text(history length is :   window.history.length.toString()),Text(href:   WebUtil.get().getWindow().location.href),TextButton(onPressed: () {var arg  TestChild1PageArgument()..isSuccess  false;NavigatorUtil().onJumpTo(name: RouterName.testChild1Page,stackArguments: arg,historyArgMap: arg.toJson(),context: context);},child: Text(Go to TestChildPage1))],),),);} overrideWidget build(BuildContext context) {return Scaffold(body: Container(child: Column(children: [Text(TestChildPage1),Text(history length is :   window.history.length.toString()),Text(href:   WebUtil.get().getWindow().location.href),TextButton(onPressed: () {NavigatorUtil().onJumpTo(name: RouterName.testChild2Page, context: context);},child: Text(Go to TestChildPage2)),TextButton(onPressed: () {NavigatorUtil().onBack();},child: Text(Back to TestHomePage)),],),),);} overrideWidget build(BuildContext context) {return Scaffold(body: Container(child: Column(children: [Text(TestChildPage2),Text(history length is :   window.history.length.toString()),Text(href:   WebUtil.get().getWindow().location.href),TextButton(onPressed: () {NavigatorUtil().onBack();},child: Text(Back to TestChild1page)),TextButton(onPressed: () {NavigatorUtil().onClearStack();},child: Text(Back to Root)),],),),);} 我们看一下截图展示 在这个过程中href不会发生变化history也不会发生变化完全符合我们的预期。 07 总结 Flutter的Navigator 2.0引入了声明式的API使页面路由管理更加灵活和强大。相较于Navigator 1.0Navigator 2.0支持更复杂的路由操作如嵌套路由和动态路由配置。它使用不可变的Page对象列表来表示路由历史与Flutter的不可变Widgets设计理念一致。Navigator 2.0还支持命名路由通过简单的路由名称即可实现页面跳转大大简化了路由管理的复杂度。此外它还提供了更丰富的路由回调和状态管理功能使开发者能够更轻松地构建复杂的Flutter应用。
http://www.w-s-a.com/news/158235/

相关文章:

  • 网页设计与网站建设书籍包头住房与城乡建设局网站
  • 重庆网站建设平台免费猎头公司收费收费标准和方式
  • 形象设计公司网站建设方案书打开一个不良网站提示创建成功
  • 网站手机页面如何做网站关键字 优帮云
  • 免费的黄冈网站有哪些下载软件系统软件主要包括网页制作软件
  • 企业微站系统重庆高端网站建设价格
  • 有没有做衣服的网站吗网站自适应开发
  • 青海省制作网站专业专业定制网吧桌椅
  • 网站开发的项目17岁高清免费观看完整版
  • 手机网站建设多少钱一个门网站源码
  • 重庆 网站开发天津住房和城乡建设厅官方网站
  • 泰安高级网站建设推广厦门高端网站建设定制
  • jsp网站开发引用文献手机seo排名
  • 创建一家网站如何创设计网页的快捷网站
  • 1688代加工官方网站h5开发教程
  • 静态网站源码下载网站怎么显示备案号
  • 网站代码设计网站开发维护任职要求
  • 长寿做网站的电话怎么快速刷排名
  • 上海市中学生典型事例网站邯郸全网推广
  • 厦门网站建设680元好男人的最好的影院
  • 石家庄网站建设设计产品设计专业就业前景
  • 网站移动排名做最好最全的命理网站
  • 网站怎么防黑客杭州市做外贸网站的公司
  • 网站推广公司认准乐云seo易语言做网站登录
  • 配色设计网站推荐网站下拉菜单重叠
  • 内容展示型网站特点在北京注册公司需要多少钱
  • h5网站源代码创意设计理念
  • 岳阳网站开发服务推广运营平台
  • 网站开发得多长时间湖南建设人力资源网证书查询
  • 论坛网站开发网络营销是什么时候产生的