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

使用泛型与抽象类时遭遇ProviderNotFound Exception问题求助

问题分析与解决方案

问题描述

在实现基于Provider的抽象组件时,调用Provider.of<T>(ctx)抛出ProviderNotFound异常,核心代码如下:

抽象类代码

abstract class BaseStatelessWidget<T extends BaseStore> extends StatelessWidget
    with RouteAware {
  late final T childStore;

  BaseStatelessWidget({super.key});

  @override
  Widget build(BuildContext context) {
    childStore = getImplementedStore();
    return Provider(
      create: <T>(_) => childStore,
      builder: (ctx, __) {
        final store = Provider.of<T>(ctx, listen: false); // 此处抛出ProviderNotFound异常
        store;
        return _BaseStatefulWidget<T>(
          builder: buildScreen,
        );
      },
    );
  }

  Widget buildScreen(BuildContext context);

  T getImplementedStore();
}

实现类代码

class Counter extends BaseStatelessWidget<CounterStore> {
  Counter({super.key});

  @override
  Widget buildScreen(BuildContext context) {
    return Scaffold(
      body: Container(
        color: AppColors.white,
        child: Column(
          children: [
            Observer(
              builder: (ctx) {
                return Text(
                  '${getStore<CounterStore>(ctx).counter} Times Clicked',
                );
              },
            )
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          getStore<CounterStore>(context).increment();
        },
        child: const Icon(Icons.add),
      ),
    );
  }

  @override
  CounterStore getImplementedStore() {
    return getIt<CounterStore>();
  }
}

T getStore<T extends BaseStore>(BuildContext context) {
  return Provider.of<T>(context, listen: false);
}

核心错误点

  • StatelessWidget的可变状态违规:StatelessWidget是不可变组件,build方法会被多次触发,每次执行childStore = getImplementedStore();都会重新赋值,既违反设计原则,也会导致Store实例重复获取,引发状态不一致。
  • Provider注册与获取时机冲突:在Provider的builder函数中立即调用Provider.of<T>(ctx)时,当前Provider的实例还未完成上下文注入,此时上下文无法识别该Provider,直接抛出异常。
  • 泛型显式指定冗余:create: <T>(_) => childStore中显式指定<T>属于冗余操作,结合Dart泛型擦除机制,会导致Provider无法正确匹配注入类型。

修复后的代码

修改抽象类为StatefulWidget(正确持有状态)

abstract class BaseStatelessWidget<T extends BaseStore> extends StatefulWidget
    with RouteAware {
  const BaseStatelessWidget({super.key});

  @override
  State<BaseStatelessWidget<T>> createState() => _BaseStatelessWidgetState<T>();

  Widget buildScreen(BuildContext context);

  T getImplementedStore();
}

class _BaseStatelessWidgetState<T extends BaseStore> extends State<BaseStatelessWidget<T>> {
  late final T childStore;

  @override
  void initState() {
    super.initState();
    // 在initState中初始化Store,仅执行一次
    childStore = widget.getImplementedStore();
  }

  @override
  Widget build(BuildContext context) {
    return Provider(
      // 移除显式泛型,让类型自动推导
      create: (_) => childStore,
      // 使用child替代builder,避免时机冲突
      child: _BaseStatefulWidget<T>(
        builder: widget.buildScreen,
      ),
    );
  }
}

实现类无需修改

原Counter组件和getStore函数可直接复用,修复后Provider已正确注入上下文,后续在buildScreen中通过getStore或Provider.of都能正常获取实例。

关键修复说明

  • 改为StatefulWidget并在initState中初始化childStore,确保Store实例仅被获取一次,符合组件生命周期规范。
  • 用Provider的child参数替代builder,避免在Provider未完成注入时提前获取实例。
  • 移除create方法中的显式泛型指定,依赖Dart自动类型推导,避免泛型擦除导致的匹配问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 00:54:52