Develop/Flutter

[Flutter] 플러터 MultiProvider를 통해 BottomNavigationBar 구현

JunJangE 2021. 12. 30. 14:07

이전에 Provider를 통해 count를 하는 UI를 구현해보았다.

Provider에 대해서 잘 모른다면 다음 링크를 통해 알아보면 좋을 것 같다.

 

[Flutter] 플러터 ChangeNotifierProvider를 통해 Provider 구현

이번에는 ChangeNotifierProvider를 통해 Provider를 구현해보자. ChangeNotifier는 Flutter SDK에 포함된 클래스로 청취자에게 변경 알림을 제공해주는 것이다. 즉, ChangeNotifier를 통해 변화에 대해 구독할 수..

fre2-dom.tistory.com

이번에는 2개 이상의 Provider를 사용하기 위해 MultiProvider를 사용하면서 BottomNavigationBar를 구현해보도록 하자.

코드의 이해를 돕기 위해 폴더의 위치를 다음과 같이 구성한다.

폴더의 위치를 구성하고 Provider 패키지를 추가했다면 본격적으로 코드를 작성하도록 하자.

main.dart

import 'package:ev_app/src/provider/bottom_navigation_provider.dart';
import 'package:ev_app/src/provider/count_provider.dart';
import 'package:flutter/material.dart';
import 'package:ev_app/src/home.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: MultiProvider(
            // MultiProvider를 통해 변화에 대해 구독
            providers: [
              ChangeNotifierProvider(
                  create: (BuildContext context) => CountProvider()), // count_provider.dart
              ChangeNotifierProvider(
                  create: (BuildContext context) => BottomNavigationProvider())
            ],
            child:
                Home() // home.dart // child 하위에 모든 것들은 CountProvider에 접근 할 수 있다.
            ));
  }
}

 

위에서부터 코드를 확인하게 되면 home 부분에 MultiProvider를 통해 여러 개의 ChangeNotifierProvider를 사용하게 된다. 즉, 여러 개의 Provider를 관리하게 된다. 다음으로 child에 Home()은 CountProvider()와 BottomNavigationProvider에 접근할 수 있다. 

home.dart

import 'package:ev_app/src/ui/list.dart';
import 'package:flutter/material.dart';
import 'package:ev_app/src/ui/count_home_widget.dart';
import 'package:provider/provider.dart';
import 'package:ev_app/src/provider/bottom_navigation_provider.dart';

class Home extends StatelessWidget {
  late BottomNavigationProvider _bottomNavigationProvider;

  // 네비게이션바 UI Widget
  Widget _navigationBody() {
    // switch를 통해 currentPage에 따라 네비게이션을 구동시킨다. 
    switch (_bottomNavigationProvider.currentPage) {
      case 0:
        return CountHomeWidget();

      case 1:
        return ListWidget();
    }
    return Container();
  }

  // 네비게이션바 Widget
  Widget _bottomNavigationBarWidget() {
    return BottomNavigationBar(
      items: [
        BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
        BottomNavigationBarItem(icon: Icon(Icons.movie), label: "Movie")
      ],

      // 현재 페이지 : _bottomNavigationProvider의 currentPage
      currentIndex: _bottomNavigationProvider.currentPage, 
      selectedItemColor: Colors.blue,

      // _bottomNavigationProvider에 updateCurrentPage를 통해 index를 전달
      onTap: (index) {
        _bottomNavigationProvider.updateCurrentPage(index);
      },
    );
  }

  @override
  Widget build(BuildContext context) {

    // Provider를 호출해 접근
    _bottomNavigationProvider = Provider.of<BottomNavigationProvider>(context);

    return Scaffold(
      body: _navigationBody(),
      bottomNavigationBar: _bottomNavigationBarWidget(),
    );
  }
}

위에서부터 코드를 확인하게 되면 BottomNavigationProvider를 호출해 late를 통해 값을 나중에 받는 것을 알 수 있다. 다음으로 바텀 네비게이션 바를 위해 두 가지 위젯을 만들었다. 첫 번째로 네비게이션 UI Widget이다. 네비게이션 UI Widget은 호출한 BottomNavigationProvider에 currentPage를 불러와 currentPage에 따라 UI를 호출하는 것을 알 수 있다. 두 번째는 네비게이션 바 Widget이다. 네비게이션 바 Widget은 BottomNavigationBar를 리턴하며 BottomNavigationProvider에 updateCurrentPage 함수를 통해 index를 전달하는 것을 알 수 있다.

bottom_navigation_provider.dart

import 'package:flutter/material.dart';

// ChangeNotifier 상속 받이 상태 관리
// BottomNavigation을 구동
class BottomNavigationProvider extends ChangeNotifier {
  int _index = 0;
  int get currentPage => _index; // get 함수를 사용해 외부에서 접근이 가능하게 한다.

  // page 업데이트
  updateCurrentPage(int index) {
    _index = index; // updateCurrentPage를 통해 index 값을 받아와 _index 페이지로 바꿔준다.
    notifyListeners(); // notifyListeners 호출해 업데이트된 값을 구독자에게 알림
  }
}

위에서부터 코드를 확인하게 되면 ChangeNotifier를 상속받아 상태 관리하는 것을 알 수 있고 바텀 네비게이션 바를 구동하게 된다. 

list.dart

import 'package:flutter/material.dart';

class ListWidget extends StatelessWidget {
  const ListWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

count_home_widget.dart

import 'package:ev_app/src/components/view_count.dart';
import 'package:ev_app/src/provider/count_provider.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class CountHomeWidget extends StatelessWidget {
  CountHomeWidget({Key? key}) : super(key: key);

  late CountProvider _countProvider;

  @override
  Widget build(BuildContext context) {
    // Provider를 호출해 접근, listen: false를 통해 구독된 모든 위젯들에게 알림을 보내지 않게 한다.
    _countProvider = Provider.of<CountProvider>(context, listen: false);

    return Scaffold(
      appBar: AppBar(
        title: Text("Count Provider"),
      ),
      body: ViewCountWidget(),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          IconButton(
            icon: Icon(Icons.add),
            onPressed: () {
              _countProvider.add(); // 클릭 되었을 때 add() 이벤트 처리
            },
          ),
          IconButton(
            icon: Icon(Icons.remove),
            onPressed: () {
              _countProvider.remove(); // 클릭 되었을 때 remove() 이벤트 처리
            },
          )
        ],
      ),
    );
  }
}

count_provider.dart

import 'package:flutter/material.dart';

// ChangeNotifier 상속 받이 상태 관리
// 이 안에 있는 맴버 변수 값들을 상태 관리 한다.
class CountProvider extends ChangeNotifier {
  int _count = 0;
  int get count => _count; // get 함수를 사용해 외부에서 접근이 가능하게 한다.

  // 더하기
  add() {
    _count++;
    notifyListeners(); // notifyListeners 호출해 업데이트된 값을 구독자에게 알림
  }

  // 빼기
  remove() {
    _count--;
    notifyListeners(); // notifyListeners 호출해 업데이트된 값을 구독자에게 알림
  }
}

view_count.dart

import 'package:ev_app/src/provider/count_provider.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class ViewCountWidget extends StatelessWidget {
  const ViewCountWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child:
          // Consumer를 활용해서 provider에 접근하여 데이터를 받아올 수 있다
          Consumer<CountProvider>(
              builder: (context, provider, child) {
        return Text(
          provider.count.toString(), // count를 화면에 출력
          style: TextStyle(fontSize: 40.0),
        );
      }),
    );
  }
}

그 외 코드들은 이전에 배웠던 코드이거나 설명 없이 작성해도 될 만큼 간단한 코드이다.

모든 코드를 한 번에 리뷰하면 다음과 같다.

main.dart에서 MultiProvider를 통해 여러 개의 ChangeNotifierProvider를 사용, 관리하게 된다. 여기서 CountProvider()와 BottomNaviagationProvider에 home.dart가 접근할 수 있게 된다. home.dart에서는 BottomNavigationProvider를 호출, 접근하게 된다. body부분은 _navigationBody()로 BottomNavigationProvider에 currentPage를 불러와 currentPage에 따라 UI를 호출한다. bottomNavigationBar는 _bottomNavigationBarWidget()으로 BottomNavigationBar를 리턴하며 BottomNavigationProvider에 updateCurrentPage 함수를 통해 index를 전달한다. 즉, _bottomNavigationBarWidget()으로 업데이트된 index를 _navigationBody()가 currentPage를 통해 전달받아 currentPage의 index에 따라 UI를 불러오는 것이다. 그 외 내용은 이전에 배웠던 내용이므로 생략한다.

위 코드를 모두 작성하면 다음과 같은 결과 화면이 나올 것이다.

<결과 화면>