Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Learn Avoiding Unnecessary Rebuilds | Provider Essentials
Practice
Projects
Quizzes & Challenges
Quizzes
Challenges
/
Flutter State Management Fundamentals

bookAvoiding 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) or Consumer<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, as Selector allows you to listen only to specific fields or computations rather than the entire model.
main.dart

main.dart

copy
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
import '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.

Consumer<CounterModel>
expand arrow
  • Rebuilds the whole widget subtree whenever the model changes
  • Can cause unnecessary rebuilds in complex layouts
Selector<CounterModel, int>
expand arrow
  • 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.

question mark

Which of the following best describes a best practice for avoiding unnecessary widget rebuilds when using Provider?

Select the correct answer

Everything was clear?

How can we improve it?

Thanks for your feedback!

SectionΒ 3. ChapterΒ 5

Ask AI

expand

Ask AI

ChatGPT

Ask anything or try one of the suggested questions to begin our chat

bookAvoiding Unnecessary Rebuilds

Swipe to show 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) or Consumer<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, as Selector allows you to listen only to specific fields or computations rather than the entire model.
main.dart

main.dart

copy
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
import '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.

Consumer<CounterModel>
expand arrow
  • Rebuilds the whole widget subtree whenever the model changes
  • Can cause unnecessary rebuilds in complex layouts
Selector<CounterModel, int>
expand arrow
  • 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.

question mark

Which of the following best describes a best practice for avoiding unnecessary widget rebuilds when using Provider?

Select the correct answer

Everything was clear?

How can we improve it?

Thanks for your feedback!

SectionΒ 3. ChapterΒ 5
some-alt