nueog

[Flutter] Flutter의 상태관리, Provider 본문

Flutter

[Flutter] Flutter의 상태관리, Provider

nueog 2024. 10. 11. 15:52
반응형

Flutter 기반의 어플 제작이 1차적으로 마무리가 되어가는 시점에 Flutter에 중요한 요소들을 정리해보려 한다.

 

Android 와는 다르게 Flutter는 선언적이기 때문에 업데이트가 되면 자동으로 변하는 라이프사이클이 존재하지 않는다.

그래서 따로 앱의 상태관리를 해줘야하는데, 여러가지 라이브러리가 있는데 그 중 가장 간단하고 적용이 쉬운 Provider를 적용해서 활용하고 있다.

 

먼저, Flutter에는 크게 Stateless와 Stateful Widget이 존재한다.

Stateful Widget은 SetState()를 통해서 앱의 변경사항이 생기면 함수를 실행하여 전체 widget을 다시 로드하는 방식으로 작동한다.

 

반면 Provider는 기본적으로 하위에 있는 위젯을 구독하는 형식으로 진행이 된다.

notifyListeners() 함수를 통해서 위젯의 변경사항을 전달하는 방식이다.

Stateless Widget을 사용하여 진행한다. 

 

Stateful Widget을 썼을 때와의 차이점은, Stateful Widget에서는 상태변경을 할 때마다 전체 화면이 로드되는 동작이었다면 Provider는 일부 화면만 재로딩해주는 점이 있다.

 

초기 Flutter 스터디를 진행할 때는 stateful을 사용했을 때 로드가 느리다라는 생각을 했는데, Provider를 적용시킨 후에는 현저히 속도가 빨라진 것을 체감할 수 있었다.

 

Provider의 구현 방법에 대해서 알아보자면,

 

1. 의존성 추가

1) pubspec.yaml 에서 provider: ^6.1.2(사용할 버전) 혹은 터미널에서 flutter pub add provider 명령어를 통해 의존성을 추가한다.

pubspec.yaml 파일에 추가

 

2. Provider 관리 class 추가

 RoutineProcessDataInsertProvider(
      {required RoutineProcessDataInsertList routineProcessDataInsert}) {
    _routineItem = routineProcessDataInsert.item;
    _animals = routineProcessDataInsert.animals;
    _processState = routineProcessDataInsert.item.process_state;

    _insertValue = List.filled(_animals.length, 0);
  }

  Future<void> loadData() async {
    _isLoading = true;
    notifyListeners();

    try {
      _routineCareDataList = await RoutineApiService.routineCareDataList(
        us_code: MyApp.us_code,
        it_code: _routineItem.it_code,
      );
    } catch (e) {
      _error = 'Failed to load data: $e';
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  })

 

위 코드에서는 처음 Load 될 때 http 통신을 이용하여 데이터를 불러오기 위해서 loadData()라는 함수를 만들었다.

그리고 통신이 마무리가 되어 finally에 도달하면 notifyListeners();를 통해 위젯에 변경사항이 notify된다.

 

 

3. 관리하고자 하는 위젯에 ChangeNotifierProvider 추가

class RoutineProcessDataInsert extends StatelessWidget {
  final RoutineProcessDataInsertList routineProcessDataInsert;

  const RoutineProcessDataInsert(
      {super.key, required this.routineProcessDataInsert});

  @override
  Widget build(BuildContext context) {
    return Container(
        color: Colors.white,
        child: ChangeNotifierProvider(
            create: (_) => RoutineProcessDataInsertProvider(
                routineProcessDataInsert: routineProcessDataInsert)
              ..loadData(),
            child: RoutineProcessDataInsertView(
              routineProcessDataInsert: routineProcessDataInsert,
            )));
  }
}

 

위 코드에서는 ChangeNotifierProvider()를 통해 위에서 기술한 provider class와 적용할 위젯을 할당하였다.

실행됐을 때 loadData()함수를 통해 데이터를 불러오는 작업을 하도록 추가하였다.

 

4. 위젯에서 Provider 불러오기

class RoutineProcessDataInsertView extends StatelessWidget {
  final RoutineProcessDataInsertList routineProcessDataInsert;

  const RoutineProcessDataInsertView(
      {super.key, required this.routineProcessDataInsert});

  @override
  Widget build(BuildContext context) {
    final routineDataInsertProvider =
        Provider.of<RoutineProcessDataInsertProvider>(context);

    if (routineDataInsertProvider.isLoading) {
      return const Center(child: CircularProgressIndicator()
          );
    } else if (routineDataInsertProvider.error != null) {
      return Center(child: Text('Error: ${routineDataInsertProvider.error}'));
    } else {
      return 
      
      ...

 

Provider.of<[프로바이더명]>(context); 를 선언하여 provider를 구독하게 되었다.

또한 추가로 loadData()가 완료될 때 까지 CircularProgressIndicator()를 돌려 로딩중임을 표시하는 동작을 추가했다.

 

                        GestureDetector(
                            onTap: () => routineDataInsertProvider
                                .showModalWithScroll(context),
                            child: Text(
                                routineDataInsertProvider.processState.state,
                                style: const TextStyle(
                                    decoration: TextDecoration.underline,
                                    color: const Color(0xff141414),
                                    fontWeight: FontWeight.w500,
                                    fontFamily: "NotoSansCJKKR",
                                    fontStyle: FontStyle.normal,
                                    fontSize: 15.0),
                                textAlign: TextAlign.right))

 

추가로 provider를 사용할 때에는 [위에서 선언한 provider 변수].[provider에서 기술한 함수명] 혹은 [위에서 선언한 provider 변수].[provider에서 기술한 변수명] 이렇게 추가하여 사용할 수 있다.

 

Provider의 전체적인 사용법을 알아보았고 자세한 동작내용은 이어서 기술하도록 하겠다!

 

 

 

 

반응형