管理页面的home_Flutter路由管理指北

c3ad04c8714728ef4a9284bb6ac56791.png

秦子帅 明确目标,每天进步一点点.....·
作者 |  艾维码 地址 |  juejin.im/post/5ee5d2c1e51d45788619dcc2

详解

FLutter中的路由,和原生组件化的路由一样,就是页面之间的跳转,也可以称之为导航。app维护一个路由栈,路由入栈(push)操作对应打开一个新页面,路由出栈(pop)操作对应页面关闭操作,而路由管理主要是指如何来管理路由栈。

MaterialPageRoute

MaterialPageRoute是一种模态路由,可以针对不同平台自适应的过渡动画替换整个屏幕页面:

对于Android,打开新页面时,新页面从屏幕底部导入到顶部。关闭页面的时候,会从顶部滑动到底部消失。

在iOS上,页面从右侧滑入并反向退出。

下面我们介绍一下MaterialPageRoute 构造函数的各个参数的意义:

MaterialPageRoute({
    WidgetBuilder builder,
    RouteSettings settings,bool maintainState = true,bool fullscreenDialog = false,
  })

builder 是一个WidgetBuilder类型的回调函数,它的作用是构建路由页面的具体内容,返回值是一个widget。我们通常要实现此回调,返回新路由的实例。settings 包含路由的配置信息,如路由名称、是否初始路由(首页)。maintainState:默认情况下,当入栈一个新路由时,原来的路由仍然会被保存在内存中,如果想在路由没用的时候释放其所占用的所有资源,可以设置maintainState为false。fullscreenDialog表示新的路由页面是否是一个全屏的模态对话框,在iOS中,如果fullscreenDialog为true,新页面将会从屏幕底部滑入(而不是水平方向)

基本使用

Flutter为我们提供了导航器Navigator。参数传入当前的BuildContext和要导航的页面即可。

  1. 调用Navigator.push导航到第二个页面
Navigator.push(
           context, new MaterialPageRoute(builder: (context) => Page2()));
  2、调用Navigator.pop返回前一个页面
Navigator.pop(context, result);
3、关闭页面后获取结果    有时候我们需要上个页面关闭时传递一个返回值,幸运的是,Navigator的调用方法都是Future,因此我们可以等待它们的结果: 3.1. 等待Navigator运行 3.2. 将返回值传递给Navigator.pop函数 3.3. 等待完成后,获取返回值 在page1中,导航到page2,并且await到page2传递返回值并pop,根据返回值弹出不同的对话框:
onPressed: () async {
      var navigationResult = await Navigator.push(
          context, new MaterialPageRoute(builder: (context) => Page2()));if (navigationResult == 'from_back') {
        showDialog(
            context: context,
            builder: (context) => AlertDialog(
                  title: Text('Navigation from back'),
                ));
      } else if (navigationResult == 'from_button') {
        showDialog(
            context: context,
            builder: (context) => AlertDialog(
                  title: Text('Navigation from button'),
                ));
      }
    },
在page2中传递返回值并返回:
Navigator.pop(context, 'from_button');
4、拦截返回键 如果不想点击返回键关闭当前页面,可以使用WillPopScope小部件,用它放在最外层包括住脚手架。并向onWillPop返回false。false告诉系统当前页面不处理返回。
@override
       Widget build(BuildContext context) {return WillPopScope(onWillPop: () => Future.value(false),child: Scaffold(body: Container(child: Center(child: Text('Page 2',style: TextStyle(fontSize: 30.0, fontWeight: FontWeight.bold)),
               ),
             ),
           ),
         );
       }
如果想自定义处理返回键,可以在return false 之前自己处理,比如关闭 当前页面并传递返回值:
WillPopScope(
             onWillPop: () async {// You can await in the calling widget for my_value and handle when complete.
                 Navigator.pop(context, 'my_value');return false;
               },
               ...
       );

命名路由

基本使用
上面代码是在没个需要导航的地方声明路由,不能复用,我们可以先给路由起一个名字,再注册路由表,然后就可以通过路由名字直接打开新的路由了,这为路由管理带来了一种直观、简单的方式,并且可以复用。 MaterialApp的routes属性,既是注册路由表用的,它对应一个Map。 起名:
static const String page1 = "/page1";static const String page2 = "/page2";
声明路由表:
Map routes = {
    page1: (context) => Page1(),
    page2: (context) => Page2(),
  };
注册路由表:
@override
  Widget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',routes: routes,theme: ThemeData(primarySwatch: Colors.blue,visualDensity: VisualDensity.adaptivePlatformDensity,
      ),home: Page1(),
    );
  }
然后在需要路由的地方使用命名路由调用:
Navigator.pushNamed(context, page2)
传递参数
给page3起名:
page3: (context) => Page3(),
page3中接收参数:
class Page3 extends StatelessWidget {@overrideWidget build(BuildContext context) {//获取路由参数
    var args = ModalRoute.of(context).settings.arguments;return Scaffold(
      body: Container(
        child: Center(
          child: Text('Page 3的参数是$args',
              style: TextStyle(fontSize: 30.0, fontWeight: FontWeight.bold)),
        ),
      ),
    );
  }
}
构造函数传参
上面我们明明给page3传递了参数,但是并非传递到构造函数上。我们看构造函数,并不知道传递了什么参数,必须去看路由,并不是很好的做法。那怎么给构造函数传参呢? 起名:
const String

注册路由:
page4: (context) => Page4(text: ModalRoute.of(context).settings.arguments),
打开路由时传递参数:
Navigator.of(context).pushNamed(page4, arguments: "hello");

动态路由

MaterialApp还为我们提供了一个onGenerateRoute参数,未在路由表里注册的路由,会在这里寻找。RouteFactory有一个RouteSettings参数,并返回一个Route。这是我们将用来执行所有路由的功能。
RouteFunction(RouteSettings settings)
我们可以这样使用:先声明路由表:
Route generateRoute(RouteSettings settings) {switch (settings.name) {
    case page5:return MaterialPageRoute(builder: (context) => Page5());
    case page6:return MaterialPageRoute(builder: (context) => Page6());
    default:return MaterialPageRoute(builder: (context) => Page1());
  }
注册:
class MyApp extends StatelessWidget {@override
  Widget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',routes: routes,theme: ThemeData(primarySwatch: Colors.blue,visualDensity: VisualDensity.adaptivePlatformDensity,
      ),onGenerateRoute: generateRoute,home: Page1(),
    );
  }

打开路由:
Navigator.of(context).pushNamed(page5);
动态路由传递参数
上面说了,settings可以拿到参数,我们当然就可以传递参数了:
Route generateRoute(RouteSettings settings) {print('====${settings.name}');switch (settings.name) {
    case page5:return MaterialPageRoute(builder: (context) => Page5());
    case page6:return MaterialPageRoute(builder: (context) => Page6(text: settings.arguments,));
    default:return MaterialPageRoute(builder: (context) => Page1());
  }
}
使用:
Navigator.of(context).pushNamed(page6, arguments: "world");

处理未定义的路线

有两种处理未定义路由的方法。
  1. 利用generateRoute,找不到路由名的返回默认路由
Route generateRoute(RouteSettings settings) {print('====${settings.name}');switch (settings.name) {
    case page5:return MaterialPageRoute(builder: (context) => Page5());
    case page6:return MaterialPageRoute(builder: (context) => Page6(text: settings.arguments,));
    default:return MaterialPageRoute(builder: (context) => NotFindPage());
  }
}
2、利用onUnknownRoute返回默认路由
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {return MaterialApp(
        title: 'Flutter Demo',
        routes: routes,
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        onGenerateRoute: generateRoute,
        onUnknownRoute: (settings) =>
            MaterialPageRoute(builder: (context) => NotFindPage()));
  }

初始路由

打开应用第一屏的路由,也有2种方式,
  1. 可以设置initialRoute,指定路由表里注册的路由名。
  2. 可以设置home,对应的page。initialRoute会覆盖home。
Widget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',routes: routes,theme: ThemeData(primarySwatch: Colors.blue,visualDensity: VisualDensity.adaptivePlatformDensity,
        ),initialRoute: root,home: Page2(),onGenerateRoute: generateRoute,onUnknownRoute: (settings) =>
            MaterialPageRoute(builder: (context) => NotFindPage()));
  }

不使用BuildContext的路由导航

很多情况是,我们已将UI代码从业务逻辑中分离出来(类似于MVVM架构)。viewModel应处理所有逻辑,视图应仅调用模型上的函数,然后在需要时使用新状态重建自身。 我们知道Navigator需要BuildContext的参数,我们在进行实际业务逻辑决策的位置进行导航,而不是在widget里调用路由,如果在viewModel里导航,就要传入context吗?下面实现不要context的导航。 为了遵守MVVM原则,我们将把Navigation功能移动到可以从viewModel调用的服务中。在lib下创建一个名为services的新文件夹,并在其中创建一个名为navigation_service.dart的新文件。 先实现单利模式:
class NavigationService {
  factory NavigationService.getInstance() => _getInstance();
  NavigationService._internal();static NavigationService _instance;static NavigationService _getInstance() {if (_instance == null) {
      _instance = new NavigationService._internal();
    }return _instance;
  }
  }
然后利用navigatorKey实现:
final GlobalKey navigatorKey =new GlobalKey();Future<dynamic> navigateTo(String routeName) {return navigatorKey.currentState.pushNamed(routeName);
  }void goBack() {return navigatorKey.currentState.pop();
  }
我们将NavigationService与应用程序链接的方式,通过navigatorKey提供给MaterialApp。转到main.dart文件并设置navigatorKey:
MaterialApp(title: 'Flutter Demo',navigatorKey: NavigationService().navigatorKey,
        ...
        )
然后写一个viewModel,尝试导航:
class ViewModel {
  final NavigationService _navigationService = NavigationService();Future goPage1() async{/// 模拟请求数据后调到首页await Future.delayed(Duration(seconds: 1));
    _navigationService.navigateTo(page1);
  }
}
在page6里使用viewModel导航:
onPressed: () {
          viewModel.goPage1();
        },
现在,将View文件的职责带回到了“显示UI”并将用户操作传递给模型,而不是“显示UI”将用户操作传递给模型并进行导航。更符合MVVM职责的划分。 这样做的好处是,随着导航逻辑的扩展,我们的UI将保持不变,并且模型将承载所有逻辑/状态管理。 ---END--- d745340617ebdc26aeb2c32c83d51dc5.png 转发至朋友圈,是绝对的真爱

让我知道你在看

1eb68c1f81f718a7c68d47444f4e32ca.gif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值