Avoiding Unnecessary Rebuilds
When working with Provider in Flutter, you may encounter situations where widgets rebuild more often than necessary. These excessive rebuilds can degrade app performance and lead to inefficient UI updates. Understanding the common pitfalls that cause unnecessary widget rebuilds is crucial for building responsive and performant applications.
Some common pitfalls include:
- Using
Provider.of<T>(context)orConsumer<T>too broadly, causing entire widget subtrees to rebuild even when only a small part of the state has changed; - Placing state-dependent widgets too high in the widget tree, resulting in a large portion of the UI being rebuilt on every state change;
- Failing to use more granular tools like
Selector, which can lead to inefficiencies, asSelectorallows you to listen only to specific fields or computations rather than the entire model.
main.dart
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; void main() { runApp( ChangeNotifierProvider( create: (_) => CounterModel(), child: const MyApp(), ), ); } class CounterModel extends ChangeNotifier { int _count = 0; int get count => _count; void increment() { _count++; notifyListeners(); } } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Provider Rebuild Demo', home: Scaffold( appBar: AppBar(title: const Text('Provider Rebuild Demo')), body: const ComparisonExample(), ), ); } } class ComparisonExample extends StatelessWidget { const ComparisonExample({super.key}); @override Widget build(BuildContext context) { return Column( children: [ const Padding( padding: EdgeInsets.all(8.0), child: Text( '1. Using Consumer (may cause unnecessary rebuilds):', style: TextStyle(fontWeight: FontWeight.bold), ), ), const ConsumerRebuildWidget(), const Divider(), const Padding( padding: EdgeInsets.all(8.0), child: Text( '2. Using Selector (optimized):', style: TextStyle(fontWeight: FontWeight.bold), ), ), const SelectorRebuildWidget(), ], ); } } class ConsumerRebuildWidget extends StatelessWidget { const ConsumerRebuildWidget({super.key}); @override Widget build(BuildContext context) { print('ConsumerRebuildWidget rebuilt'); return Consumer<CounterModel>( builder: (context, counter, child) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Count: counter.count}'), IconButton( icon: const Icon(Icons.add), onPressed: counter.increment, ), const Padding( padding: EdgeInsets.only(left: 16.0), child: Text('This widget rebuilds on any CounterModel change.'), ), ], ); }, ); } } class SelectorRebuildWidget extends StatelessWidget { const SelectorRebuildWidget({super.key}); @override Widget build(BuildContext context) { print('SelectorRebuildWidget rebuilt'); return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Selector<CounterModel, int>( selector: (context, counter) => counter.count, builder: (context, count, child) { return Text('Count: $count'); }, ), IconButton( icon: const Icon(Icons.add), onPressed: () => context.read<CounterModel>().increment(), ), const Padding( padding: EdgeInsets.only(left: 16.0), child: Text('Only the count Text widget rebuilds.'), ), ], ); } }
There are two common ways to consume state with Provider.
- Rebuilds the whole widget subtree whenever the model changes
- Can cause unnecessary rebuilds in complex layouts
- Listens only to a specific value like
count - Rebuilds only the widget that depends on that value
Using Selector helps you optimize performance by updating only what truly needs to change.
Obrigado pelo seu feedback!
Pergunte à IA
Pergunte à IA
Pergunte o que quiser ou experimente uma das perguntas sugeridas para iniciar nosso bate-papo
Incrível!
Completion taxa melhorada para 7.14
Avoiding Unnecessary Rebuilds
Deslize para mostrar o menu
When working with Provider in Flutter, you may encounter situations where widgets rebuild more often than necessary. These excessive rebuilds can degrade app performance and lead to inefficient UI updates. Understanding the common pitfalls that cause unnecessary widget rebuilds is crucial for building responsive and performant applications.
Some common pitfalls include:
- Using
Provider.of<T>(context)orConsumer<T>too broadly, causing entire widget subtrees to rebuild even when only a small part of the state has changed; - Placing state-dependent widgets too high in the widget tree, resulting in a large portion of the UI being rebuilt on every state change;
- Failing to use more granular tools like
Selector, which can lead to inefficiencies, asSelectorallows you to listen only to specific fields or computations rather than the entire model.
main.dart
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; void main() { runApp( ChangeNotifierProvider( create: (_) => CounterModel(), child: const MyApp(), ), ); } class CounterModel extends ChangeNotifier { int _count = 0; int get count => _count; void increment() { _count++; notifyListeners(); } } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Provider Rebuild Demo', home: Scaffold( appBar: AppBar(title: const Text('Provider Rebuild Demo')), body: const ComparisonExample(), ), ); } } class ComparisonExample extends StatelessWidget { const ComparisonExample({super.key}); @override Widget build(BuildContext context) { return Column( children: [ const Padding( padding: EdgeInsets.all(8.0), child: Text( '1. Using Consumer (may cause unnecessary rebuilds):', style: TextStyle(fontWeight: FontWeight.bold), ), ), const ConsumerRebuildWidget(), const Divider(), const Padding( padding: EdgeInsets.all(8.0), child: Text( '2. Using Selector (optimized):', style: TextStyle(fontWeight: FontWeight.bold), ), ), const SelectorRebuildWidget(), ], ); } } class ConsumerRebuildWidget extends StatelessWidget { const ConsumerRebuildWidget({super.key}); @override Widget build(BuildContext context) { print('ConsumerRebuildWidget rebuilt'); return Consumer<CounterModel>( builder: (context, counter, child) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Count: counter.count}'), IconButton( icon: const Icon(Icons.add), onPressed: counter.increment, ), const Padding( padding: EdgeInsets.only(left: 16.0), child: Text('This widget rebuilds on any CounterModel change.'), ), ], ); }, ); } } class SelectorRebuildWidget extends StatelessWidget { const SelectorRebuildWidget({super.key}); @override Widget build(BuildContext context) { print('SelectorRebuildWidget rebuilt'); return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Selector<CounterModel, int>( selector: (context, counter) => counter.count, builder: (context, count, child) { return Text('Count: $count'); }, ), IconButton( icon: const Icon(Icons.add), onPressed: () => context.read<CounterModel>().increment(), ), const Padding( padding: EdgeInsets.only(left: 16.0), child: Text('Only the count Text widget rebuilds.'), ), ], ); } }
There are two common ways to consume state with Provider.
- Rebuilds the whole widget subtree whenever the model changes
- Can cause unnecessary rebuilds in complex layouts
- Listens only to a specific value like
count - Rebuilds only the widget that depends on that value
Using Selector helps you optimize performance by updating only what truly needs to change.
Obrigado pelo seu feedback!