본문 바로가기

Flutter/07 State - Provider

[Flutter] setState

오늘은 setState 에 대해서 정리했습니다.

개발환경 : 윈도우11, 안드로이드 스튜디오(Arctic Fox 2020.3.1 Patch 4), flutter 2.10

소스코드 위치 - Release setState_Test · mike-bskim/provider_overview (github.com)

 

Release setState_Test · mike-bskim/provider_overview

 

github.com

 

setState 를 사용하면 얼마나 많은 부분을 다시 랜더링할까요?

몇단계의 위젯으로 나눠서 아래와 같은 앱을 만들었습니다.

 

먼저 화면설명은 아래와 같습니다.

 

 

앱을 실행하면 처음에 아래와 같이 각 위젯별 build 로그를 출력됩니다. 

 

I/flutter ( 5490): -----------------------------------
I/flutter ( 5490): MyApp >> build
I/flutter ( 5490): ===================================
I/flutter ( 5490): MyHomePage >> build
I/flutter ( 5490): CounterA >> build
I/flutter ( 5490): Middle >> build
I/flutter ( 5490): CounterB >> build
I/flutter ( 5490): Sibling >> build

 

"increment" 버튼 클릭시 setStage 는 MyHomePage 에서 호출되므로 하위의 모든 위젯들이 다시 랜더링됩니다.

 

I/flutter ( 5490): ===================================
I/flutter ( 5490): MyHomePage >> build
I/flutter ( 5490): CounterA >> build
I/flutter ( 5490): Middle >> build
I/flutter ( 5490): CounterB >> build
I/flutter ( 5490): Sibling >> build

 


 

전체 소스 코드는 "더보기" 클릭하면 볼수 있습니다.

더보기
import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    debugPrint('-----------------------------------');
    debugPrint('MyApp >> build');

    return MaterialApp(
      title: 'Counter',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int counter = 0;

  void increment() {
    setState(() {
      counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    debugPrint('===================================');
    debugPrint('MyHomePage >> build');

    return Scaffold(
      appBar: AppBar(
        title: const Text('My Counter'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Container(
              color: Colors.blue[100],
              padding: const EdgeInsets.all(20.0),
              child: const Text(
                'MyHomePage',
                style: TextStyle(fontSize: 24.0),
              ),
            ),
            const SizedBox(height: 20.0),
            CounterA(
              counter: counter,
              increment: increment,
            ),
            const SizedBox(height: 20.0),
            Middle(counter: counter),
          ],
        ),
      ),
    );
  }
}

class CounterA extends StatelessWidget {
  final int counter;
  final void Function() increment;

  const CounterA({
    Key? key,
    required this.counter,
    required this.increment,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    debugPrint('CounterA >> build');

    return Container(
      color: Colors.red[100],
      padding: const EdgeInsets.all(20.0),
      child: Column(
        children: [
          Text(
            '$counter',
            style: const TextStyle(fontSize: 48.0),
          ),
          ElevatedButton(
            onPressed: increment,
            child: const Text(
              'Increment',
              style: TextStyle(fontSize: 20.0),
            ),
          ),
        ],
      ),
    );
  }
}

class Middle extends StatelessWidget {
  final int counter;
  const Middle({
    Key? key,
    required this.counter,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    debugPrint('Middle >> build');

    return Container(
      color: Colors.grey[200],
      padding: const EdgeInsets.all(20.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        mainAxisSize: MainAxisSize.min,
        children: [
          CounterB(counter: counter),
          const SizedBox(width: 20.0),
          Sibling(),
        ],
      ),
    );
  }
}

class CounterB extends StatelessWidget {
  final int counter;
  const CounterB({
    Key? key,
    required this.counter,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    debugPrint('CounterB >> build');

    return Container(
      color: Colors.yellow[100],
      padding: const EdgeInsets.all(10.0),
      child: Text(
        '$counter',
        style: const TextStyle(fontSize: 24.0),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    debugPrint('Sibling >> build');

    return Container(
      color: Colors.orange[100],
      padding: const EdgeInsets.all(10.0),
      child: const Text(
        'Sibling',
        style: TextStyle(fontSize: 24.0),
      ),
    );
  }
}

 


 

 

여기서 Sibling() 호출시 const 키워드를 붙이면 랭더링시 다시 build 하지 않습니다. 다른 상태관리 패키지를 사용하지 않고 쉽게 랜더링 비용을 줄일수 있는 방법입니다.

 

// increment 버튼을 2번 클릭한 로그
I/flutter ( 5490): ===================================
I/flutter ( 5490): MyHomePage >> build
I/flutter ( 5490): CounterA >> build
I/flutter ( 5490): Middle >> build
I/flutter ( 5490): CounterB >> build
I/flutter ( 5490): ===================================
I/flutter ( 5490): MyHomePage >> build
I/flutter ( 5490): CounterA >> build
I/flutter ( 5490): Middle >> build
I/flutter ( 5490): CounterB >> build

 

 

 

[참고자료] udemy - Flutter Provider Essential 코스 (Korean)