오류
Flutter로 사이드 프로젝트를 진행하며 기존의 버튼 위젯에 Provider 패턴을 적용해 보고 있었다.
ChangeNotifier을 Mixin하고 ChangeNotifierProvider을 이용하여 데이터를 생성, 사용 할 Container를 감싸주었다.
그 다음, ChangeNotifierProvider의 child에서 Provider.of(context)와 같은 형식으로 전달받은 데이터를 이용하려 하였는데, 다음과 같은 오류가 발생했다.
Error: Could not find the correct Provider above this HomeScreen Widget
원인
위 오류의 원인은 디버그 콘솔에 나타난 오류 메세지에서 쉽게 찾을 수 있었다.
먼저, 코드를 보면서 어떤식으로 Provider가 사용되었는지 살펴보자. 아래의 코드는 설명을 위해 만들어본 예시코드이다.
예시코드
코드를 간단히 설명해 보자면 아래와 같다.
- HomeScreen 위젯에서 ChangeNotifierPorivder을 사용하였고, ButtonModel을 데이터로 사용한다.
- provider의 child는 Column이고, Colum안에는 1개의 Container(100x100), 2개의 버튼이 존재한다.
- Column안의 요소들은 각각 Provider.of(context)와 같은 형식으로 데이터에 접근하려한다.
- 데이터 접근을 통해, 각 버튼이 클릭될 때 마다 Container의 색을 바꾸도록 작성된 코드이다.
오류 메세지 확인
이번에는 위의 코드에서 잘못된 부분을 찾기 위해 디버그 콘솔을 한 번 살펴보자.
❗ **Error: Could not find the correct Provider above this HomeScreen 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 HomeScreen 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>().toString()),
);
}
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, child) {
// No longer throws
return Text(context.watch<Example>().toString());
}
);
}
If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter
먼저, 오류 메세지 2번째 줄을 살펴보자.
This happens because you used a BuildContext that does not include the provider of your choice.
: 당신이 선택한 Provider을 포함하고 있지 않은 BuildContext를 사용했기 때문에 이 오류가 발생하였습니다.
라는 문장을 볼 수 있다. 또한, 그 밑에는 몇 가지의 오류가 발생했을 법한 경우가 나열되어있는데, 그 중 나에게 해당되는 것은 가장 마지막 시나리오였다.
You used a BuildContext that is an ancestor of the provider you are trying to read.
즉, 내가 Provider를 사용하며 넘겨준 context는 HomeScreen위젯 최상단의 Buildcontext이기 때문에 이 오류가 발생한 것이다.
정리하자면, 나는 HomeScreen위젯의 하위에 ChangeNotifierProvider을 작성하였음에도, 데이터를 사용할 때에는 Provider.of<>(context) 명령어로 HomeScreen의 context를 넘겨주었으니 해당 context에는 ChangeNotifierProvider에 관한 정보가 존재하지 않는 것이다.
해결법
해결방법은 디버그 콘솔의 예시를 따라하니 간단히 해결되었다.
그 방법은 바로 builder을 이용하는 것이다.
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());
}
);
}
이처럼 데이터 생성을 위해 Provider을 작성할 때, child가 아닌 builder을 사용하여 Provider의 정보가 포함된 context를 인자로 넘겨주면 문제를 해결 할 수 있다. 수정된 코드는 아래와 같다.
이처럼, builder를 사용하여 Provider의 정보가 포함된 BuildContext를 새로 넘겨주어 문제를 해결 할 수 있었다.
느낀점
기존에 Global key를 이용하여 비효율적으로 작성했던 코드를 개선하기 위해 Provider을 처음으로 사용해보았다.
그러나 Provider의 사용 예시를 따라하는데 너무 급급한 나머지 가장 기본적인 BuildContext를 고려하지 않아 이런 오류를 발생시킨 것 같다.
단순히 작동하는 코드를 짜는 것보다, 구조를 이해하고 코드를 작성하도록 노력해야겠다.
그리고 바로 검색하는 것 보다는 공식문서나 내부 코드를 찾아보는게 많은 도움이 될 것 같다.
'Dart & Flutter' 카테고리의 다른 글
[Error] Vertical viewport was given unbounded height / RenderBox was not laid out. (0) | 2023.04.04 |
---|---|
[Error] Use context before initState() completed (0) | 2023.04.02 |
[Flutter] Stateless Widgets (0) | 2023.03.21 |
[Dart 기초] Named parameter construtor (0) | 2023.02.01 |
[Dart 기초] Dart Null safety (0) | 2023.01.29 |