본문 바로가기
Flutter

덩치가 커진 위젯의 rebuild

by 붕어사랑 티스토리 2024. 3. 8.
반응형

플러터를 하다보면 하나의 위젯이 덩치가 어마어마하게 커지는 경우가 있다. 이때 setState를 하면 어마어마한 퍼포먼스 문제를 야기한다.

 

헌데 위젯을 하나하나 따로 빼서 끊어주고 생성자 뚫고 해주는게 여간 쉬운일이 아니다. 이럴 때 사용하는 팁을 적어본다

 

 

 

 

 

1. 프로파이더 사용

너무나 당연한 얘기이지만 프로바이더를 사용하면 커다란 빌드 함수 안에서 특정 위젯만 리빌드 할 수 있다.

다만 코드 작성하기가 여간 귀찮다

 

 

 

 

 

 

2. 스트림 컨트롤러와 스트림 빌더 사용

 

가장 쉬운 방법이다. 특정부분만 리빌드 하고 싶은 부분을 스트림 빌더로 감싸준다.

 

그리고 스트림컨트롤러르 생성한 뒤 스트림을 스트림빌더에만 내려준다. 즉 특정부분만 setState 하는 효과를 가지게 된다.

 

 

 

 

 

3. ChangeNotifier와 ListenableBuilder 조합 사용

https://api.flutter.dev/flutter/widgets/ListenableBuilder-class.html

 

ListenableBuilder class - widgets library - Dart API

A general-purpose widget for building a widget subtree when a Listenable changes. ListenableBuilder is useful for more complex widgets that wish to listen to changes in other objects as part of a larger build function. To use ListenableBuilder, construct t

api.flutter.dev

 

 

스트림 컨트롤러만 있는줄 알았는데 다른 방법도 있다. 구글도 역시 이런거 다 만들어 놓았네..

 

import 'package:flutter/material.dart';

/// Flutter code sample for a [ChangeNotifier] with a [ListenableBuilder].

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

class CounterModel with ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count += 1;
    notifyListeners();
  }
}

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

  @override
  State<ListenableBuilderExample> createState() =>
      _ListenableBuilderExampleState();
}

class _ListenableBuilderExampleState extends State<ListenableBuilderExample> {
  final CounterModel _counter = CounterModel();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('ListenableBuilder Example')),
        body: CounterBody(counterNotifier: _counter),
        floatingActionButton: FloatingActionButton(
          onPressed: _counter.increment,
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

class CounterBody extends StatelessWidget {
  const CounterBody({super.key, required this.counterNotifier});

  final CounterModel counterNotifier;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          const Text('Current counter value:'),
          // Thanks to the ListenableBuilder, only the widget displaying the
          // current count is rebuilt when counterValueNotifier notifies its
          // listeners. The Text widget above and CounterBody itself aren't
          // rebuilt.
          ListenableBuilder(
            listenable: counterNotifier,
            builder: (BuildContext context, Widget? child) {
              return Text('${counterNotifier.count}');
            },
          ),
        ],
      ),
    );
  }
}

 

 

 

 

 

 

4. ValueNotifier와 ValueListenableBuilder 조합 사용

https://medium.com/@thekavak/flutter-valuenotifier-with-examples-66b3933d7036

 

Flutter ValueNotifier with Examples

Introduction Flutter is a powerful framework for developing cross-platform mobile applications. One of its key features is the ability to…

medium.com

위와 비슷한 기능. 단 단일 밸류에만 유용함

 

ChangeNotifier 공식문서에서도 사용하는 예제가 있음

 

import 'package:flutter/material.dart';

/// Flutter code sample for a [ValueNotifier] with a [ListenableBuilder].

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

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

  @override
  State<ListenableBuilderExample> createState() =>
      _ListenableBuilderExampleState();
}

class _ListenableBuilderExampleState extends State<ListenableBuilderExample> {
  final ValueNotifier<int> _counter = ValueNotifier<int>(0);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('ListenableBuilder Example')),
        body: CounterBody(counterValueNotifier: _counter),
        floatingActionButton: FloatingActionButton(
          onPressed: () => _counter.value++,
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

class CounterBody extends StatelessWidget {
  const CounterBody({super.key, required this.counterValueNotifier});

  final ValueNotifier<int> counterValueNotifier;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          const Text('Current counter value:'),
          // Thanks to the ListenableBuilder, only the widget displaying the
          // current count is rebuilt when counterValueNotifier notifies its
          // listeners. The Text widget above and CounterBody itself aren't
          // rebuilt.
          ListenableBuilder(
            listenable: counterValueNotifier,
            builder: (BuildContext context, Widget? child) {
              return Text('${counterValueNotifier.value}');
            },
          ),
        ],
      ),
    );
  }
}
반응형

댓글