본문 바로가기

Flutter/07 State - Provider

[Flutter] Could not find the correct Provider

오늘은 Provider 오류중 흔하게 발생하는 "Could not find the correct Provider" 에 대해서 정리했습니다.

개발환경 : 윈도우11, 안드로이드 스튜디오(Arctic Fox 2020.3.1 Patch 4), flutter 2.10

오류가 있는 원본 소스코드 - Release provider_context_error_org · mike-bskim/provider_overview (github.com)

 

Release provider_context_error_org · mike-bskim/provider_overview

 

github.com

 

 

Provider 를 구현한 위젯에서 바로 Provider 를 호출하는 경우 발생한다.

 

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    debugPrint('MyHomePage >> build');

    return Scaffold(
      appBar: AppBar(
        title: const Text('My Counter'),
      ),
      body: ChangeNotifierProvider<Counter>( // provider 삽입
        create: (context) => Counter(),
        child: Center(
          child: Container(
            color: Colors.grey,
            padding: const EdgeInsets.all(16.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                Text(
                  Provider.of<Counter>(context).titleMyHomePage, // provider 호출
                  style: const TextStyle(fontSize: 24.0),
                ),
                const SizedBox(height: 20.0),
                const CounterA(),
                const SizedBox(height: 20.0),
                const Middle(),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

 


 

자세한 오류 내용은 "더보기" 클릭

더보기

======== Exception caught by widgets library =======================================================
The following ProviderNotFoundException was thrown building MyHomePage(dirty, state: _MyHomePageState#489ff):
Error: Could not find the correct Provider<Counter> above this MyHomePage 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 MyHomePage is under your MultiProvider/Provider<Counter>.
  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

 


 

여기서 문제를 해결하는 방법은 2가지가 있습니다. 새로운 위젯으로 감싸는 방법과 Builder 위젯을 사용하는 방법입니다.문제의 핵심은 내가 가지고 있는 context 는 내 부모의 context 이므로 

"Provider.of<Counter>(context).titleMyHomePage"(a) 가 호출될때 여기서 사용되는 context 는 부모의 위젯을 참고합니다. 그래서 부모위젯으로 올라갔더니, "ChangeNotifierProvider<Counter>"(b) 를 찾을수 없다는 얘기입니다.

사실 2가지 이상의 해결방법이 있습니다. 핵심 사항은 새로운 context 를 만들어서  a, b 를 분리시키는 것입니다. 

새로운 위젯을 만들던지, Builder 로 감싸면 새로운 context 가 생성됩니다. 그래서 Consumer 로 감싸도 문제를 해결할 수 있습니다.

 

우선 builder 위젯 예시입니다.

 

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    debugPrint('MyHomePage >> build');

    return Scaffold(
      appBar: AppBar(
        title: const Text('My Counter'),
      ),
      body: ChangeNotifierProvider<Counter>(
        create: (context) => Counter(),
        child: Builder(                         // 이렇게 추가했습니다.
          builder: (context) {                  // 여기서 새로운 context 가 생성됩니다.
            return Center(
              child: Container(
                color: Colors.grey,
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    Text(
                      Provider.of<Counter>(context).titleMyHomePage,
                      style: const TextStyle(fontSize: 24.0),
                    ),
                    const SizedBox(height: 20.0),
                    const CounterA(),
                    const SizedBox(height: 20.0),
                    const Middle(),
                  ],
                ),
              ),
            );
          }
        ),
      ),
    );
  }
}

 

두번째는 새로운 위젯을 만드는것입니다. Refactor 메뉴에서 Extract Flutter Widget 으로 하면 됩니다.

 

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    debugPrint('MyHomePage >> build');

    return Scaffold(
      appBar: AppBar(
        title: const Text('My Counter'),
      ),
      body: ChangeNotifierProvider<Counter>(
        create: (context) => Counter(),
        child: const NewWidget(),                 // 이렇게 위젯으로 처리합니다.
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {            // 여기서 새로운 context 가 생성됩니다.
    return Center(
      child: Container(
        color: Colors.grey,
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Text(
              Provider.of<Counter>(context).titleMyHomePage,
              style: const TextStyle(fontSize: 24.0),
            ),
            const SizedBox(height: 20.0),
            const CounterA(),
            const SizedBox(height: 20.0),
            const Middle(),
          ],
        ),
      ),
    );
  }
}

 

Consumer 사용할때 builder 인자가 있으므로 동일한 결과입니다.

 

 

 

 

'Flutter > 07 State - Provider' 카테고리의 다른 글

[Flutter] Provider Access - Named route  (0) 2022.05.06
[Flutter] Provider Access - Anonymous route  (0) 2022.05.06
[Flutter] Consumer, Selector  (0) 2022.05.05
[Flutter] ChangeNotifierProvider  (0) 2022.05.04
[Flutter] setState  (0) 2022.05.04