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

重写BeamLocation的builder方法引发Provider相关状态错误

解决Beamer+Provider在多BeamLocation切换时的构建错误问题

问题核心

你用嵌套Beamer+IndexedStack实现BottomNavbar标签状态保留,想给单个BeamLocation下的页面共享状态,于是用ChangeNotifierProvider包裹BeamLocation,但切换到其他BeamLocation时,被包裹的Location仍会触发页面构建,导致Provider销毁后引发错误。根源在于:

  • 当BeamLocation的builder直接返回传入的navigator时,Beamer内部会做优化,非活跃Location不会触发额外构建;
  • 一旦builder返回嵌套了其他Widget(比如Container)的结构,即使Location不活跃,Beamer仍会调用该方法生成新Widget,进而触发navigator及内部页面的构建,引发Provider相关错误。

解决方案

方案1:将Provider放在BeamLocation的页面层级(推荐)

不要在BeamLocation的builder里包裹Provider,而是给该Location的所有页面创建一个根页面,把Provider放在这个根页面中,确保只有Location活跃时才会构建Provider:

class SharedStateLocation extends BeamLocation {
  @override
  List<BeamPage> buildPages(BuildContext context, BeamState state) {
    // 确定当前活跃的子页面索引
    final activeIndex = state.pathSegments.isEmpty ? 0 : int.parse(state.pathSegments.first);
    
    return [
      BeamPage(
        key: const ValueKey('shared-state-root'),
        child: ChangeNotifierProvider(
          create: (_) => SharedPageState(), // 共享状态
          child: Scaffold(
            body: IndexedStack(
              index: activeIndex,
              children: const [
                PageA(),
                PageB(),
                PageC(),
              ],
            ),
          ),
        ),
      ),
    ];
  }

  @override
  Set<Pattern> get pathPatterns => {'/', '/:pageIndex'};
}

这样,只有当该BeamLocation处于活跃状态时,根BeamPage才会被加入Widget树,Provider也会随之创建;切换到其他Location后,根页面被移除,Provider会被正确销毁,不会出现多余构建。

方案2:在BeamLocation的builder中添加活跃判断

如果必须在BeamLocation的builder里包裹Provider,可以通过判断当前活跃的Location是否为当前实例,仅在活跃时构建带Provider的导航器:

class SharedStateLocation extends BeamLocation {
  @override
  Widget builder(BuildContext context, Widget navigator) {
    final currentActiveLocation = Beamer.of(context).currentBeamLocation;
    // 仅当当前活跃的是本Location时,才构建Provider和导航器
    if (currentActiveLocation == this) {
      return ChangeNotifierProvider(
        create: (_) => SharedPageState(),
        child: navigator,
      );
    }
    // 非活跃时返回空Widget,避免不必要的构建
    return const SizedBox.shrink();
  }

  @override
  List<BeamPage> buildPages(BuildContext context, BeamState state) {
    // 页面构建逻辑...
  }

  @override
  Set<Pattern> get pathPatterns => {/* 路径规则 */};
}

方案3:通过BeamerDelegate的builder全局控制

在主BeamerDelegate的builder中,根据当前活跃的BeamLocation类型,动态决定是否包裹Provider:

final mainBeamerDelegate = BeamerDelegate(
  locationBuilder: (context) {
    // 根据路由返回对应BeamLocation
    final route = Beamer.of(context).currentRouteInformation;
    if (route.location?.startsWith('/shared') ?? false) {
      return SharedStateLocation();
    }
    return OtherLocation();
  },
  builder: (context, navigator) {
    final currentLocation = Beamer.of(context).currentBeamLocation;
    if (currentLocation is SharedStateLocation) {
      return ChangeNotifierProvider(
        create: (_) => SharedPageState(),
        child: navigator,
      );
    }
    return navigator;
  },
);

关键原理

Beamer对builder直接返回navigator的情况做了优化,会复用已有导航器实例且避免非活跃Location的构建;而嵌套Widget会导致每次调用builder都生成新的Widget树,触发非活跃Location页面的不必要构建,进而引发Provider销毁后的错误。通过上述方案,可确保Provider仅在对应Location活跃时存在,切换后正确销毁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 22:45:40