Flutter

Flutter Key 및 상태 관리의 기본 개념 - Flutter의 Element 재사용 메커니즘

whs5758 2025. 8. 19. 17:22

Flutter의 Element 재사용 메커니즘

canUpdate() 메서드

Flutter는 Widget.canUpdate() 메서드를 사용하여 Element 재사용 여부를 결정합니다

// Flutter 프레임워크의 실제 canUpdate() 메서드
static bool canUpdate(Widget oldWidget, Widget newWidget) {
  return oldWidget.runtimeType == newWidget.runtimeType
      && oldWidget.key == newWidget.key;
}

재사용 판단 과정

  1. 프레임 빌드 시: Element Tree의 각 노드를 검사
  2. 비교 수행: 새 위젯의 타입과 키를 이전 위젯과 비교
  3. 재사용 결정:
    • 타입과 키가 같으면 → Element 재사용, 참조만 업데이트
    • 다르면 → 기존 Element 제거 후 새로 생성

성능에 미치는 영향

// 좋은 예: Element 재사용
Widget build(BuildContext context) {
  return Container(  // 항상 Container 타입 유지
    color: isActive ? Colors.blue : Colors.grey,
    child: Text(message),
  );
}

// 나쁜 예: Element 재생성
Widget build(BuildContext context) {
  return isActive
    ? Container(child: Text(message))  // Container 타입
    : SizedBox(child: Text(message));  // SizedBox 타입 - 타입 변경으로 인한 Element 재생성
}

문제가 있는 코드 만들어 보기

import 'package:class_key/components/custom_container.dart';
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

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

class _MyHomePageState extends State<MyHomePage> {
  // 자료 구조(메모에서 데이터를 넣고 빼고 수정..)
  List<Widget> containers = [
    CustomContainer("1", key: ValueKey(1)),
    CustomContainer("2", key: ValueKey(2)),
    CustomContainer("3", key: ValueKey(3)),
  ];

  // 변수
  Widget extraContainer = const CustomContainer("Extra");

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: containers,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            if (containers.length == 3) {
              containers.insert(0, extraContainer);
            } else if (containers.length == 4) {
              containers.removeAt(0);
            }
          });
        },
        child: Icon(
          Icons.add_box_rounded,
          size: 42,
        ),
      ),
    );
  }
}
import 'dart:math';

import 'package:flutter/material.dart';

//

class CustomContainer extends StatefulWidget {
  final String name;
  const CustomContainer(this.name, {super.key});

  @override
  State<CustomContainer> createState() => _CustomContainerState();
}

class _CustomContainerState extends State<CustomContainer> {
  late Color color = getRandomColor();
  // 부모 위젯에 멤버, 메서드에 접근하는 방법은 내부적으로 widget 변수가
  // 설계 되어 있다.
  @override
  Widget build(BuildContext context) {
    print("${widget.name} - 에 build() 메서드가 호출 됨");
    return Container(
      height: 150,
      width: 150,
      color: color,
      child: Center(
        child: Text(
          widget.name,
          style: TextStyle(
            color: Colors.black,
            fontSize: 24,
          ),
        ),
      ),
    );
  }

  Color getRandomColor() {
    final Random random = Random();
    return Color.fromRGBO(
      random.nextInt(256),
      random.nextInt(256),
      random.nextInt(256),
      1,
    );
  }
}