You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter:如何检测页面导航离开以停止计时器?

解决方案

方案一:使用RouteObserver监听路由切换

这是最贴合Flutter路由机制的方案,能精准捕获当前页面被新路由覆盖的事件:

  1. 全局注册RouteObserver
    在你的MaterialApp中添加RouteObserver到navigatorObservers:
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorObservers: [routeObserver], // 注册路由观察者
      // ... 其他配置项
    );
  }
}
  1. 页面State混入RouteAware并订阅监听
    在计时器所在页面的State类中,混入RouteAware,并在生命周期方法中完成订阅与取消:
class MeditationTimerPageState extends State<MeditationTimerPage> with RouteAware {
  Timer? _timer;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 订阅路由观察者
    routeObserver.subscribe(this, ModalRoute.of(context)! as PageRoute);
  }

  @override
  void dispose() {
    routeObserver.unsubscribe(this); // 取消订阅
    _timer?.cancel(); // 确保页面销毁时清理计时器
    super.dispose();
  }

  // 当新路由被push到当前路由之上时触发(比如通过Drawer跳转到其他页面)
  @override
  void didPushNext() {
    super.didPushNext();
    _timer?.cancel(); // 停止计时器
    _timer = null;
  }

  // 可选:当当前路由重新回到前台时,可重启计时器(按需实现)
  @override
  void didPopNext() {
    super.didPopNext();
    // _initTimer(); // 调用你的计时器初始化方法
  }

  // ... 你的计时器初始化逻辑(比如在initState中创建Timer.periodic)
}

方案二:监听页面可见性(使用VisibilityDetector)

如果不想处理路由监听逻辑,可以用visibility_detector包监听页面可见性,当页面完全不可见时停止计时器:

  1. 添加依赖到pubspec.yaml:
dependencies:
  visibility_detector: ^0.4.0+2
  1. 包裹页面内容并监听可见性变化:
class MeditationTimerPageState extends State<MeditationTimerPage> {
  Timer? _timer;

  @override
  Widget build(BuildContext context) {
    return VisibilityDetector(
      key: const Key('meditation-timer-page'),
      onVisibilityChanged: (info) {
        if (info.visibleFraction == 0.0) {
          // 页面完全不可见,停止计时器
          _timer?.cancel();
          _timer = null;
        } else if (info.visibleFraction == 1.0 && _timer == null) {
          // 页面重新可见,重启计时器(按需实现)
          _initTimer();
        }
      },
      child: Scaffold(
        // 你的页面内容
      ),
    );
  }

  void _initTimer() {
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      // 计时逻辑
    });
  }

  @override
  void dispose() {
    _timer?.cancel();
    super.dispose();
  }
}

方案三:在Drawer跳转时主动通知停止计时器

如果你的Drawer跳转逻辑集中管理,可以在跳转前主动触发停止计时器的操作,以下是用Provider全局状态管理的示例:

  1. 创建计时器状态类:
class TimerNotifier extends ChangeNotifier {
  Timer? _timer;

  void startTimer() {
    _timer?.cancel();
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      // 计时逻辑
    });
  }

  void stopTimer() {
    _timer?.cancel();
    _timer = null;
  }
}
  1. 在MaterialApp中提供状态:
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => TimerNotifier(),
      child: MaterialApp(
        // ... 其他配置项
      ),
    );
  }
}
  1. 计时器页面使用状态:
class MeditationTimerPageState extends State<MeditationTimerPage> {
  late TimerNotifier _timerNotifier;

  @override
  void initState() {
    super.initState();
    _timerNotifier = Provider.of<TimerNotifier>(context, listen: false);
    _timerNotifier.startTimer();
  }

  @override
  void dispose() {
    _timerNotifier.stopTimer();
    super.dispose();
  }

  // ... 页面内容
}
  1. Drawer跳转时调用停止方法:
Drawer(
  child: ListView(
    children: [
      ListTile(
        title: const Text('其他页面'),
        onTap: () {
          // 先停止计时器
          Provider.of<TimerNotifier>(context, listen: false).stopTimer();
          // 再执行跳转
          Navigator.pushNamed(context, '/other-page');
        },
      ),
    ],
  ),
)

内容的提问来源于stack exchange,提问作者Katya S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 17:39:56