Flutter笔记:路由观察者

举报
jcLee95 发表于 2023/11/30 10:46:12 2023/11/30
【摘要】 Flutter 路由观察者相关话题
Flutter系列
路由观察者




路由观察者(Route Observer)是一个监听路由(页面)变化的工具。在Flutter应用中,我们经常需要监听路由的变化,例如当用户从一个页面跳转到另一个页面时,我们可能需要执行一些操作,如数据统计、页面切换动画等,这时就需要用到路由观察者。

路由观察者的定义:路由观察者是一个抽象类,它定义了一些方法,这些方法可以在路由发生变化时被调用。我们可以通过继承路由观察者类并实现这些方法,来创建自己的路由观察者。

路由观察者的作用:路由观察者的主要作用是监听路由的变化,当路由发生变化时,路由观察者的相应方法会被调用,我们可以在这些方法中执行需要的操作。

路由观察者的使用场景:路由观察者可以用于各种需要监听路由变化的场景,例如数据统计、页面切换动画、用户行为跟踪等。

总的来说,路由观察者是Flutter中一个非常重要的工具,它可以帮助我们更好地理解和控制路由的变化。



在Flutter中,路由观察者是通过继承NavigatorObserver类来创建的。NavigatorObserver是一个抽象类,它定义了一些方法,这些方法会在路由发生变化时被调用。例如,didPush方法会在新的路由被推送到导航器时被调用,didPop方法会在路由从导航器中弹出时被调用。


要创建一个路由观察者,我们需要创建一个NavigatorObserver 的子类,并实现它的方法。下面是一个简单的例子:

class MyRouteObserver extends NavigatorObserver {
  @override
  void didPush(Route route, Route previousRoute) {
    super.didPush(route, previousRoute);
    print('didPush ${route.settings.name}');
  }

  @override
  void didPop(Route route, Route previousRoute) {
    super.didPop(route, previousRoute);
    print('didPop ${route.settings.name}');
  }
}

在这个例子中,我们创建了一个名为 MyRouteObserver 的路由观察者,它会在路由被推送和弹出时打印路由的名称。


创建了路由观察者后,我们需要将它添加到 MaterialApp  WidgetsApp 中,这样它才能监听到路由的变化。这可以通过设置navigatorObservers 属性来实现:

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final MyRouteObserver _myRouteObserver = MyRouteObserver();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      navigatorObservers: [_myRouteObserver],
      home: MyHomePage(),
    );
  }
}

在这个例子中,我们创建了一个 MyRouteObserver 的实例,并将它添加到了 MaterialApp 的navigatorObservers中。这样,MyRouteObserver 就能监听到所有路由的变化了。


路由观察者的方法主要包括 didPushdidPopdidRemove  didReplace 等。下面我们来详细介绍这些方法。


didPush 方法会在新的路由被推送到导航器时被调用。它有两个参数:route  previousRoute,分别表示新推送的路由和之前的路由。

使用场景:当我们需要在新的路由被推送时执行一些操作时,可以在didPush方法中实现。


didPop 方法会在路由从导航器中弹出时被调用。它的参数和 didPush 方法相同。

使用场景:当我们需要在路由被弹出时执行一些操作时,可以在didPop方法中实现。


didRemove 方法会在路由被从导航器中移除时被调用。它有两个参数:route  previousRoute ,分别表示被移除的路由和之前的路由。

使用场景:当我们需要在路由被移除时执行一些操作时,可以在didRemove方法中实现。


didReplace 方法会在一个路由被另一个路由替换时被调用。它有两个参数:newRoute 和 oldRoute,分别表示新的路由和被替换的路由。

使用场景:当我们需要在路由被替换时执行一些操作时,可以在didReplace方法中实现。

下面是一个使用这些方法的例子:
在这个例子中,我们在每个方法中都打印了相关的信息,这样我们就可以清楚地看到路由的变化。

class MyRouteObserver extends NavigatorObserver {
  @override
  void didPush(Route route, Route previousRoute) {
    super.didPush(route, previousRoute);
    print('didPush ${route.settings.name}');
  }

  @override
  void didPop(Route route, Route previousRoute) {
    super.didPop(route, previousRoute);
    print('didPop ${route.settings.name}');
  }

  @override
  void didRemove(Route route, Route previousRoute) {
    super.didRemove(route, previousRoute);
    print('didRemove ${route.settings.name}');
  }

  @override
  void didReplace({Route newRoute, Route oldRoute}) {
    super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
    print('didReplace ${oldRoute.settings.name} with ${newRoute.settings.name}');
  }
}

路由观察者在 Flutter 应用中有很多实际的应用场景,例如用于用户行为跟踪,页面切换动画等。


在很多应用中,我们需要跟踪用户的行为,例如用户打开了哪些页面,停留在每个页面的时间等。这时,我们可以使用路由观察者来实现。

当用户打开一个新的页面时,didPush 方法会被调用,我们可以在这个方法中记录用户打开这个页面的时间;当用户关闭一个页面时,didPop 方法会被调用,我们可以在这个方法中记录用户关闭这个页面的时间。通过这种方式,我们就可以跟踪用户在每个页面的停留时间。


在一些应用中,我们希望在页面切换时有一些特殊的动画效果。这时,我们也可以使用路由观察者来实现。

我们可以在 didPush  didPop 方法中添加动画效果。例如,当用户打开一个新的页面时,我们可以在 didPush 方法中添加一个淡入效果;当用户关闭一个页面时,我们可以在 didPop 方法中添加一个淡出效果。

下面是一个使用路由观察者来实现用户行为跟踪的例子:

class MyRouteObserver extends NavigatorObserver {
  @override
  void didPush(Route route, Route previousRoute) {
    super.didPush(route, previousRoute);
    print('User opened ${route.settings.name}');
    // 这里我们可以启动一个计时器来跟踪用户在这个页面上停留的时间。
  }

  @override
  void didPop(Route route, Route previousRoute) {
    super.didPop(route, previousRoute);
    print('User closed ${route.settings.name}');
    // 在这里,我们可以停止计时器,并获得用户在此页面停留的时间。
  }
}

在这个例子中,我们在 didPush  didPop 方法中分别打印了用户打开和关闭页面的信息,并且提到了如何使用计时器来跟踪用户在每个页面的停留时间。


虽然路由观察者是一个非常强大的工具,但在使用过程中也有一些限制和需要注意的事项。


仅仅监听 Navigator 进行的路由变化

  1. 路由观察者只能监听到通过 Navigator 进行的路由变化,如果路由变化是通过其他方式(例如直接修改组件的状态)进行的,那么路由观察者是无法监听到的。

PopScope

  1. 路由观察者的方法是在路由变化后被调用的,因此它不能阻止路由的变化。如果你需要在路由变化前进行一些操作(例如询问用户是否确定要离开当前页面),那么你需要使用其他的方法,例如 PopScope WillPopScope 已经被废弃,使用PopScope替代)。
    关于 PopScope 这里补充一个案例分析:
import 'package:flutter/material.dart';

void main() => runApp(const NavigatorPopHandlerApp());

class NavigatorPopHandlerApp extends StatelessWidget {
  const NavigatorPopHandlerApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // 设置初始路由为 '/home'
      initialRoute: '/home',
      // 定义路由表
      routes: <String, WidgetBuilder>{
        '/home': (BuildContext context) => const _HomePage(),
        '/two': (BuildContext context) => const _PageTwo(),
      },
    );
  }
}
class _HomePage extends StatefulWidget {
  const _HomePage();

  @override
  State<_HomePage> createState() => _HomePageState();
}

// 首页的状态组件
class _HomePageState extends State<_HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Page One'), // 显示 "Page One" 文字
            TextButton(
              onPressed: () {
                Navigator.of(context).pushNamed('/two'); // 点击按钮后跳转到第二个页面
              },
              child: const Text('Next page'), // 显示 "Next page" 文字
            ),
          ],
        ),
      ),
    );
  }
}
// 第二个页面的组件
class _PageTwo extends StatefulWidget {
  const _PageTwo();

  @override
  State<_PageTwo> createState() => _PageTwoState();
}

// 第二个页面的状态组件
class _PageTwoState extends State<_PageTwo> {
  // 显示一个对话框,询问用户是否确定离开
  void _showBackDialog() {
    showDialog<void>(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('Are you sure?'), // 对话框标题
          content: const Text(
            'Are you sure you want to leave this page?', // 对话框内容
          ),
          actions: <Widget>[
            TextButton(
              style: TextButton.styleFrom(
                textStyle: Theme.of(context).textTheme.labelLarge,
              ),
              child: const Text('Nevermind'), // "Nevermind" 按钮
              onPressed: () {
                Navigator.pop(context); // 点击后关闭对话框
              },
            ),
            TextButton(
              style: TextButton.styleFrom(
                textStyle: Theme.of(context).textTheme.labelLarge,
              ),
              child: const Text('Leave'), // "Leave" 按钮
              onPressed: () {
                Navigator.pop(context); // 点击后关闭对话框
                Navigator.pop(context); // 并返回上一个页面
              },
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Page Two'), // 显示 "Page Two" 文字
            PopScope(
              canPop: false,
              onPopInvoked: (bool didPop) {
                if (didPop) {
                  return;
                }
                _showBackDialog(); // 当用户尝试离开时,显示对话框
              },
              child: TextButton(
                onPressed: () {
                  _showBackDialog(); // 点击按钮后显示对话框
                },
                child: const Text('Go back'), // 显示 "Go back" 文字
              ),
            ),
          ],
        ),
      ),
    );
  }
}

这段代码是Flutter官方给出的一个案例,它包含两个页面,用户可以在这两个页面之间导航。当用户尝试离开第二个页面时,会弹出一个对话框询问用户是否确定离开。


  1. 在使用路由观察者时,需要注意不要在路由观察者的方法中进行过于复杂的操作,因为这可能会影响到路由的性能。如果需要进行复杂的操作,建议在另一个异步任务中进行。

  2. 在使用路由观察者时,需要注意正确地管理路由观察者的生命周期。如果一个路由观察者被销毁了,但是你仍然试图在它的方法中访问一些资源,那么可能会导致错误

  3. 在使用路由观察者时,需要注意处理好异常。因为路由观察者的方法是在路由变化后被调用的,如果在这些方法中发生了异常,那么可能会导致应用崩溃。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。