작성
·
1.1K
·
수정됨
0
안녕하세요~ 덕분에 클린 아키텍쳐 구조 잘 공부했습니다.
혹시 의존성 주입부분에서 질문이 있습니다.
제가 개인적으로 연습을 하면서 클린 아키텍쳐를 적용하고 있습니다.
/di/provider_setup.dart 에서 한번에 의존성 주입
List<ChangeNotifierProvider> getProviders() {
final dio = Dio();
SongRepository repository = SongRepository(dio);
UseCases useCases = UseCases(
getSearchSong: GetSearchSongUseCase(repository: repository),
getSearchSinger: GetSearchSingerUseCase(repository: repository),
getRecentlySongsList: GetRecentlySongsListUseCase(repository:repository),
);
SearchViewModel searchViewModel = SearchViewModel(useCases: useCases);
HomeViewModel homeViewModel = HomeViewModel(useCases: useCases);
return [
ChangeNotifierProvider(create: (_) => searchViewModel),
ChangeNotifierProvider(create: (_) => homeViewModel),
];
}
main 에서 주입
void main() {
// provider 호출
final providers = getProviders();
runApp(
MultiProvider(
providers: providers,
child: const MyApp(),
),
);
}
context.watch<SearchViewModel>(); 은 잘 작동해서 뷰에 출력을 잘 하고있습니다.
class _SearchScreenState extends State<SearchScreen> {
@override
Widget build(BuildContext context) {
final searchViewModel = context.watch<SearchViewModel>();
final state = searchViewModel.state;
...
}
context.watch<HomeViewModel>();은 에러가 발생합니다.
class _Body extends StatelessWidget {
const _Body({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final homeViewModel = context.watch<HomeViewModel>();
final state = homeViewModel.state;
...
}
에러내용
======== Exception caught by widgets library =======================================================
The following ProviderNotFoundException was thrown building _Body(dirty):
Error: Could not find the correct Provider<HomeViewModel> above this _Body 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 _Body is under your MultiProvider/Provider<HomeViewModel>.
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
The relevant error-causing widget was:
When the exception was thrown, this was the stack:
#0 Provider._inheritedElementOf (package:provider/src/provider.dart:343:7)
#1 Provider.of (package:provider/src/provider.dart:293:30)
#2 WatchContext.watch (package:provider/src/provider.dart:693:21)
#3 _Body.build (package:what_do_you_want_to_sing/presentation/home/home_screen.dart:79:35)
#4 StatelessElement.build (package:flutter/src/widgets/framework.dart:4949:49)
#5 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4878:15)
#6 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#7 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2667:19)
#8 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
#9 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:378:5)
#10 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1175:15)
#11 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1104:9)
#12 SchedulerBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:881:7)
(elided 4 frames from class _RawReceivePortImpl, class _Timer, and dart:async-patch)
저번에 이런 에러가 발생했을때, 의존성 주입이 안된 상태에서 context.watch() 를 해서 오류가 나 의존성을 추가해 해결했습니다.
하지만 이번에는 잘모르겠습니다. 의존성도 잘 주입되어서 view 단에서 잘 호출 하고 있는거 같은데.. 어떻게 해결해야할까요?
답변 3
0
해결했습니다! Provider 못찾는 문제라고 말씀주셔서
Provider 생성및 선언해주는 부분을 잘 찾아서 해보니
getProviders() 함수의 리턴 해주는 배열에 각각 타입을 넣어주니 호출하는 부분에서 Provider 를 못찾는다는 에러가 해결되었습니다.
타입을 안넣어주니 각 Provider 를 못찾는 것이였습니다.
List<ChangeNotifierProvider> getProviders() {
final dio = Dio();
SongRepository repository = SongRepository(dio);
UseCases useCases = UseCases(
getSearchSong: GetSearchSongUseCase(repository: repository),
getSearchSinger: GetSearchSingerUseCase(repository: repository),
getRecentlySongsList: GetRecentlySongsListUseCase(repository: repository),
);
SearchViewModel searchViewModel = SearchViewModel(useCases: useCases);
HomeViewModel homeViewModel = HomeViewModel(useCases: useCases);
return [
// 타입추가
ChangeNotifierProvider<SearchViewModel>(create: (_) => searchViewModel),
// 타입추가
ChangeNotifierProvider<HomeViewModel>(create: (_) => homeViewModel),
];
}
0
답변 주셔서 감사합니다.
제가 선생님 강의를 공부 하면서 적용한 방법은
provider_setup.dart 에서 의존성 선언 및 생성해주고
main.dart 에서 MultiProvider 로 Provider 여러개 주입시켜주고
home_screen.dart 에서는 주입시켜준 Provider를 찾아서 상태에 접근한다. 라고 생각하며 코드를 작성했습니다!
main.dart 입니다.
void main() {
// provider 호출
final providers = getProviders();
runApp(
MultiProvider(
providers: providers,
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'title',
home: HomeScreen(),
);
}
}
homeScreen 입니다.
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return DefaultLayout(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const SearchScreen()));
},
backgroundColor: Colors.black,
child: const Icon(Icons.search_rounded),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: const [
_Header(),
SizedBox(height: 8.0),
_Body(),
SizedBox(height: 8.0),
],
),
),
);
}
}
// _Header 코드
// _Body 코드
class _Body extends StatelessWidget {
const _Body({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final homeViewModel = context.watch<HomeViewModel>();
final state = homeViewModel.state;
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
final data = state.recentlyList[index];
return ListCard(data: data);
},
);
}
검색 페이지에는 이미 Provider 를 주입해서 잘 출력하고있습니다.
하지만 homeScreen 클래스에서는 Provider 를 찾지를 못하네요.. 무엇이 잘못 되었을까요?
context 가 중첩되서 그렇습니다. 대표적인 안티패턴 코드인데요.
아마 Builder 위젯으로 감싸면 될 건데 오히려 코드가 복잡해 집니다.
_Body StatelessWidget 대신 Widget을 리턴하는 일반 헬퍼 함수로 변경하시거나,
_Body 는 ViewModel에 접근하지 않고 생성자를 통해서 homeViewModel.state 를 전달받도록 수정하시면 됩니다.
HomeViewModel 은 HomeScreen 에서만 접근하도록이요.
0
Provider를 찾지 못하고 있군요.
_Body 까지의 위젯 Tree 가 어떻게 되실까요?
일반적인 구조라면 문제가 없어야 하는데, 혹시 좀 특이한 구조가 있거나 하진 않을까요?
해결하셨다니 다행입니다.