结合GoRouter Stateful Shell Navigation与persistent_bottom_nav_bar_v2监听标签页切换以启停定时器的实现方案
嘿,我来帮你搞定这个定时器启停的问题!结合你用的GoRouter Stateful Shell和Persistent Bottom Nav,其实有几个关键的地方可以入手,我一步步给你拆解:
一、优先推荐:把定时器逻辑抽离到全局Provider
把定时器从页面VM移到全局Provider里,能让你在任何地方(比如标签切换回调)都能轻松控制它的启停,比绑定在页面VM上更灵活。比如用ChangeNotifier实现一个定时器管理类:
class TimerManager extends ChangeNotifier { Timer? _timer; int _elapsedSeconds = 0; bool _isRunning = false; int get elapsedSeconds => _elapsedSeconds; bool get isRunning => _isRunning; // 启动/恢复定时器 void startOrResume() { if (_isRunning) return; _isRunning = true; _timer = Timer.periodic(const Duration(seconds: 1), (timer) { _elapsedSeconds++; notifyListeners(); }); } // 暂停定时器 void pause() { _timer?.cancel(); _timer = null; _isRunning = false; notifyListeners(); } // 重置定时器 void reset() { pause(); _elapsedSeconds = 0; notifyListeners(); } @override void dispose() { _timer?.cancel(); super.dispose(); } }
然后在你的MultiProvider里注册这个类:
MultiProvider( providers: [ // 其他已注册的Provider... ChangeNotifierProvider(create: (_) => TimerManager()), ], child: MyApp(), )
二、添加标签索引管理Provider
为了准确判断是进入还是离开目标标签,我们需要一个Provider来记录当前活跃的标签索引:
class TabIndexNotifier extends ChangeNotifier { int _currentIndex = 0; int get currentIndex => _currentIndex; void updateIndex(int newIndex) { _currentIndex = newIndex; notifyListeners(); } }
同样在MultiProvider里注册它。
三、在标签切换时触发定时器启停
假设你的Foo标签是底部导航的第2个(索引为1,从0开始计数),我们来修改你现有代码中的StatefulShellRoute部分:
StatefulShellRoute.indexedStack( builder: (context, state, navigationShell) { final currentIndex = navigationShell.currentIndex; log("Navigated to tab of index: $currentIndex"); final timerManager = context.read<TimerManager>(); final tabIndexNotifier = context.read<TabIndexNotifier>(); final previousIndex = tabIndexNotifier.currentIndex; // 核心逻辑:根据标签切换状态控制定时器 if (previousIndex == 1 && currentIndex != 1) { // 离开Foo标签,暂停定时器 timerManager.pause(); } if (currentIndex == 1 && previousIndex != 1) { // 进入Foo标签,恢复定时器 timerManager.startOrResume(); } // 更新当前活跃标签索引 tabIndexNotifier.updateIndex(currentIndex); return Consumer<ThemeNotifier>( builder: (context, themeNotifier, child) { final currentTheme = Theme.of(context); return PersistentTabView.router( stateManagement: false, tabs: [ // 你的现有标签配置... PersistentRouterTabConfig( item: ItemConfig( icon: const Icon(Icons.example), title: "Examples", activeForegroundColor: currentTheme.colorScheme.primary, inactiveForegroundColor: currentTheme.colorScheme.onSurface, textStyle: currentTheme.textTheme.bodyLarge!), ), PersistentRouterTabConfig( item: ItemConfig( icon: const Icon(Icons.foo), title: "Foos", inactiveBackgroundColor: Colors.transparent, activeForegroundColor: currentTheme.colorScheme.primary, inactiveForegroundColor: currentTheme.colorScheme.onSurface, textStyle: currentTheme.textTheme.bodyLarge ?? TextStyle( color: CupertinoColors.systemGrey, fontWeight: FontWeight.w400, fontSize: 12.0, ), ), ), // 其他标签... ], navBarBuilder: (navBarConfig) => Style2BottomNavBar( navBarDecoration: NavBarDecoration(color: currentTheme.colorScheme.surface), navBarConfig: navBarConfig, ), navigationShell: navigationShell, ); }, ); }, // 你的现有branches配置... branches: [ StatefulShellBranch(navigatorKey: _ScheduleNavKey, routes: [...]), StatefulShellBranch(navigatorKey: _FooNavKey, routes: [...]), StatefulShellBranch(navigatorKey: _SettingsNavKey, routes: [...]), ], )
四、在Foo页面初始化时启动定时器
当用户第一次进入Foo页面时,需要自动启动定时器,修改FooHomePage的代码:
class FooHomePage extends StatefulWidget { final FooHomeViewModel vm; const FooHomePage({super.key, required this.vm}); @override State<FooHomePage> createState() => _FooHomePageState(); } class _FooHomePageState extends State<FooHomePage> { @override void initState() { super.initState(); final timerManager = context.read<TimerManager>(); final tabIndexNotifier = context.read<TabIndexNotifier>(); // 如果当前活跃标签是Foo且定时器未运行,启动定时器 if (tabIndexNotifier.currentIndex == 1 && !timerManager.isRunning) { timerManager.startOrResume(); } } @override Widget build(BuildContext context) { // 用Consumer监听定时器状态,实时更新UI return Consumer<TimerManager>( builder: (context, timerManager, child) { return Scaffold( body: Center( child: Text("Foo页面定时器已运行: ${timerManager.elapsedSeconds}秒"), ), ); }, ); } }
五、如果不想移到Provider的替代方案
如果你想保留定时器在Foo页面的VM里,也可以通过GlobalKey获取页面State来调用VM方法,但这种方式耦合性较高,不推荐长期使用:
- 给
FooHomePage设置全局key:final GlobalKey<_FooHomePageState> fooPageKey = GlobalKey(); - 在
FooHomePage的State里暴露VM的控制方法:void pauseTimer() => widget.vm.pauseTimer(); - 在标签切换回调里调用:
fooPageKey.currentState?.pauseTimer()
内容来源于stack exchange
相关产品推荐
相关产品推荐

