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

如何用GoRouter实现仅保留部分底部导航路由的状态?

实现底部导航页按需刷新(A页保状态,B/C页每次切换重加载)

针对你的需求,A页面需要保留状态仅首次加载长任务,B/C页面每次切换都要重新拉取数据库数据,结合现有StatefulShellRoute的结构,推荐以下两种实现方案:

方案1:利用页面可见性监听(推荐)

让B/C页面监听自身的路由可见状态,当页面从不可见变为可见时自动触发数据重新加载,无需修改现有路由结构。

步骤1:全局配置路由观察者

首先定义全局的RouteObserver,并注册到应用的导航观察者中:

final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();

// 在MaterialApp或GoRouter的配置中添加
MaterialApp(
  navigatorObservers: [routeObserver],
  // 其他配置项
)

步骤2:改造B/C页面添加监听

以B页面为例(C页面逻辑完全一致),通过RouteAware混入实现状态监听:

class B extends StatefulWidget {
  const B({super.key});

  @override
  State<B> createState() => _BState();
}

class _BState extends State<B> with RouteAware {
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 注册路由监听
    routeObserver.subscribe(this, ModalRoute.of(context)! as PageRoute);
    // 首次加载数据
    _fetchData();
  }

  @override
  void dispose() {
    routeObserver.unsubscribe(this);
    super.dispose();
  }

  @override
  void didPush() {
    // 从其他页面切换到B时,重新加载数据
    _fetchData();
  }

  @override
  void didPopNext() {
    // 从A的子路由返回B时,重新加载数据
    _fetchData();
  }

  void _fetchData() {
    // 这里编写数据库数据获取逻辑,每次调用都会重新评估页面状态
    // 示例:读取本地数据库并更新UI
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("页面B")),
      body: // 页面内容组件
    );
  }
}

A页面保持原有逻辑即可,无需添加监听,确保长任务仅执行一次。

方案2:底部导航切换时主动触发刷新

通过全局Key绑定B/C页面的State,在底部导航切换时主动调用页面的刷新方法。

步骤1:给B/C页面绑定全局Key

// 全局定义页面Key
final GlobalKey<_BState> _bPageKey = GlobalKey<_BState>();
final GlobalKey<_CState> _cPageKey = GlobalKey<_CState>();

// 在GoRoute的页面构建中传入Key
GoRoute(
  path: B.route,
  pageBuilder: (context, state) => NoTransitionPage(
    child: B(key: _bPageKey),
  ),
)

步骤2:修改底部导航切换逻辑

在ScaffoldWithBottomNavigation中,切换到B/C索引时主动触发数据刷新:

class ScaffoldWithBottomNavigation extends StatelessWidget {
  const ScaffoldWithBottomNavigation({super.key, required this.navigationShell});

  final StatefulNavigationShell navigationShell;

  void _onTap(int index) {
    navigationShell.goBranch(
      index,
      initialLocation: index == navigationShell.currentIndex,
    );
    // 切换到B或C时触发刷新
    if (index == 1) { // 假设B是第二个索引(0=A,1=B,2=C)
      _bPageKey.currentState?._fetchData();
    } else if (index == 2) {
      _cPageKey.currentState?._fetchData();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: navigationShell,
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: navigationShell.currentIndex,
        onTap: _onTap,
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: "A"),
          BottomNavigationBarItem(icon: Icon(Icons.business), label: "B"),
          BottomNavigationBarItem(icon: Icon(Icons.school), label: "C"),
        ],
      ),
    );
  }
}

同时需要将B页面的_fetchData方法改为可访问的公开方法:

class _BState extends State<B> {
  // 修改为公开方法
  void _fetchData() {
    // 数据库数据获取逻辑
  }
}

方案说明

两种方案都无需修改现有StatefulShellRoute的分支结构,既能保留A页面的状态和子路由栈,又能满足B/C页面每次切换重新加载数据的需求。方案1更符合Flutter的路由生命周期设计,无需额外的全局Key管理,推荐优先使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 18:23:17