오늘은 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)
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 |