无法在此测试小部件上方找到正确的提供程序

发布于 2025-01-12 00:54:17 字数 3828 浏览 2 评论 0原文

======== 手势捕获异常======================================== ========================= 处理手势时抛出以下 ProviderNotFoundException: 错误:在此测试小部件之上找不到正确的提供程序

发生这种情况是因为您使用了不包含提供程序的 BuildContext 由您选择。有一些常见的场景:

  • 您在 main.dart 中添加了一个新的提供程序并执行了热重载。 要修复此问题,请执行热重启。

  • 您尝试读取的提供商位于不同的路线。

    提供者是有“范围的”。因此,如果您在路线中插入提供者,那么 其他路由将无法访问该提供商。

  • 您使用的 BuildContext 是您尝试读取的提供程序的祖先。

    确保测试位于您的 MultiProvider/Provider 下。 当您创建提供程序并尝试立即读取它时,通常会发生这种情况。

    例如,而不是:

    小部件构建(BuildContext context){
      返回提供者<示例>(
        创建:(_) =>例子(),
        // 会抛出 ProviderNotFoundError,因为 `context` 是关联的
        // 到作为 `Provider` 父级的小部件
        子:文本(context.watch<示例>()),
      ),
    }
    

    考虑使用builder,如下所示:

    小部件构建(BuildContext context){
      返回提供者<示例>(
        创建:(_) =>例子(),
        // 我们使用 `builder` 来获取一个可以访问提供者的新的 `BuildContext`
        构建器:(上下文){
          // 不再抛出异常
          返回文本(context.watch<示例>()),
        }
      ),
    }
    

如果这些解决方案都不起作用,请考虑在 StackOverflow 上寻求帮助: https://stackoverflow.com/questions/tagged/flutter

我正在构建一个小部件“测试”来搜索用户通过他们的用户名。这是使用 Bloc 进行测试的小部件。

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

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => DonorsCubit(),
      child: BlocListener<DonorsCubit, DonorsState>(
        listener: (context, state) {
          print(state);
        },
        child: Scaffold(
          appBar: AppBar(),
          body: IconButton(
            onPressed: () {
              context.read<DonorsCubit>().searchDonors(searchKey: "masum");
            },
            icon: BlocBuilder<DonorsCubit, DonorsState>(
              builder: (context, state) {
                if (state is DonorsInitialState) return const Icon(Icons.add);
                if (state is DonorsLoadedState) return const Icon(Icons.done);
                if (state is DonorsLoadingState) return const Icon(Icons.circle);
                return const SizedBox();
              },
            ),
          ),
        ),
      ),
    );
  }
}

我使用这个肘节来管理状态。

class DonorsCubit extends Cubit<DonorsState> {
  List<MyUser> users = <MyUser>[];
  final FirebaseDBRepo _firebaseDBRepo = FirebaseDBRepo();
  late StreamSubscription _streamSubscription;

  DonorsCubit() : super(DonorsInitialState()) {
    _streamSubscription =
        _firebaseDBRepo.usersStream().listen((List<MyUser> users) {
      this.users = users;
    });
  }

  void searchDonors({required String? searchKey}) {
    emit(DonorsLoadingState());
    List<MyUser> searchedUser = <MyUser>[];
    searchedUser.clear();
    if (searchKey == null) {
      emit(DonorsLoadedState(users: users));
    } else {
      for (MyUser user in users) {
        if (user.username!.toLowerCase().contains(searchKey.toLowerCase())) {
          searchedUser.add(user);
        }
      }
      emit(DonorsLoadedState(users: searchedUser));
    }
  }

  @override
  Future<void> close() {
    _streamSubscription.cancel();
    return super.close();
  }
}



abstract class DonorsState extends Equatable {
  const DonorsState();
}

class DonorsLoadingState extends DonorsState {
  @override
  List<Object> get props => [];
}

class DonorsInitialState extends DonorsState {
  @override
  List<Object> get props => [];
}

class DonorsLoadedState extends DonorsState {
  final List<MyUser> users;

  const DonorsLoadedState({required this.users});

  @override
  List<Object?> get props => [users];
}

======== Exception caught by gesture ===============================================================
The following ProviderNotFoundException was thrown while handling a gesture:
Error: Could not find the correct Provider above this Test 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 Test is under your MultiProvider/Provider.
    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<Example>(
        create: (_) => Example(),
        // Will throw a ProviderNotFoundError, because `context` is associated
        // to the widget that is the parent of `Provider<Example>`
        child: Text(context.watch<Example>()),
      ),
    }
    

    consider using builder like so:

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

If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter

I am building an Widget "Test" to search users by their username. This is the widget Test with Bloc.

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

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => DonorsCubit(),
      child: BlocListener<DonorsCubit, DonorsState>(
        listener: (context, state) {
          print(state);
        },
        child: Scaffold(
          appBar: AppBar(),
          body: IconButton(
            onPressed: () {
              context.read<DonorsCubit>().searchDonors(searchKey: "masum");
            },
            icon: BlocBuilder<DonorsCubit, DonorsState>(
              builder: (context, state) {
                if (state is DonorsInitialState) return const Icon(Icons.add);
                if (state is DonorsLoadedState) return const Icon(Icons.done);
                if (state is DonorsLoadingState) return const Icon(Icons.circle);
                return const SizedBox();
              },
            ),
          ),
        ),
      ),
    );
  }
}

I used this cubit to manage states.

class DonorsCubit extends Cubit<DonorsState> {
  List<MyUser> users = <MyUser>[];
  final FirebaseDBRepo _firebaseDBRepo = FirebaseDBRepo();
  late StreamSubscription _streamSubscription;

  DonorsCubit() : super(DonorsInitialState()) {
    _streamSubscription =
        _firebaseDBRepo.usersStream().listen((List<MyUser> users) {
      this.users = users;
    });
  }

  void searchDonors({required String? searchKey}) {
    emit(DonorsLoadingState());
    List<MyUser> searchedUser = <MyUser>[];
    searchedUser.clear();
    if (searchKey == null) {
      emit(DonorsLoadedState(users: users));
    } else {
      for (MyUser user in users) {
        if (user.username!.toLowerCase().contains(searchKey.toLowerCase())) {
          searchedUser.add(user);
        }
      }
      emit(DonorsLoadedState(users: searchedUser));
    }
  }

  @override
  Future<void> close() {
    _streamSubscription.cancel();
    return super.close();
  }
}



abstract class DonorsState extends Equatable {
  const DonorsState();
}

class DonorsLoadingState extends DonorsState {
  @override
  List<Object> get props => [];
}

class DonorsInitialState extends DonorsState {
  @override
  List<Object> get props => [];
}

class DonorsLoadedState extends DonorsState {
  final List<MyUser> users;

  const DonorsLoadedState({required this.users});

  @override
  List<Object?> get props => [users];
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

苍风燃霜 2025-01-19 00:54:17

您遇到的问题与 provider 包的工作方式有关。为了访问肘,您应该在小部件树上方提供它。现在,您在相同的上下文中提供并聆听肘节。有几种方法可以处理它。

  1. 使用Builder小部件。
class Test extends StatelessWidget {
  const Test({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => DonorsCubit(),
      child: Builder(
        builder: (context) => BlocListener<DonorsCubit, DonorsState>(
          listener: (context, state) {
            print(state);
          },
          child: Scaffold(
            appBar: AppBar(),
            body: IconButton(
              onPressed: () {
                context.read<DonorsCubit>().searchDonors(searchKey: "masum");
              },
              icon: BlocBuilder<DonorsCubit, DonorsState>(
                builder: (context, state) {
                  if (state is DonorsInitialState) return const Icon(Icons.add);
                  if (state is DonorsLoadedState) return const Icon(Icons.done);
                  if (state is DonorsLoadingState)
                    return const Icon(Icons.circle);
                  return const SizedBox();
                },
              ),
            ),
          ),
        ),
      ),
    );
  }
}
  1. 将您的小部件分成两部分,并在父小部件中提供您的肘节:
class TestWrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => DonorsCubit(),
      child: const Test(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return BlocListener<DonorsCubit, DonorsState>(
      listener: (context, state) {
        print(state);
      },
      child: Scaffold(
        appBar: AppBar(),
        body: IconButton(
          onPressed: () {
            context.read<DonorsCubit>().searchDonors(searchKey: "masum");
          },
          icon: BlocBuilder<DonorsCubit, DonorsState>(
            builder: (context, state) {
              if (state is DonorsInitialState) return const Icon(Icons.add);
              if (state is DonorsLoadedState) return const Icon(Icons.done);
              if (state is DonorsLoadingState) return const Icon(Icons.circle);
              return const SizedBox();
            },
          ),
        ),
      ),
    );
  }
}

我是选项 2 的粉丝,因为更清楚的是您正在拆分代码并在不同的上下文中工作。

BONUS

您可以使用 BlocConsumer 小部件,而不是单独使用 BlocListenerBlocBuilder

class TestWrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => DonorsCubit(),
      child: const Test(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: IconButton(
        onPressed: () {
          context.read<DonorsCubit>().searchDonors(searchKey: "masum");
        },
        icon: BlocConsumer<DonorsCubit, DonorsState>(
          listener: (context, state) {
            print(state);
          },
          builder: (context, state) {
            if (state is DonorsInitialState) return const Icon(Icons.add);
            if (state is DonorsLoadedState) return const Icon(Icons.done);
            if (state is DonorsLoadingState) return const Icon(Icons.circle);
            return const SizedBox();
          },
        ),
      ),
    );
  }
}

The problem you get is related to how the provider package works. In order to access the cubit, you should provide it above in the widget tree. Now, you provide and listen to the cubit in the same context. There are several ways how you could handle it.

  1. Use the Builder widget.
class Test extends StatelessWidget {
  const Test({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => DonorsCubit(),
      child: Builder(
        builder: (context) => BlocListener<DonorsCubit, DonorsState>(
          listener: (context, state) {
            print(state);
          },
          child: Scaffold(
            appBar: AppBar(),
            body: IconButton(
              onPressed: () {
                context.read<DonorsCubit>().searchDonors(searchKey: "masum");
              },
              icon: BlocBuilder<DonorsCubit, DonorsState>(
                builder: (context, state) {
                  if (state is DonorsInitialState) return const Icon(Icons.add);
                  if (state is DonorsLoadedState) return const Icon(Icons.done);
                  if (state is DonorsLoadingState)
                    return const Icon(Icons.circle);
                  return const SizedBox();
                },
              ),
            ),
          ),
        ),
      ),
    );
  }
}
  1. Split your widget into two and provide your cubit in the parent widget:
class TestWrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => DonorsCubit(),
      child: const Test(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return BlocListener<DonorsCubit, DonorsState>(
      listener: (context, state) {
        print(state);
      },
      child: Scaffold(
        appBar: AppBar(),
        body: IconButton(
          onPressed: () {
            context.read<DonorsCubit>().searchDonors(searchKey: "masum");
          },
          icon: BlocBuilder<DonorsCubit, DonorsState>(
            builder: (context, state) {
              if (state is DonorsInitialState) return const Icon(Icons.add);
              if (state is DonorsLoadedState) return const Icon(Icons.done);
              if (state is DonorsLoadingState) return const Icon(Icons.circle);
              return const SizedBox();
            },
          ),
        ),
      ),
    );
  }
}

I am a fan of option 2 since it is more clear that you are splitting your code and working in separate contexts.

BONUS

Instead of using BlocListener and BlocBuilder separately, you could use the BlocConsumer widget:

class TestWrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => DonorsCubit(),
      child: const Test(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: IconButton(
        onPressed: () {
          context.read<DonorsCubit>().searchDonors(searchKey: "masum");
        },
        icon: BlocConsumer<DonorsCubit, DonorsState>(
          listener: (context, state) {
            print(state);
          },
          builder: (context, state) {
            if (state is DonorsInitialState) return const Icon(Icons.add);
            if (state is DonorsLoadedState) return const Icon(Icons.done);
            if (state is DonorsLoadingState) return const Icon(Icons.circle);
            return const SizedBox();
          },
        ),
      ),
    );
  }
}
流年已逝 2025-01-19 00:54:17

我有同样的问题,我使用 MultiProvider 列出我的提供程序,如下所示:

@override
Widget build(BuildContext context) {
  return MultiProvider(
    providers: [
      ChangeNotifierProvider(create: (_) => Example()),
    ],
    child: MaterialApp(
      title: 'Example',
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark().copyWith(
        textTheme: GoogleFonts.poppinsTextTheme(Theme.of(context).textTheme)
      ),
      // here I set my first screen...
      home: HomePage(),
    ),
  );
}

I have the same problem, I use the MultiProvider to list my providers like this:

@override
Widget build(BuildContext context) {
  return MultiProvider(
    providers: [
      ChangeNotifierProvider(create: (_) => Example()),
    ],
    child: MaterialApp(
      title: 'Example',
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark().copyWith(
        textTheme: GoogleFonts.poppinsTextTheme(Theme.of(context).textTheme)
      ),
      // here I set my first screen...
      home: HomePage(),
    ),
  );
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文