Vintage appMaker의 Tech Blog

[Flutter] BottomNavigation을 provider로 제어. 본문

Source code or Tip/Flutter & Dart

[Flutter] BottomNavigation을 provider로 제어.

VintageappMaker 2022. 8. 26. 18:02

 

의외로 BottomNavigation을 쓰다보면 손이 가는 경우가 많이 발생한다. 주로,

 

1. 하단메뉴를 눌렀을 때, 상태에 따라 다른 반응을 보여주기(지금 화면에서 보여주고 있는가? 처음누르는가?, ...)

2. 메인화면 액션을 취하면, 하단메뉴의 상태가 변하며 이동하기(화면이동 후, 위젯의 메소드 실행 등등..)

 

인데, 구글링을 해도 속시원한 해결방법이 없었다. 
그래서 Provider에 각 위젯의 메소드를 함수형태로 보관하여 call하는 방식을 취했다. 
다음은 전체소스이다.

 

[전체소스]

import "package:flutter/material.dart";
import "package:flutter/widgets.dart";
import "package:provider/provider.dart";

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => GlobalState()),
      ],
      child: MaterialApp(
        title: 'Flutter 예제',
        theme: ThemeData(
          primarySwatch: Colors.green,
        ),
        home: MyHomePage(),
      ),
    );
  }
}

// 단순 함수,변수 공유용으로 사용
class GlobalState with ChangeNotifier {
  bool isBottomNavigate = true;
  int nClick = 0;

  // page 이동
  void goPage(int n) {
    isBottomNavigate = false;
    nClick++;
    fnPageMove(n);
  }

  // Function 변수들
  late Function fnPageMove;
  late Function fnPage2_setMesageInfo = (String s) {};
}

class _OnePage extends StatefulWidget {
  @override
  _OnePageState createState() {
    return _OnePageState();
  }
}

class _OnePageState extends State<_OnePage> with AutomaticKeepAliveClientMixin {
  late GestureDetector gestureDetector;

  // Setting to true will force the tab to never be disposed. This could be dangerous.
  @override
  bool get wantKeepAlive => true;

  @override
  void initState() {
    return super.initState();
  }

  @override
  Widget build(BuildContext context) {
    var c = context.watch<GlobalState>();
    gestureDetector = GestureDetector(
        onTap: () {
          c.goPage(1);
        },
        child: Text(
          "Tab2로 이동",
          style: TextStyle(fontSize: 30),
        ));

    return gestureDetector;
  }
}

class _TwoPage extends StatefulWidget {
  @override
  _TwoPageState createState() {
    return _TwoPageState();
  }
}

class _TwoPageState extends State<_TwoPage> with AutomaticKeepAliveClientMixin {
  // Setting to true will force the tab to never be disposed. This could be dangerous.
  @override
  bool get wantKeepAlive => true;
  ValueNotifier<String> _data = ValueNotifier<String>("");
  late GlobalState c;

  @override
  void initState() {
    return super.initState();
  }

  @override
  Widget build(BuildContext context) {
    // build에서 초기화해야 한다.
    c = context.watch<GlobalState>();
    c.fnPage2_setMesageInfo = setMessageInfo;
    setDefaultInfo();

    return ValueListenableBuilder(
        valueListenable: _data,
        builder: (ctx, String s, child) {
          return GestureDetector(
              onTap: () {},
              child: Text(
                "${s}",
                style: TextStyle(color: Colors.blue, fontSize: 30),
              ));
        });
  }

  void setMessageInfo(String s) {
    _data.value = s;
  }

  void setDefaultInfo() {
    if (c.isBottomNavigate == false) {
      setMessageInfo("위젯 클릭으로 이동. ${c.nClick}");
      c.isBottomNavigate = true;
    } else {
      setMessageInfo("하단메뉴로 이동.");
      c.isBottomNavigate = true;
    }
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() {
    return _MyHomePageState();
  }
}

class _MyHomePageState extends State<MyHomePage> {
  late List<Widget> _items;

  late GlobalState c;

  ValueNotifier<int> _param = ValueNotifier<int>(0);

  @override
  void initState() {
    _items = [_OnePage(), _TwoPage()];
  }

  int _selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    // build에서 초기화해야 한다.
    c = context.watch<GlobalState>();
    c.fnPageMove = _onTap;

    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter Learning"),
      ),
      body: ValueListenableBuilder(
        valueListenable: _param,
        builder: (ctx, int n, child) {
          return Center(child: _items[n]);
        },
      ),
      bottomNavigationBar: _showBottomNav(),
    );
  }

  Widget _showBottomNav() {
    return ValueListenableBuilder(
      valueListenable: _param,
      builder: (ctx, n, child) {
        return BottomNavigationBar(
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.abc),
              label: '1',
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.numbers_rounded),
              label: '2',
            )
          ],
          currentIndex: _param.value,
          selectedItemColor: Colors.green,
          unselectedItemColor: Colors.grey,
          onTap: _onTap,
        );
      },
    );
  }

  void _onTap(int index) {
    _param.value = index;

    if (_param.value == index && index == 1) {
      c.fnPage2_setMesageInfo("현재 선택된 상태에서 click함.");
    }
  }
}
Comments