import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; /// NavBar 配置,由各页面在 build 中设置,AppShell 统一渲染 class NavBarConfig { final String title; final bool showBack; final bool showRight; final Widget? rightWidget; final VoidCallback? onBack; final IconData? leadingIcon; final bool hasFilter; final int filterVersion; const NavBarConfig({ required this.title, this.showBack = false, this.showRight = false, this.rightWidget, this.onBack, this.leadingIcon, this.hasFilter = false, this.filterVersion = 0, }); /// 根页面默认配置(无返回按钮) static const home = NavBarConfig(title: 'TBOSS 工作台', showBack: false); static const messages = NavBarConfig(title: '消息', showBack: false); static const profile = NavBarConfig(title: '我的', showBack: false); /// 带返回按钮的页面快捷构造 factory NavBarConfig.withBack(String title, {VoidCallback? onBack}) { return NavBarConfig(title: title, showBack: true, onBack: onBack); } /// 带返回按钮 + 右侧按钮的页面快捷构造 factory NavBarConfig.withRight( String title, { required Widget rightWidget, VoidCallback? onBack, }) { return NavBarConfig( title: title, showBack: true, showRight: true, rightWidget: rightWidget, onBack: onBack, ); } @override bool operator ==(Object other) => other is NavBarConfig && other.title == title && other.showBack == showBack && other.showRight == showRight && other.hasFilter == hasFilter && other.filterVersion == filterVersion; // 故意不比较 onBack / rightWidget / leadingIcon,避免每次 build 新闭包触发无限重建 @override int get hashCode => Object.hash(title, showBack, showRight, hasFilter, filterVersion); } /// NavBar 配置变更器,内部做相等判断避免无限重建 /// /// 通过 [Future.microtask] 将状态更新推迟到事件循环下一个 tick, /// 避免子页面在 build 中更新 provider 时与 AppShell watch 冲突。 class NavBarConfigNotifier extends StateNotifier { NavBarConfigNotifier() : super(NavBarConfig.home); void update(NavBarConfig config) { if (state != config) { Future.microtask(() { if (mounted) state = config; }); } } } /// NavBar 配置的 Provider —— 各页面在 build 中更新,AppShell 统一消费 final navBarConfigProvider = StateNotifierProvider( (ref) => NavBarConfigNotifier(), ); /// 仅在当前路由可见时更新导航栏标题,避免被覆盖的页面因 GoRouter /// rebuild 级联效应竞争标题(如 showDatePicker 导致的路由栈全量重建)。 /// /// [context] 为当前页面 build 的 context, /// [config] 为要设置的 NavBarConfig。 void setNavBarTitle(BuildContext context, WidgetRef ref, NavBarConfig config) { final route = ModalRoute.of(context); // null = 首次 build 路由尚未完全插入,仍需更新 // isCurrent = 当前可见路由 // false = 已被覆盖的旧页面,跳过 if (route == null || route.isCurrent) { ref.read(navBarConfigProvider.notifier).update(config); } }