Flutter

[Flutter] Riverpod 기본 개념, 간단 예제

izongg 2023. 12. 28. 16:58
반응형

 

Getx 만 사용해오다가 프로젝트에서 Riverpod을 사용하게되어서 공부하였다.

 

Getx는 사용하기 편리하고 자유성이 높은 프레임워크지만, 여러가지 이슈가 있고 대응이 느리다고한다.

따라서 Getx에만 너무 의존하지 말고, Provider/Riverpod/Bloc 중 하나정도는 더 공부하는 것이 좋다.

 

flutter Riverpod 이란 "반응형 캐싱 및 데이터 바인딩 프레임워크" 이다.

Riverpod과 Provider의 개발자는 같은 개발자이며, Provider의 문제점을 해결하여 만든 것이 Riverpod이라고 한다.

 

 

Install

flutter pub add flutter_riverpod

 

Provider 종류

Provider은 상태관리를 할 데이터 변수를 정의하는 것이다.

 

1. Provider

Provider은 read_only Provider이다.

final countProvider = Provider<int>((ref) {
  return 0;
});

 

2. StateProvider

StateProvider은 상태를 변경할 수 있는 Provider이다. 하지만 다른 로직과 함께 사용할 수는 없다.

final counterProvider = StateProvider<int>((ref) {
  return 0;
});

 

3. StateNotifierProvider 

이게 아마 제일 자주 사용할 Provider일 것이다.

데이터의 상태관리 뿐만 아니라 로직도 추가하여 사용할 수 있다.

final todoListProvider = StateNotifierProvider<TodoList, List<String>>((ref) {
  return TodoList();
});

class TodoList extends StateNotifier<List<String>> {
  TodoList() : super([]);

  void add(String item) {
    print("state : $state");
    state = [...state, item]; // state에는 prev 값이 들어가네.
  }
  
}

 

Riverpod을 사용하여 간단하게 Todo 리스트를 추가하고 삭제하는 기능을 구현해보았다.

 

// main.dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'home.dart';

void main() {
  runApp (const ProviderScope(child: MainApp()));
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return  MaterialApp(
      home: Home()
    );
  }
}

 

Riverpod을 사용하기 위해서는 runApp내부를 ProviderScope로 감싸주어야한다.

 

// provider.dart

import 'package:flutter_riverpod/flutter_riverpod.dart';

final todoListProvider = StateNotifierProvider<TodoList, List<String>>((ref) {
  return TodoList();
});

class TodoList extends StateNotifier<List<String>> {
  TodoList() : super([]);

  void add(String item) {
    print("state : $state");
    state = [...state, item];
  }

  void remove(String item) {
    state = state.where((element) => element != item).toList();
  }
 
}

Provider을 별도의 파일에 정의해보았다. 

Todo리스트를 추가하거나 삭제하는 로직이 필요하기 때문에 StateNotifierProviderStateNotifier을 사용하였다.

 

// home.dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'provider.dart';

class Home extends ConsumerWidget {
  Home({super.key});

  TextEditingController inputTodo = TextEditingController();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final todo = ref.watch(todoListProvider);

    ref.listen(todoListProvider, (prev, next) {
      print("현재상태 : $prev , $next");
    });

    return SafeArea(
      child: Scaffold(
        body: Padding(
          padding: EdgeInsets.symmetric(horizontal: 12),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Text(
                "Todo",
                style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
              ),
              SizedBox(
                height: 20,
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Container(
                      decoration: BoxDecoration(
                          border: Border.all(color: Colors.green[400]!),
                          borderRadius: BorderRadius.circular(10)),
                      width: MediaQuery.of(context).size.width * 0.7,
                      child: TextField(
                        controller: inputTodo,
                        decoration: InputDecoration(border: InputBorder.none),
                      )),
                  ElevatedButton(
                      onPressed: () {
                        ref.watch(todoListProvider.notifier).add(inputTodo.text);
                      },
                      child: Text("추가"))
                ],
              ),
              SizedBox(
                height: 30,
              ),
              ...List.generate(
                  todo.length,
                  (index) => Align(
                      alignment: Alignment.centerLeft,
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          Padding(
                            padding: const EdgeInsets.only(left: 8.0, top: 8),
                            child: Text(
                              todo[index],
                              style: TextStyle(
                                fontSize: 17,
                              ),
                            ),
                          ),
                          TextButton(
                              onPressed: () {
                                ref
                                    .read(todoListProvider.notifier)
                                    .remove(todo[index]);
                              },
                              child: Text("삭제"))
                        ],
                      )))
            ],
          ),
        ),
      ),
    );
  }
}

 

Riverpod을 사용할 땐 StatelessWidget 이나 StatefulWidget을 사용하지 않고 ConsumerWidget을 사용한다.

Consumer, ConsumerStatefulWidget도 있지만 보편적으로 ConsumerWidget을 사용하는 것 같다.

ConsumerWidget을 사용하면 Widget build 부분에 Widget ref 를 추가해준다.

 

ref 객체는 위젯에서 Provider에 접근할 수 있도록 해준다.

 

접근 하는 방법에는 watch, read, listen이 있는데 공식문서에서  read와 listen 보다 watch를 사용하는 것을 권장하고 있다.

 

ref.watch는 provider의 변화된 값을 감지하고, Widget을 재빌드한다.

 

참고자료

https://velog.io/@leeeeeoy/Flutter-Riverpod-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0-1

 

[Flutter] Riverpod 사용해보기 #1

이 글은 공식 문서, 유튜브 강의와 블로그를 참고하여 정리한 글입니다.

velog.io

https://riverpod.dev/ko/

 

 

 

 

 

 

반응형