[Flutter] flutter_riverpod 상태 관리 라이브러리

만들면서 배우는 플러터 앱 프로그래밍
최재원's avatar
Jul 30, 2025
[Flutter] flutter_riverpod 상태 관리 라이브러리

프로젝트 코드

1. 라이브러리 설치

notion image
notion image
notion image

리버팟 라이브러리 설치

flutter_riverpod: ^2.6.1

전용 스니펫 플러그인 설치

notion image
해당 플러그인 설치 후 기존 스니펫 작동 안될 경우 위 확인

2. riverpod 코드 작성

창고 만드는 법

파일명은 마지막에 “_vm” 넣기

notion image

리버팟 창고 코드

import 'package:flutter_riverpod/flutter_riverpod.dart'; // 1. 창고 데이터 타입(int 면 안 만들어도 됨) // 관리 타입은 1가지만 가능함 여러개면 클래스 타입 안에 넣어서 사용함 // 2. 창고 // <> <- 내부에는 관리하고 싶은 type 을 작성하면 됨 class HomeVM extends Notifier<int> { // 창고가 만들어질 때 초기화 메서드(return 하는 값을 창고가 state 로 관리함) @override int build() { print("창고 빌드 후 상태 초기화"); return 1; } void increase() { state++; // setState 가 필요하지 않음. state 가 변화 되면 알아서 알려줌 } } // 3. 창고 관리자 <창고이름, 관리타입> final homeProvider = NotifierProvider<HomeVM, int>(() { print("창고 생성"); return HomeVM(); }); // 창고 만드는 법 // 창고 구독하는 법 - read or watch // 창고를 업데이트하는 법
  1. 창고에 사용될 데이터 타입을 작성한다
  1. 창고 클래스를 생성한다
    1. Notifier 를 상속 받는다
    2. 제네릭에 넣을 타입은 사용될 데이터 타입을 넣는다
  1. 창고 관리자 생성
    1. 창고 클래스를 리턴하는 익명함수를 넣어 줘야 한다

최상위 위젯을 감싸야 한다

void main() { // 1. 상태관리 라이브러리 적용 // Adding ProviderScope enables Riverpod for the entire project runApp(const ProviderScope(child: MyApp())); }
  • 리버팟이 모든 위젯을 관리 할 수 있도록…

창고 구독하는 법

notion image

read

watch

  • state가 변화하면 model 변수에 변화된 state 값을 계속 받는다
  • 구독하고 있는 state 가 변화하면 rebuild 한다
class HeaderPage extends ConsumerWidget { HeaderPage(); @override Widget build(BuildContext context, WidgetRef ref) { // 모든 provider에게 접근할 수 있는 변수 ref print("header 빌드"); // state를 받는 변수를 model로 사용함 // 이 watch 하면 창고가 만들어짐 print("창고 생성전"); int model = ref.watch(homeProvider); // watch 를 사용하면 창고의 state에 바로 접근한다. return yield 방법으로 한다. print("창고 생성후"); return Container( color: Colors.red, child: Align( child: Text( "$model", style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 100, decoration: TextDecoration.none, ), ), ), ); } }
 

창고 업데이트 하는 법

  • 창고 관리자를 통해 창고를 1번 읽는다
  • 해당 창고에 있는 함수를 실행 한다
  • 함수가 실행되면서 state 가 변한다
class BottomPage extends ConsumerWidget { BottomPage(); @override Widget build(BuildContext context, WidgetRef ref) { print("bottom 빌드"); // 창고에 접근하는 법 // 창고 타입을 받아야함. 이름은 vm 을 사용 HomeVM vm = ref.read(homeProvider.notifier); return Container( color: Colors.blue, child: Align( child: ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: Colors.red), onPressed: () { print("버튼 클릭됨"); vm.increase(); }, child: Text( "증가", style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 100), ), ), ), ); } }
 

riverpod으로 유저 객체 다루기

리버팟 창고

import 'package:flutter_riverpod/flutter_riverpod.dart'; // 1. 창고 데이터 타입(int 면 안 만들어도 됨) // 관리 타입은 1가지만 가능함 여러개면 클래스 타입 안에 넣어서 사용함 class User { String name; int age; User(this.name, this.age); } // 2. 창고 // <> <- 내부에는 관리하고 싶은 type 을 작성하면 됨 class HomeVM extends Notifier<User> { // 창고가 만들어질 때 초기화 메서드(return 하는 값을 창고가 state 로 관리함) @override User build() { print("창고 빌드 후 상태 초기화"); return User("", 0); } void changeUser({required String name, required int age}) { state = User(name, age); // state에는 항상 새로운 객체를 넣어야함. 기존 객체 수정 ❌ // setState 가 필요하지 않음. state 가 변화 되면 알아서 알려줌 } } // 3. 창고 관리자 <창고이름, 관리타입> final homeProvider = NotifierProvider<HomeVM, User>(() { print("창고 생성"); return HomeVM(); });

입력 창

import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_test/home_vm.dart'; void main() { runApp(const ProviderScope(child: MyApp())); } class MyApp extends StatelessWidget { const MyApp(); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: HomePage(), ); } } class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: ListView( children: [ Header(), Bottom(), ], ), ); } } class Bottom extends ConsumerWidget { TextEditingController _name = TextEditingController(); TextEditingController _age = TextEditingController(); void submit(vm) { print(_name.text); print(_age.text); // 상태를 바꿔야함 vm.changeUser(name: _name.text, age: int.parse(_age.text)); } @override Widget build(BuildContext context, WidgetRef ref) { HomeVM vm = ref.read(homeProvider.notifier); return Form( child: Column( children: [ TextField( decoration: InputDecoration(hintText: "이름입력"), controller: _name, ), TextField( decoration: InputDecoration(hintText: "나이입력"), controller: _age, ), TextButton(onPressed: () => submit(vm), child: Text("입력")), ], ), ); } } class Header extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { User model = ref.watch(homeProvider); return Column( children: [ Container( child: Text( "이름 : ${model.name}", style: TextStyle(fontSize: 40), ), ), Container( child: Text( "나이 : ${model.age}", style: TextStyle(fontSize: 40), ), ), ], ); } }
notion image
 
Share article

jjack1