
가장 기본이되는 provider인 state를 사용해보자.
① 새 프로젝트를 만든다.
그리고 사진과 같이 lib 아래 새 폴더와 파일을 만들어준다.

② defalut_layout.dart 코드를 작성한다
import 'package:flutter/material.dart';
class DefalutLayout extends StatelessWidget {
// 1. 3가지 값 받기
final String title;
final Widget body;
final List<Widget>? actions; // actions는 앱바 오른쪽 긑에 들어갈 값들
const DefalutLayout({
super.key,
// 2. 생성자
required this.title,
required this.body,
this.actions,
});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
actions: actions,
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: body,
),
);
}
}
③ screen/home_screen 폴더 및 파일 생성

import 'package:flutter/material.dart';
import 'package:riverpod_study/layout/defalut_layout.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
Widget build(BuildContext context) {
// 1. DefalutLayout 넣어주기
// DefalutLayout 사용하려고 폴더랑 파일 만들었었으니깐!
return DefalutLayout(
title: 'HomeScreen',
body: ListView(
children: const [],
),
);
}
}
④ main에 홈스크린 넣어주기
import 'package:flutter/material.dart';
import 'package:riverpod_study/screen/home_screen.dart';
void main() {
runApp(
const MaterialApp(
home: HomeScreen(),
),
);
}
실행해 보면 아래와 같이 HomeScreen 화면이 잘 나오는 것을 볼 수 있다.

⑤ ElevatedButton 추가
home_screen.dart 코드 중 ListView에다가 테스트해 볼 기능들이 있는
스크린들을 이동할 수 있는 ElevatedButton 을 하나씩 추가.
(눌렀을 때 화면 이동만 되게)

⑥ 이동을 위해 Navigator 추가
home_screen.dart 코드로 가서 onPressed에 이동을 위한 Navigator를 추가해 준다.

import 'package:flutter/material.dart';
import 'package:riverpod_study/layout/defalut_layout.dart';
import 'package:riverpod_study/screen/state_provider.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
Widget build(BuildContext context) {
// 1. DefalutLayout 넣어주기
// DefalutLayout 사용하려고 폴더랑 파일 만들었었으니깐!
return DefalutLayout(
title: 'HomeScreen',
body: ListView(
children: [
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => const StateProviderScreen(),
),
);
},
child: const Text('StateProviderScreen'),
)
],
),
);
}
}


StateProviderScreen 버튼을 누르면 StateProviderScreen 화면으로 잘 넘어가는 것을 볼 수 있다.

플러터에서 쓰려면 그냥 riverpod이 아니라 flutter_riverpod을 설치해야 함!

순서대로 잘 실행하면 riverpod 설치가 완료된다.
⑧ riverpod/state_provider.dart 폴더 및 파일 생성
가장 기본이되는 provider인 state를 사용해보자.

import 'package:flutter_riverpod/flutter_riverpod.dart';
// 1. 변수 선언
// 2. StateProvider 생성
// 0을 넣어 숫자를 상태 관리해보자!
// 3. <> 제너릭에 타입 넣어주기 => 어떤 타입의 값을 관리하게 될지 넣어주는 것!
final numberProvider = StateProvider<int>((ref) => 0);
⑨ riverpod 사용하기
⑨-1. StatelessWidget을 ConsumerWidget으로 바꾸기
StateProviderScreen으로 돌아가서 StatelessWidget을 ConsumerWidget으로 바꿔준다.

그리고!!!
⭐ ⑨-2. main.dart에 MaterialApp을 ProviderScope로 감싸준다.
이 두 과정을 거쳐야 riverpod을 사용할 수 있다.

⑩ build 함수에 WidgetRef ref 파라미터 추가하기
ConsumerWidget을 상속받았을 때 달라지는 점은?
기존 StatelessWidget과 99% 같은데, 유일한 1% 차이점은
build 함수 안에 하나의 파라미터를 더 추가해줘야 한다는 것이다.
이 ref를 사용해 riverpod/state_provider_screen.dart에 선언해 놓은
StateProvider에 접근할 수 있게 된다.

그럼 어떻게 접근할 수 있을까?
⑪ ref.watch 사용해서 특정 provider 바라보기
state_provider_screen.dart 코드에서 build 함수 아래 변수를 선언하고,
ref.watch 사용해서 특정 provider 바라보다가 그 provider가 변경되면 build를 재실행하도록 한다.

⑫ numberProvider 넣어주기
state_provider.dart에 선언해 놓은 numberProvider를 넣어준다.
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_study/layout/defalut_layout.dart';
import 'package:riverpod_study/riverpod/state_provider.dart';
class StateProviderScreen extends ConsumerWidget {
const StateProviderScreen({super.key});
Widget build(BuildContext context, WidgetRef ref) {
// ref.watch => 특정 provider를 바라보고 있다가
// 그 provider가 변경되면 build를 재실행
final provider = ref.watch(numberProvider);
return const DefalutLayout(
title: 'StateProviderScreen',
body: Column(
children: [],
),
);
}
}
⑫ Column에 Text 넣기
provider.toString( )을 해 준 이유는 조금있다 알 수 있음 ~

저장하고 앱을 재실행 해보자!

StateProviderScreen 버튼을 누르면...

StateProviderScreen 화면으로 이동하고 0도 잘 보이는 것을 확인할 수 있다.
그렇다면, 이 0이라는 숫자는 어디서 온걸까?
state_provider.dart 파일의 StateProvider에 있는 0이 바로 이 0이다.

즉, numberProvider를 ref.watch에 넣음으로써 0이라는 반환 값을 가져올 수 있었던 것!
⑬ ElevatedButton 추가하기
그런데 이 값만 가져오는 건 사실 별로 유용하지 않겠지?
진짜 원하는 건 상태를 변경하는 것!
그리고 변경된 데이터를 화면에 보여주고...
이걸 확인하는 작업을 해보자.

⑭ ref 가져오기
그리고 onPressde에 ref를 가져와보자.

여기서는 ref.watch가 아닌 ref.read로 작성해야 한다!
ref.read = 어떤 버튼을 눌렀을 때 실행되는 경우고,
build 함수 안에서 직접적으로 UI에 반영하기 위해 가져올 땐 watch라고
간단하게 기억하고 있으면 될 것 같다.
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_study/layout/defalut_layout.dart';
import 'package:riverpod_study/riverpod/state_provider.dart';
class StateProviderScreen extends ConsumerWidget {
const StateProviderScreen({super.key});
Widget build(BuildContext context, WidgetRef ref) {
// ref.watch => 특정 provider를 바라보고 있다가
// 그 provider가 변경되면 build를 재실행
final provider = ref.watch(numberProvider);
return DefalutLayout(
title: 'StateProviderScreen',
body: SizedBox(
width: MediaQuery.of(context).size.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
provider.toString(),
),
ElevatedButton(
onPressed: () {
// .update((state) => null );
// state = 현재 상태 ----> 여기서는 0
// 그럼 up 버튼을 누르면 state에 +1 더해 반환해줘야겠지?
ref.read(numberProvider.notifier).update((state) => state + 1);
},
child: const Text('UP'),
),
],
),
),
);
}
}
여기서 스크린을 하나 더 만들어보자!
⑮ StateProviderScreen에 스크린 하나 더 만들기
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_study/layout/defalut_layout.dart';
import 'package:riverpod_study/riverpod/state_provider.dart';
class StateProviderScreen extends ConsumerWidget {
const StateProviderScreen({super.key});
Widget build(BuildContext context, WidgetRef ref) {
// ref.watch => 특정 provider를 바라보고 있다가
// 그 provider가 변경되면 build를 재실행
final provider = ref.watch(numberProvider);
return DefalutLayout(
title: 'StateProviderScreen',
body: SizedBox(
width: MediaQuery.of(context).size.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
provider.toString(),
),
ElevatedButton(
onPressed: () {
// .update((state) => null );
// state = 현재 상태 ----> 여기서는 0
// 그럼 up 버튼을 누르면 state에 +1 더해 반환해줘야겠지?
ref.read(numberProvider.notifier).update((state) => state + 1);
},
child: const Text('UP'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => const _NextScreen(),
),
);
},
child: const Text('Next Screen'),
),
],
),
),
);
}
}
class _NextScreen extends ConsumerWidget {
const _NextScreen({super.key});
Widget build(BuildContext context, WidgetRef ref) {
final provider = ref.watch(numberProvider);
return DefalutLayout(
title: 'StateProviderScreen',
body: SizedBox(
width: MediaQuery.of(context).size.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
provider.toString(),
),
ElevatedButton(
onPressed: () {
// .update((state) => null );
// state = 현재 상태 ----> 여기서는 0
// 그럼 up 버튼을 누르면 state에 +1 더해 반환해줘야겠지?
ref.read(numberProvider.notifier).update((state) => state + 1);
},
child: const Text('UP'),
),
],
),
),
);
}
}
StateProviderScreen과 _NextScreen 이 둘은 거의 똑같은 코드지만 다른 화면이다.
여기서 UP 버튼을 누르고 NextScreen 버튼을 눌러서 들어가보면
마지막으로 누른 숫자 그대로 있는 걸 볼 수 있다.
마찬가지로 NextScreen에서 UP 버튼을 누르고
뒤로가서 StateProviderScreen을 확인해 보면
마지막으로 누른 숫자 그대로 있는 것을 볼 수 있다.
원래 같으면 Navigator를 할 때, final provider라는 값을 변경했으니깐
변경한 값을 _NextScreen에다가 어떻게든 넘겨줘야지 똑같은 값을 공유할 수 있었음.
하지만!!!
riverpod을 사용함으로서 똑같은 numberProvider를
ref.watch 하고만 있으면 간단하게 어디서든 데이터를 서로 넘겨주고 할 필요 없이
같은 프로바이더(numberProvider)를 그냥 불러옴으로써 데이터를 공유할 수 있음!