통신을 쉽게 관리하기 위해 Dart에서의 통신은 뷰 모델(ViewModel)을 사용합니다. (MVVM패턴)
ViewModel
은 Repository
를 통해 데이터를 가져오며, 앱의 상태를 관리합니다. 기본적으로 비동기 요청을 통해 데이터를 가져와서 UI를 업데이트합니다.아래의 예제는 Riverpod 라이브러리를 필요로 합니다.
View
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))),
Expanded(
child: ListView.builder(
itemCount: 4,
itemBuilder: (context, index) {
return ListTile(leading: Text("${index + 1}"), title: Text("내용"),);
},),
)
],
);
}
}
}
- ConsumerWidget: Riverpod 패키지에서 제공하는 위젯으로, 상태를 구독하고 빌드할 때 사용합니다.
WidgetRef
객체를 사용해서 해당 위젯이 구독하고 있는 상태에 접근할 수 있습니다.
- ref.watch(homeProvider): Riverpod을 통해
homeProvider
상태를 구독하는 부분입니다. 이 코드에서int?
타입이어야 하는one
변수를 받아옵니다. 이 변수는homeProvider
의 현재 상태를 나타냅니다.
- one == null:
one
변수가null
인 경우, 이는 아직 데이터가 로드되지 않았음을 의미합니다. 이때,CircularProgressIndicator()
를 보여주며 로딩 중임을 사용자에게 알립니다.
- Column: 데이터가 로드된 후,
one
변수는null
이 아니게 됩니다. 이때Column
위젯을 사용하여 여러 자식을 수직으로 배치합니다. - Text("${one}"):
one
값을 표시하는Text
위젯입니다. 이 숫자를 큰 폰트 크기로 표시합니다. - Expanded: 남은 화면 공간을 차지하는
ListView
위젯을 포장합니다. - ListView.builder: 제공한
itemCount
만큼ListTile
을 생성합니다.
Provider (VM)
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();
state = one;
}
}
// 위의 내용은 실제로 통신을 한 것이 아닌, 통신을 한 것과 유사하게 작성한 내용입니다.
- homeProvider: 이 부분에서는
NotifierProvider
를 사용하여HomePageVM
클래스의 인스턴스를 제공합니다.NotifierProvider
는 Riverpod에서 제공해주는 상태 관리 도구입니다. 상태로int?
를 가지고 있으며, 이는 초기에는null
이 될 수 있음을 의미합니다.
- HomePageVM 클래스:
- Notifier<int?>: 이 클래스는
Notifier
를 확장하며, 이는 Riverpod에서 제공하는 상태 관리의 기본 단위입니다. - HomeRepository repo:
HomeRepository
클래스의 인스턴스를 생성합니다. 이 클래스는 데이터를 가져오는 역할을 합니다. - @override int? build():
build
메서드는Notifier
클래스에서 반드시 구현해야 하는 메서드로, 초기 상태를 설정하는 역할을 합니다. 이 메서드 안에서getOne()
메서드를 호출하여 비동기적으로 데이터를 가져옵니다.build
메서드는 기본적으로 상태를null
로 초기화합니다. - Future<void> getOne() async: 이 메서드는 비동기적으로 동작하며
HomeRepository
에서 값을 가져오는 역할을 합니다.await
을 통해repo.getOne()
을 호출하여 결과를one
변수에 저장합니다. 그리고state
변수를one
값으로 업데이트하여Notifier
의 상태를 변경합니다.
Repository
// SRP : 데이터를 가져오는 곳 (휴다폰 디바이스(파일), 휴대폰 DB, Firebase(외부서버), 내서버, 공공데이터서버)
class HomeRepository {
const HomeRepository();
// 비동기 요청에서 return 타입은 항상 Future를 붙여야 한다. void의 경우는 Future<void>
Future<List<int>> getList() async {
List<int> response = await Future.delayed(Duration(seconds: 3), () {
// 3초후에 실행됨
return [1, 2, 3, 4];
},);
return response;
}
Future<int> getOne() async {
int response = await Future.delayed(Duration(seconds: 3), () {
// 3초후에 실행됨
return 5;
},);
return response;
}
}
- HomeRepository 클래스:
const
생성자를 사용하여 클래스의 인스턴스를 상수로 만듭니다. 이는 불변 객체(immutable object)를 생성하여 메모리 효율을 높일 수 있습니다.
주요 메서드 설명:
- Future<List<int>> getList():
- 비동기적으로 작동하며
Future<List<int>>
을 반환합니다. await Future.delayed
를 사용하여 3초 후에 실행됩니다.- 이 메서드는
[1, 2, 3, 4]
리스트를 반환합니다.
- Future<int> getOne():
- 비동기적으로 작동하며 `Future<int>을 반환합니다.
await Future.delayed
를 사용하여 3초 후에 실행됩니다.- 이 메서드는
5
를 반환합니다.
요약
통신이 완료되기 전에는 state 값이 null 이기 때문에 돌아가는 원 모양의 뷰를 확인할 수 있고,
통신이 완료된 후에는 정상적으로 의도한 컬럼요소가 출력될 것 입니다.
통신 완료 전 (3초 이전) :

통신 완료 후 (3초 후) :

Share article