Flutter

덩치가 커진 위젯의 rebuild

붕어사랑 티스토리 2024. 3. 8. 13:50
반응형

플러터를 하다보면 하나의 위젯이 덩치가 어마어마하게 커지는 경우가 있다. 이때 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}');
            },
          ),
        ],
      ),
    );
  }
}
반응형