-
[FLUTTER] 상태 관리 패키지 (Provider) 적용하기Flutter 2023. 4. 9. 18:46
[버킷 리스트 조회 (Read) 기능]
: BucketService에 있는 bucketList 값을 가져와서 HomePage에 보여줘야 하는데, Consumer 위젯을 이용하면 위젯트리 꼭대기에 등록된 BucketService에 접근할 수 있다.
Consumer<BucketService>( // 찾고자하는 클래스의 이름을 적어주면 됨 (BucketService) builder: (context, bucketService, child) { // 받을 변수의 이름 (마음대로 바꿔도 상관없음) : bucketService return 위젯; } )
- Consumer<BucketService> : 위젯트리 상단에서 찾아올 클래스 이름을 <>사이에 적는다.
- builder: (context, bucketService, child) { return 위젯; } : 화면에 보여줄 위젯을 반환하는 함수로, 위젯트리 상단에서 찾아온 클래스를 두 번째 파라미터로 받을 수 있다.
Consumer<BucketService>는 위젯 트리를 타고 올라가 Provider로 등록된 BucketService를 찾고, 찾은 BucketService를 bucketService라는 이름을 가진 두 번째 파라미터로 전달해 준다.
BucketService에서 값이 변경되는 경우, StatefulWidget의 setState와 같이 notifyListeners();를 호출하는데, 이 때 해당 서비스를 Consumer로 등록된 모든 위젯의 builder 함수가 재호출 되면서 화면이 갱신된다.
class HomePage extends StatelessWidget { const HomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Consumer<BucketService>(builder: (context, bucketService, child) { // bucketService로 부터 bucketList 가져오기 List<Bucket> bucketList = bucketService.bucketList; return Scaffold( appBar: AppBar( title: Text("버킷 리스트"), ), body: Center(child: Text("버킷 리스트를 작성해 주세요.")), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: () { // + 버튼 클릭시 버킷 생성 페이지로 이동 Navigator.push( context, MaterialPageRoute(builder: (_) => CreatePage()), ); }, ), ); }); } }
bucketList 보여주기
: bucketList를 HomePage가 아닌 BucketService 가져오는 점에서 이전 프로젝트와 다르다!
body: bucketList.isEmpty ? Center(child: Text("버킷 리스트를 작성해 주세요.")) : ListView.builder( itemCount: bucketList.length, // bucketList 개수 만큼 보여주기 itemBuilder: (context, index) { var bucket = bucketList[index]; // index에 해당하는 bucket 가져오기 return ListTile( // 버킷 리스트 할 일 title: Text( bucket.job, style: TextStyle( fontSize: 24, color: bucket.isDone ? Colors.grey : Colors.black, decoration: bucket.isDone ? TextDecoration.lineThrough : TextDecoration.none, ), ), // 삭제 아이콘 버튼 trailing: IconButton( icon: Icon(CupertinoIcons.delete), onPressed: () { // 삭제 버튼 클릭시 }, ), onTap: () { // 아이템 클릭시 }, ); }, ),
[버킷 리스트 생성 (Create) 기능]
: 기존과 달리 CreatePage와 HomePage 간 데이터를 주고받지 않고, CreatePage에서 곧 바로 BucketService에 job을 넘겨주는 방식으로 구현
BucketService에 Bucket을 추가하는 함수를 구현
- bucket_service.dart
/// bucket 추가 void createBucket(String job) { bucketList.add(Bucket(job, false)); }
: BucketService에 bucketList에 대한 CRUD 기능을 담당하는 함수를 추가할 예정, 그 중 Create를 담당하는 createBucket 함수를 생성
CreatePage에서 추가하기 버튼 클릭시, BucketService에 방금 만든 createBucket함수를 호출
추가하기 버튼을 누를 때 기존에는 job을 Navigator.pop(context, job);을 이용해서 HomePage로 넘겨주었지만 이번에는 BucketService 에 접근해서 createBucket() 함수로 job을 넘겨주어 버킷 리스트를 추가!
context.read<클래스명>();를 이용하면 위젯 트리 상단에 있는 Provider로 등록한 클래스에 접근할 수 있다.
// BucketService 가져오기 BucketService bucketService = context.read<BucketService>(); bucketService.createBucket(job);
- main
/// 버킷 생성 페이지 class CreatePage extends StatefulWidget { const CreatePage({Key? key}) : super(key: key); @override State<CreatePage> createState() => _CreatePageState(); } class _CreatePageState extends State<CreatePage> { // TextField의 값을 가져올 때 사용합니다. TextEditingController textController = TextEditingController(); // 경고 메세지 String? error; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("버킷리스트 작성"), // 뒤로가기 버튼 leading: IconButton( icon: Icon(CupertinoIcons.chevron_back), onPressed: () { Navigator.pop(context); }, ), ), body: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ // 텍스트 입력창 TextField( controller: textController, autofocus: true, decoration: InputDecoration( hintText: "하고 싶은 일을 입력하세요", errorText: error, ), ), SizedBox(height: 32), // 추가하기 버튼 SizedBox( width: double.infinity, height: 48, child: ElevatedButton( child: Text( "추가하기", style: TextStyle( fontSize: 18, ), ), onPressed: () { // 추가하기 버튼 클릭시 String job = textController.text; if (job.isEmpty) { setState(() { error = "내용을 입력해주세요."; // 내용이 없는 경우 에러 메세지 }); } else { // 사용자가 글을 입력한 경우에 누르는 시점에 실행되는 부분 setState(() { error = null; // 내용이 있는 경우 에러 메세지 숨기기 }); // BucketService 가져오기 BucketService bucketService = context.read<BucketService>(); bucketService.createBucket(job); Navigator.pop(context); // 화면을 종료합니다. } }, ), ), ], ), ), ); } }
- bucket_service.dart
import 'package:flutter/material.dart'; import 'main.dart'; /// Bucket 담당 class BucketService extends ChangeNotifier { // 변경된사항을 알려준다 (notifier) List<Bucket> bucketList = [ Bucket('잠자기', false), // 더미(dummy) 데이터 ]; /// bucket 추가 void createBucket(String job) { // create화면에서 추가하기 버튼을 누를때 호출할것 bucketList.add(Bucket(job, false)); notifyListeners(); // 갱신 = Consumer<BucketService>의 builder 부분만 새로고침 } }
: bucket_service.dart에 notifyListeners();를 붙여주지않으면 생성되지않는다.
why? job을 추가 했지만, HomePage에 아무런 변화가 없는 이유는 BucketService에 신규 버킷이 추가 되었지만 HomePage 화면이 갱신되지 않았기 때문!
→ BucketService에서 notifyListeners();를 호출하면 모든 Consumer<BucketService>의 builder 파라미터의 함수를 호출해 화면을 갱신해 준다!
이 기능은 BucketService가 ChangeNotifier 클래스 기능을 그대로 물려받고, main.dart 11번째 줄에 Provider로 등록시 변경사항 알람을 보낼 수 있는 ChangeNotifierProvider로 등록 된 경우에만 이용할 수 있다!
- main.dart
- bucket_service.dart
[버킷 리스트 수정 (Update) 기능]
: 버킷의 상태가 변하는 경우 화면을 갱신하기 위해 notifyListeners()를 호출
/// bucket 수정 void updateBucket(Bucket bucket, int index) { // index에 해당되는 bucketList에 전달받은 bucket을 넣는 방식으로 update bucketList[index] = bucket; // 버킷의 상태가 변하는 경우 화면을 갱신하기 위해 notifyListeners()를 호출 notifyListeners(); }
ListTile을 클릭하는 경우, isDone 상태를 변경
bucket.isDone = !bucket.isDone; bucketService.updateBucket(bucket, index);
- main
: 아이템을 클릭했을때, 선이 그어지도록 (다시 클릭하면 지워지도록 - Toggle)
onTap: () { // 아이템 클릭시 bucket.isDone = !bucket.isDone; bucketService.updateBucket(bucket, index); },
[버킷 리스트 삭제 (Delete) 기능]
: 버킷을 삭제하는 경우, 화면이 갱신되어야 하므로 notifyListners();를 호출
/// bucket 삭제 void deleteBucket(int index) { bucketList.removeAt(index); notifyListeners(); }
- main
: 삭제 아이콘 버튼을 눌렀을경우 해당 index 데이터 삭제!
// 삭제 아이콘 버튼 trailing: IconButton( icon: Icon(CupertinoIcons.delete), onPressed: () { // 삭제 버튼 클릭시 bucketService.deleteBucket(index); }, ),
'Flutter' 카테고리의 다른 글
[Flutter] DT_TOOLCHAIN_DIR cannot be used to evaluate LIBRARY_SEARCH_PATHS, use TOOLCHAIN_DIR instead 해결방법 (Xcode 15 Firebase SDK 적용) (0) 2023.09.30 [FLUTTER] 상태 관리 패키지 (Provider) 사용법 정리 (0) 2023.04.09 [FLUTTER] 상태 관리 패키지 (Provider) 준비하기 (0) 2023.04.07 [FLUTTER] 리펙토리(Refactory) (0) 2023.04.07 [FLUTTER] 비동기처리 (async와 await) -화면 간 데이터 전달 (0) 2023.04.07