## 해당 TIL은 주어진 과제를 수행하면서 얻은 학습 내용과, 시행착오 등등을 종합해서 작성한것임
1. RiverPod 메서드 동기적으로 작동시키기
해당 코드는 IconButton을 눌렀을때 로딩 트리거를 발생시키고, 현재 위치의 검색하는 로직이다.
IconButton(
onPressed: () {
ref.read(homeViewModelProvier.notifier).startLoading();
ref.read(homeViewModelProvier.notifier).searchCurrentLocation();
},
icon: Icon(Icons.gps_fixed),
),
여기서 추가로 구현하고자 하는 것은, 해당 버튼을 눌렀을때, 현재 위치가 검색된 후
TextField의 text가 바뀌도록 하는것이다.
** ( 현재 TextField의 controller.text는 homeState와 연결되지 않은 상태 - 일부러인데, 매번 값을 입력할 때마다 onChange를 통한 상태 업데이트를 해주는것이 손해라고 생각했기 때문에...)
여기서 중요한것은, searchCurrentLocation 메서드는 비동기 함수로써, 현재 위치를 가져오는데 시간이 걸린다는 것이었다.
< 첫번째 시도 >
IconButton(
onPressed: () {
ref.read(homeViewModelProvier.notifier).startLoading();
ref.read(homeViewModelProvier.notifier).searchCurrentLocation();
_searchController.text = homeState.controllerText;
},
icon: Icon(Icons.gps_fixed),
),
해당 코드는 정상적으로 작동하지 않았는데, 그 이유는 위의 코드들이 동기적으로(순차적으로) 작동하지 않기 때문이었다.
아니, 사실 위에서 코드는 동기적으로 작동하나, 그건 ref.read 부분만 그런것이었다.
실제로 ref.read는 각각 "~ 메서드를 실행시켜라!" 라는 명령만 내리는 코드이기 때문에 발생하는 오해였다.
즉, 뒤의 startLoading과 searchCurrentLocation은 비동기적 작동을 하고 있으므로,
_searchController.text = homeState.controllerText 부분이 실행될때는 아직 해당 값이 업데이트 되지 않은 상태였던 것이다.
< 두번째 시도 >
IconButton(
onPressed: () async {
await ref.read(homeViewModelProvier.notifier).startLoading();
await ref.read(homeViewModelProvier.notifier).searchCurrentLocation();
_searchController.text = homeState.controllerText;
},
icon: Icon(Icons.gps_fixed),
),
첫번째 시도를 발판삼아 억지로 동기적으로 해보려는 시도였으나,
해당 코드는 오류 범벅이다.
왜냐하면, 지금 사용되는 ref.read 는 동기적인 함수이며, 동기적으로 상태를 반환하기 때문이었다.
즉, await를 사용할 수 없는 코드라는 뜻이다.
그럼 어떻게 searchCurrentLocation이 끝나고 난뒤 text가 업데이트 되도록 해야 하는 걸까?
< 세번째 시도 >
IconButton(
onPressed: () async {
await ref.read(homeViewModelProvier.notifier).startLoading();
await ref.read(homeViewModelProvier.notifier)
.searchCurrentLocation()
.then((_){
_searchController.text = homeState.controllerText;
})
},
icon: Icon(Icons.gps_fixed),
),
.then을 사용하는 방법이다.
searchCurrentLocation 메서드에 .then을 넣어줌으로써, 해당 비동기 작업이 끝날때 then 안의 작업이 실행되도록 하는 방식이다.
해당 방법은 await를 사용할 수 없는 코드에서 반드시 순차적으로 작업이 이어져야 할때 사용되는 방식이다.
그러나 결과적으로 이 코드도 정상작동하지 않았다.
그 이유는 다름아닌, homeState.controllerText가 업데이트 되지 않은 값을 가져오기 때문이었다.
이는 .then이 작동되는 타이밍과 ref.watch의 작동방식에 대한 문제였다.
searchCurrentLocation 메서드를 살펴보면,
// searchCurrentLocation() 마지막 부분
state = HomeState(response, false, query);
이와 같이 UI 까지 업데이트하고 있는데, .then의 경우 그 이후에 이루어지는 로직이다.
간단히 설명해 보면,
원래 text가 A 라는 값을 가지고 있는 와중, searchCurrentLocation으로 B라는 값을 받아온다고 하자.
searchCurrentLocation메서드가 작동되면, text의 상태를 B로 바꾸어 주게 된다. 다만, 현재 UI의 text는 A 그대로인 상태.
( 현재 UI를 그리는 부분에 text를 watch와 연결해 주지 않은 상태인데, TextField의 특징으로 인해 의도한 바라서..)
이 상태에서 .then을 통해 _searchController.text = homeState.controllerText을 수행하게 되는데,
UI의 text는 B로 바뀌게 되었으나, 상태가 변경된 것이 아니므로, ref.watch는 업데이트를 수행하지 않는 현상이 발생한다.
따라서 사용자가 보이는 UI text는 A 로 보이게 되는것이다!!!
< 네번째 시도 >
IconButton(
onPressed: () async {
await ref.read(homeViewModelProvier.notifier).startLoading();
await ref.read(homeViewModelProvier.notifier)
.searchCurrentLocation()
.then((_){
final text = ref.read(homeViewModelProvier).controllerText;
_searchController.text = text;
})
},
icon: Icon(Icons.gps_fixed),
),
따라서 .then 이후 한번더 read를 받아서 화면을 재 업데이트 함으로써, 해당 문제를 해결하게 되었다!
'TIL' 카테고리의 다른 글
TIL - 지역 검색 앱 (3) <AlertDialog의 context, 파이어스토리지에 에셋이미지 업로드하기> (0) | 2024.12.04 |
---|---|
TIL - 지역 검색 앱 (2) <파이어베이스 TimeStamp, 다트 DateTime> (0) | 2024.12.03 |
Flutter 이주의 위젯 탐방 (5) - FadeInImage, StreamBuilder, InheritedModel (0) | 2024.11.27 |
TIL - SirenOrder(팀프로젝트) (2) <애니메이션 기초, precacheImage> (0) | 2024.11.25 |
TIL - SirenOrder(팀프로젝트) (1) <이미지피커, 텍스트필드 포커싱> (0) | 2024.11.22 |