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

Flutter中BlocBuilder找不到Provider及BlocProvider创建未执行问题

问题:AuthScreen中BlocProvider的create方法未执行,且BlocBuilder找不到对应Provider

我开发的Flutter应用包含HomePage和AuthScreen(登录注册)两个路由,main函数通过Provider实现依赖注入。HomeScreen中的BlocProvider可正常工作,但AuthScreenWidget存在两个问题:

  • BlocProvider的create方法未执行
  • 出现错误:Could not find the correct Provider<StateStreamable<Object?>> above this BlocBuilder<StateStreamable<Object?>, Object?> Widget

相关代码

main函数

Future<void> main() async {
   JustAudioBackground.init(
    androidNotificationChannelId: 'com.ryanheise.bg_demo.channel.audio',
    androidNotificationChannelName: 'Audio playback',
    androidNotificationOngoing: true,
  );
  runApp(Provider<Repository>(
    create: (context) => Repository(
        RemoteDataSource(ApiService.dioInstance()), LocalDataSource()),
    child: MyApp(),
  ));
}

MyApp

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const defaultFontFamily = 'iran_sans';

  @override
  Widget build(BuildContext context) {
    const primaryTextColor = Color(0xff0D253C);
    const secondaryTextColor = Color(0xff2D4379);
    return Builder(
      builder: (context) {
        return MaterialApp(
          initialRoute: '/auth',
          routes: {
            '/': (context) => const HomeScreen(),
            '/auth': (context) => AuthScreenWidget(),
          },
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            textTheme: const TextTheme(
              subtitle1: TextStyle(
                  fontFamily: defaultFontFamily,
                  color: Colors.white,
                  fontWeight: FontWeight.w500,
                  fontSize: 16),
              subtitle2: TextStyle(
                  fontFamily: defaultFontFamily,
                  color: Colors.white,
                  fontWeight: FontWeight.w700,
                  fontSize: 18),
              headline5: TextStyle(
                  fontFamily: defaultFontFamily,
                  color: Colors.black,
                  fontWeight: FontWeight.w700,
                  fontSize: 16),
              headline6: TextStyle(
                  fontFamily: defaultFontFamily,
                  fontWeight: FontWeight.bold,
                  fontSize: 18,
                  color: primaryTextColor),
              headline4: TextStyle(
                  fontFamily: defaultFontFamily,
                  fontWeight: FontWeight.w700,
                  fontSize: 24,
                  color: primaryTextColor),
              bodyText2: TextStyle(
                  fontFamily: defaultFontFamily,
                  color: secondaryTextColor,
                  fontSize: 12),
              bodyText1: TextStyle(
                  fontFamily: defaultFontFamily,
                  color: Color.fromARGB(255, 33, 0, 122),
                  fontSize: 20),
            ),
          ),
          builder: EasyLoading.init(),
        );
      }
    );
  }
}

AuthScreenWidget

class AuthScreenWidget extends StatefulWidget {
  @override
  State<AuthScreenWidget> createState() => AuthScreenWidgetState();
}

class AuthScreenWidgetState extends State<AuthScreenWidget> {
  String dropdownValue = '+98';
  late final TextEditingController phoneNumber;


  @override
  void initState() {
    super.initState();
    phoneNumber = TextEditingController();
  }

  @override
  void dispose() {
    phoneNumber.dispose();
    super.dispose();
  }


  @override
  Widget build(BuildContext context) {
    return Builder(
      builder: (context) {
        return BlocProvider<AuthBloc>(
          create: (context) {
            final authBloc = AuthBloc(
                context.read<Repository>(),
                phoneNumber.text);
            authBloc.add(AuthStarted());
            return authBloc;
          },
          child: Scaffold(
            body: Column(
              children: [
                Expanded(
                  child: Padding(
                    padding: EdgeInsets.fromLTRB(18, 18, 18, 0),
                    child: Align(
                      alignment: Alignment.bottomCenter,
                      child: Directionality(
                        textDirection: TextDirection.rtl,
                        child: TextField(
                          controller: phoneNumber,
                          keyboardType: TextInputType.phone,
                          style: TextStyle(color: Colors.black),
                          decoration: const InputDecoration(
                              border: OutlineInputBorder(),
                              hintText: 'شماره تلفن',
                              contentPadding: EdgeInsets.all(18),
                              icon: Icon(CupertinoIcons.phone)),
                        ),
                      ),
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.fromLTRB(18, 18, 18, 18),
                  child: Align(
                    alignment: Alignment.bottomCenter,
                    child: Directionality(
                      textDirection: TextDirection.rtl,
                      child: SizedBox(
                        width: double.infinity,
                        height: 53,
                        child: ElevatedButton(
                          style: ButtonStyle(
                            shape: MaterialStateProperty.all<
                                RoundedRectangleBorder>(
                              RoundedRectangleBorder(
                                borderRadius: BorderRadius.circular(18.0),
                              ),
                            ),
                          ),
                          child: const Text(
                            'ورود',
                          ),
                          onPressed: () {
                            if (phoneNumber.text != "") {
                              BlocProvider<AuthBloc>(
                                  create: (BuildContext context) {
                                    final authBloc = AuthBloc(
                                        context.read<Repository>(),
                                        phoneNumber.text);
                                    authBloc.add(AuthStarted());
                                    return authBloc;
                                  });
                            }
                          },
                        ),
                      ),
                    ),
                  ),
                ),
                Container(
                  width: double.infinity,
                  child: Column(
                    children: [
                      Padding(
                        padding: const EdgeInsets.fromLTRB(0, 0, 0, 32),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: const [
                            Text("ورود با ایمیل"),
                            Icon(Icons.email, size: 20),
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
                DropdownButton<String>(
                  value: dropdownValue,
                  icon: const Icon(Icons.phone),
                  elevation: 16,
                  style: const TextStyle(color: Colors.black),

                  onChanged: (String? newValue) {
                    setState(() {
                      dropdownValue = newValue!;
                    });
                  },
                  items: <String>['+98']
                      .map<DropdownMenuItem<String>>((String value) {
                    return DropdownMenuItem<String>(
                      value: value,
                      child: Text(value),
                    );
                  }).toList(),
                ),

                BlocBuilder(builder: (context, state) {
                  if (state is AuthSuccess) {
                    print("success auth");
                    return Container();
                  }
                  return Container();
                })
              ],
            ),
          ),
        );
      }
    );
  }
}

错误详情

Error: Could not find the correct Provider<StateStreamable<Object?>> above this BlocBuilder<StateStreamable<Object?>, Object?> Widget

This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:

- You added a new provider in your `main.dart` and performed a hot-reload.
  To fix, perform a hot-restart.

- The provider you are trying to read is in a different route.

  Providers are "scoped". So if you insert of provider inside a route, then
  other routes will not be able to access that provider.

- You used a `BuildContext` that is an ancestor of the provider you are trying to read.

  Make sure that BlocBuilder<StateStreamable<Object?>, Object?> is under your MultiProvider/Provider<StateStreamable<Object?>>.
  This usually happens when you are creating a provider and trying to read it immediately.

  For example, instead of:

Widget build(BuildContext context) {
return Provider(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because context is associated
// to the widget that is the parent of Provider<Example>
child: Text(context.watch().toString()),
);
}

consider using `builder` like so:

Widget build(BuildContext context) {
return Provider(
create: (_) => Example(),
// we use builder to obtain a new BuildContext that has access to the provider
builder: (context, child) {
// No longer throws
return Text(context.watch().toString());
}
);
}


解决方案

1. 给BlocBuilder指定明确的泛型类型

当前BlocBuilder未声明对应的Bloc和State类型,导致框架无法定位到AuthBloc。修改为:

BlocBuilder<AuthBloc, AuthState>(builder: (context, state) {
  if (state is AuthSuccess) {
    print("success auth");
    return Container();
  }
  return Container();
})

2. 修复BlocProvider的作用域与BuildContext问题

外层的Builder是多余的,且原代码中BlocBuilder使用的是BlocProvider的父context,无法访问到BlocProvider。需要将BlocProvider的child替换为builder,让子组件能获取包含BlocProvider的context:

@override
Widget build(BuildContext context) {
  return BlocProvider<AuthBloc>(
    create: (context) {
      final authBloc = AuthBloc(
        context.read<Repository>(),
        phoneNumber.text
      );
      authBloc.add(AuthStarted());
      return authBloc;
    },
    builder: (context, child) {
      return Scaffold(
        body: Column(
          children: [
            // ... 其他组件保持不变
            // 这里的context已经包含BlocProvider
            BlocBuilder<AuthBloc, AuthState>(builder: (context, state) {
              if (state is AuthSuccess) {
                print("success auth");
                return Container();
              }
              return Container();
            })
          ],
        ),
      );
    },
  );
}

3. 修复按钮点击事件中的错误逻辑

按钮点击时创建BlocProvider但未将其加入widget树,完全无效。应该直接获取已存在的AuthBloc并触发事件:

onPressed: () {
  if (phoneNumber.text.isNotEmpty) {
    // 获取当前已存在的AuthBloc
    final authBloc = context.read<AuthBloc>();
    // 传递手机号并触发登录事件(需确保AuthStarted事件支持接收手机号参数)
    authBloc.add(AuthStarted(phoneNumber: phoneNumber.text));
  }
}

4. 解决初始化时手机号为空的问题

initState时phoneNumber是空字符串,此时创建的AuthBloc会传入空值。可以:

  • 延迟AuthStarted事件的触发,直到用户输入手机号后再执行
  • 监听TextField的变化,动态更新AuthBloc的状态
  • 在按钮点击时重新创建AuthBloc(需注意旧Bloc的资源释放)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 10:24:45