Vintage appMaker의 Tech Blog

[Flutter] 부분갱신을 위한 Stateful과 GlobalKey 본문

Source code or Tip/Flutter & Dart

[Flutter] 부분갱신을 위한 Stateful과 GlobalKey

VintageappMaker 2022. 10. 14. 20:00

Flutter로 개발을 하다보면 Stateful Widget 사용을 최소화하여 사용하는 것이 좋다.
과부하가 발생하기 때문이다.

 

그래서 화면 1개당 stateful 위젯을 1개로 하고 그 자식 위젯을 모두 stateless로 구현하는 경우도 많다. 그럴 경우는 화면 내 check box 같은 자식 화면이 변화되더라도 부모 widget이 rebuild 되어야 하는 문제가 발생하지만 큰 부하가 없다면 이런 방법을 사용하는 것도 무방하다. 그러나 부하가 발생한다면, 갱신이 필요한 부분만 Stateful Widget으로 나누어 사용하면 된다. 

 

1. 부분갱신할 Widget을 Stateful로 정의 및 구현한다. 

2. 부모 Widget에서  자식 Widget을 제어할 GlobalKey를 선언하고 넘긴다. 

3. 부모 Widget에서 자식 Widget에 넘긴 GlobalKey의 currentState를 가져와 부분갱신할 Widget의 메소드를 사용한다. 

 

import "package:flutter/material.dart";

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

class StateFulTest extends StatefulWidget {
  @override
  _StateFulTestState createState() {
    return _StateFulTestState();
  }
}

class _StateFulTestState extends State<StateFulTest>
    with AutomaticKeepAliveClientMixin {
  late List<Widget> _items;
  ScrollController controller = ScrollController();
  GlobalKey buttonKey = GlobalKey();

  @override
  void initState() {
    _items = [
      GestureDetector(
          onTap: () {
            setState(() {});
          },
          child: Text("Click me - this ${this.context.toString()}")),
      for (var i = 0; i < 10; i++) Icon(Icons.bolt),
      GestureDetector(
          onTap: () {
            var w = (buttonKey.currentState as _ButtonWidgetState);
            w.setVisble(!w.isVisble);
          },
          child: Text("Click me - ButtonWidget(child with key)"))
    ];
    super.initState();
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

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

  @override
  Widget build(BuildContext context) {
    print("${this.widget.toString()}");
    return MaterialApp(
      home: Scaffold(
        floatingActionButton: ButtonWidget(key: buttonKey),
        body: SingleChildScrollView(
          controller: controller,
          child: Expanded(
              child: Container(
            color: Colors.amber,
            width: double.infinity,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: _items,
            ),
          )),
        ),
      ),
    );
  }
}

class ButtonWidget extends StatefulWidget {
  const ButtonWidget({Key? key}) : super(key: key);

  @override
  _ButtonWidgetState createState() => _ButtonWidgetState();
}

class _ButtonWidgetState extends State<ButtonWidget> {
  bool isVisble = false;
  void setVisble(bool b) {
    setState(() {
      isVisble = b;
    });
  }

  @override
  Widget build(BuildContext context) {
    print("${this.widget.toString()}");
    return Visibility(
        visible: isVisble,
        child: Container(
          padding: EdgeInsets.all(10),
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(50),
            color: Colors.red,
          ),
          child: Icon(
            Icons.add_alert,
            color: Colors.black,
          ),
        ));
  }
}
Comments