[플러터] 16. Riverpod로 상태 관리(2)

KangHo Lee's avatar
Dec 31, 2024
[플러터] 16. Riverpod로 상태 관리(2)

1. Flutter에 쓰는 Repository

💡
Flutter에서 Repository의 책임은 데이터를 가져오는 클래스
  • 휴대폰 디바이스(파일), 휴대폰 DB, Firebase(외부서버), 내 서버, 공공데이터서버
home_repository.dart
class HomeRepository { const HomeRepository(); // 1개로 관리하기 위한 const Future<List<int>> getList() async { List<int> response = await Future.delayed(Duration(seconds: 3), () { return [1, 2, 3, 4, 5, 6]; }); return response; } Future<int> getOne() async { int response = await Future.delayed(Duration(seconds: 3), () { return 5; }); return response; } }

Future

  • 자바스크립트의 Promise와 유사한 객체
  • await Future.delayed
    • 통신으로 데이터를 받는 것처럼 테스트하기 위해 3초 뒤에 실행되도록 설정했습니다.
    • 비동기로 만들어야 하기 때문에 await를 추가합니다.
  • async 함수
    • 반환형을 Future<T>로 해야 합니다.
    • return이 없으면 Future<void>로 적습니다.

2. Future로 데이터 받기

future_body.dart
class FutureBody extends StatelessWidget { HomeRepository repo = const HomeRepository(); @override Widget build(BuildContext context) { return Column( children: [ FutureBuilder( future: repo.getList(), builder: (context, snapshot) { if (snapshot.hasData) { return Expanded( child: ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (context, index) { return ListTile( leading: Text("${index + 1}"), title: Text("내용")); }), ); } else { // 로딩 중을 시각적으로 보여주는 위젯 return Center(child: CircularProgressIndicator()); } }, ), ], ); } }

FutureBuilder

  • future: 받을 데이터
  • builder: 그림 그릴 내용
    • snapshot.hasData → 데이터가 not null일 때 true가 됩니다.
    • snapshot.data → 받아온 데이터입니다.
💡
null 처리를 위한 연산자 !?
  • ! 는 해당 변수가 null이 아니라고 확신할 때 사용합니다.
  • ? 는 해당 변수가 null일 수도 있을 때 사용합니다.
  • CircularProgressIndicator
    • 로딩 중을 원으로 보여주는 위젯입니다.

3. Riverpod으로 데이터 받기

home_body.dart
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mockapp/home_page_vm.dart'; class HomeBody extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { int? one = ref.watch(homeProvider); if (one == null) { return Center(child: CircularProgressIndicator()); } else { return Column( children: [ Center(child: Text("$one", style: TextStyle(fontSize: 50))), ], ); } } }
home_page_vm.dart
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mockapp/home_repository.dart'; final homeProvider = NotifierProvider<HomePageVM, int?>(() { return HomePageVM(); }); class HomePageVM extends Notifier<int?> { HomeRepository repo = const HomeRepository(); @override int? build() { // 상태 초기화 시작 getOne(); // 통신 완료 전 상태 null로 초기화 return null; } Future<void> getOne() async { int one = await repo.getOne(); // View는 상태값을 리턴 받을 필요가 없다. 보고 있기만 하면 된다.(watch) state = one; } }
  • async getOne()은 1번은 실행되어야 하므로 View인 ConsumerWidget 보다 ViewModel(VM)에서 초기화 하는 것이 깔끔합니다.
  • VM의 장점은 View가 상태값을 받을(return)할 필요가 없습니다.
    • WidgetRef.watch(homeProvider) → provider를 통해 VM을 관찰
 
Share article

devleekangho