Vintage appMaker의 Tech Blog

[Flutter] BottomNavitaionBar에서 화면이동시 코드실행(화면갱신) 본문

Source code or Tip/Flutter & Dart

[Flutter] BottomNavitaionBar에서 화면이동시 코드실행(화면갱신)

VintageappMaker 2022. 7. 20. 11:49

 

BottomNavigationBar를 통해 하단 버튼을 누르며 메인화면을 갱신하다보면 선택 화면이 표시된 후,  특정 작업을 수행해야 하는 경우가 종종 발생한다. 이럴경우, Flutter는 선언형 구조이기 때문에 Native 프로그래밍에 비해 불편한 부분이 있다. 그래서 Provider를 사용하여 코드를 깔금하게 정리하는 것을 권장되고 있다. 

 

- 화면갱신완료 시점
- provider로 데이터 처리
- 객체의 메소드 실행(권장하지 않지만 사용할 경우가 있다)

 

- 화면갱신 완료시점

WidgetsBinding.instance.addPostFrameCallback()을 사용한다. 주로 stateful 위젯의 initState()에서 구현해준다. 

저 함수의 파라메토로 종료시 실행될 콜백을 구현하면 된다. 

 

- provider로 데이터 처리

위젯간 계층관계가 있고 위젯변환시 데이터가 공유되어야 한다면 고민없이 provider를 사용하기를 권장한다.  

 

- 객체의 메소드 실행(권장하지 않지만 사용할 경우가 있다)

Flutter는 선언형 프로그래밍이라 화면에 표시되는 Widget을 객체로 관리하는 것을 권장하지 않는다. 그럼에도 불구하고 특정위젯의 메소드를 직접 실행하기 위해서는 명명된 object로 변수처리 되어야 한다. 그럴 경우, build 메소드에 넘겨질 Widget을 변수로 선언 후 관리하면 된다. 단, 변수를 액세스하려면 화면갱신이 완료된 시점에만 가능하다. 

결국, WidgetsBinding.instance.addPostFrameCallback()에서 처리해야한다.

 

 

state 클래스에서 변수를 선언하고
build 함수에서 값을 대입한다.

[전체소스]

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: (_) => Counter()),
      ],
      child: MaterialApp(
        title: 'Flutter 예제',
        theme: ThemeData(
          primarySwatch: Colors.green,
        ),
        home: MyHomePage(),
      ),
    );
  }
}

class Counter with ChangeNotifier {
  static int initsize = 15;
  int _count = initsize;

  bool isOnTapBefore = false;

  int get fontsize => _count;

  void increment() {
    _count++;
    isOnTapBefore = true;
    notifyListeners();
  }

  void reset() {
    _count = initsize;
    isOnTapBefore = false;
    notifyListeners();
  }
}

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

class _OnePageState extends State<OnePage> {
  late GestureDetector gestureDetector;

  @override
  void initState() {
    // 위젯바인딩 완료
    WidgetsBinding.instance.addPostFrameCallback((_) {
      print("화면갱신");

      // 1.5초 후 tap하기
      Future.delayed(Duration(milliseconds: 1500))
          .then((onValue) => gestureDetector.onTap?.call());
    });

    return super.initState();
  }

  @override
  Widget build(BuildContext context) {
    var c = context.watch<Counter>();
    gestureDetector = GestureDetector(
      onTap: () {
        context.read<Counter>().increment();
      },
      child: (c.isOnTapBefore)
          ? Text(
              "click me!(${c.fontsize})",
              style: TextStyle(fontSize: c.fontsize.toDouble()),
            )
          : Image.network(
              "https://thumbs.dreamstime.com/b/click-here-button-hand-icon-vector-web-sign-cursor-symbol-isolated-152282340.jpg"),
    );

    return gestureDetector;
  }
}

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

class _MyHomePageState extends State<MyHomePage> {
  double fontSize = 15;
  late List<Widget> _items;

  @override
  void initState() {
    _items = [
      OnePage(),
      Text(
        '2',
      ),
      Text(
        '3',
      ),
    ];
  }

  int _selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter Learning"),
      ),
      body: Center(child: _items[_selectedIndex]),
      bottomNavigationBar: _showBottomNav(),
    );
  }

  Widget _showBottomNav() {
    return BottomNavigationBar(
      items: [
        BottomNavigationBarItem(
          icon: Icon(Icons.abc),
          label: '1',
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.numbers_rounded),
          label: '2',
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.numbers_sharp),
          label: '3',
        ),
      ],
      currentIndex: _selectedIndex,
      selectedItemColor: Colors.green,
      unselectedItemColor: Colors.grey,
      onTap: _onTap,
    );
  }

  void _onTap(int index) {
    _selectedIndex = index;
    if (index == 0) {
      //context.read<Counter>().reset();
      context.read<Counter>().reset();
    }

    // 화면갱신
    setState(() {});
  }
}

Comments