Design Converter
Education
Software Development Executive - II
Last updated on Dec 26, 2024
Last updated on Dec 25, 2024
Hey Flutter developers! 👩💻👨💻 Ready to level up your app-building skills? In this blog, we’re diving into a game-changing concept that lies at the heart of every Flutter app: ChangeNotifier. If you've ever faced challenges with managing state and keeping your app’s components in sync, this post is for you.
State management in Flutter is crucial — it's what keeps your app running smoothly as components interact and evolve. But don’t worry, we’ll make it easy to understand and even easier to apply!
We’ll walk you through the magic of Flutter ChangeNotifier and ChangeNotifierProvider, giving you the tools to manage the Flutter state like a pro. Let’s explore how mastering this will take your Flutter development to the next level! 🌟
When it comes to state management in the Flutter SDK, one of the classes that can't be ignored is the ChangeNotifier class, a simple class that exposes a crucial part of the change notification API. The Flutter ChangeNotifier is a part of the Flutter Provider package, which allows for efficient management of the state of an application.
1class MyApp extends StatelessWidget { 2 @override 3 Widget build(BuildContext context) { 4 return ChangeNotifierProvider( 5 create: (context) => SomeClass(), 6 child: MaterialApp( 7 home: Scaffold( 8 appBar: AppBar(), 9 body: SomeWidget(), 10 ), 11 ), 12 ); 13 } 14}
The above code depicts a sample Flutter app inside which a ChangeNotifierProvider is created.
A ChangeNotifier is a way to manage and abstract your business logic from the user interface (UI). It's similar to scattering your app's state into different entities. Each entity manages its private state and exposes methods for manipulating this state while keeping the state's consistency.
In essence, ChangeNotifier is a class from the Flutter framework that provides a change notification API, using Void add and removing listeners to dispatch notifications, triggering a rebuild of large portions of your widget tree.
In the Dart language, the class ChangeNotifier has a simple set of methods and APIs as part of the Flutter package. This class encapsulates the process of adding listeners and removing them, as well as dispatching notifications to them. It's important to note that the state in our ChangeNotifier class is what we are concerned with; thus, we only notify listeners when there is a change.
1class MyNotifier extends ChangeNotifier { 2 int _value = 0; 3 int get value => _value; 4 5 void increment() { 6 _value++; 7 notifyListeners(); 8 } 9}
In this example, the _value represents the current state of the object, and whenever this value is incremented using the increment method, the notifyListeners() function is called, notifying all the listeners of the new state, causing them to rebuild.
The ChangeNotifier class in Flutter is a simple yet powerful tool for managing the state of an application. For example, let's examine a class that extends ChangeNotifier:
1class Counter extends ChangeNotifier { 2 int _count = 0; 3 int get count => _count; 4 5 void increment() { 6 _count++; 7 notifyListeners(); 8 } 9}
In this simple class, a count variable is used to manage a private state. This state can only be manipulated by the increment method, which increments the count and then calls notifyListeners(). This method allows for the dispatching of notifications whenever the state changes.
For a widget to listen to a class extending ChangeNotifier, it must be wrapped in a Consumer widget or use the context to listen to the ChangeNotifier directly.
1class CountDisplay extends StatelessWidget { 2 @override 3 Widget build(BuildContext context) { 4 var count = context.watch<Counter>().count; 5 return Text('Count: $count'); 6 } 7}
In CountDisplay, the widget listens for any changes in Counter and rebuilds automatically when Counter changes due to the watch method.
The Consumer widget can also be used as follows to handle change notifications:
1Consumer<Counter>( 2 builder: (context, counter, child) => Text('Count: ${counter.count}'), 3)
The ChangeNotifierProvider is one of the classes from the provider package that links the lifecycle of a ChangeNotifier to widgets on the widget tree. It is responsible for creating and supplying an instance of a ChangeNotifier directly to the UI.
1class MyApp extends StatelessWidget { 2 @override 3 Widget build(BuildContext context) { 4 return ChangeNotifierProvider( 5 create: (context) => TaskData(), 6 child: MaterialApp( 7 home: TasksScreen(), 8 ), 9 ); 10 } 11}
In MyApp, it creates an instance of the TaskData class and then offers it to all consumer widgets that are its children in the widget tree. It handles the lifecycle of the ChangeNotifier for us. The 'create' method is in charge of creating a new instance of TaskData.
1class TaskData extends ChangeNotifier { 2 List<Task> _tasks = [ 3 Task(name: 'Buy milk'), 4 ]; 5 6 UnmodifiableListView<Task> get tasks { 7 return UnmodifiableListView(_tasks); 8 } 9 10 void addTask(String newTaskTitle) { 11 final task = Task(name: newTaskTitle); 12 _tasks.add(task); 13 notifyListeners(); 14 } 15}
In TaskData, which extends ChangeNotifier, we maintain a private list of tasks and expose it through a getter. We also provide an addTask function to manipulate the state.
Having understood what Flutter ChangeNotifier and ChangeNotifierProvider are, let's see how to implement them in a Flutter application. We'll create a simple counter application.
1class Counter with ChangeNotifier { 2 int _value = 0; 3 4 int get value => _value; 5 6 void increment() { 7 _value++; 8 notifyListeners(); 9 } 10}
In this Counter class, the value is exposed through a getter for read-only access. To change the value, we have increment(). notifyListerners() is crucial here as it informs listening widgets of the changes.
1void main() => runApp( 2 ChangeNotifierProvider( 3 create: (context) => Counter(), 4 child: MyApp(), 5 ), 6);
This code creates an instance of Counter and provides it to our widget tree.
1class CounterText extends StatelessWidget { 2 @override 3 Widget build(BuildContext context) { 4 return Text( 5 'Value: ${context.watch<Counter>().value}', 6 style: Theme.of(context).textTheme.headline4, 7 ); 8 } 9}
In the CounterText widget, we subscribe to changes using context.watch(). This will cause our widget to rebuild whenever notifyListeners() is called in our ChangeNotifier.
Consider an app whose primary function is to display a list of notes. The notes' information is dynamic and can be updated at any time, hence requiring state management. Here's how we can implement this using Flutter ChangeNotifier:
First, we define a simple Note class and our NoteProvider, which extends ChangeNotifier.
1class Note { 2 String content; 3 4 Note(this.content); 5} 6 7class NoteProvider extends ChangeNotifier { 8 List<Note> _notes = []; 9 10 void addNote(String content) { 11 _notes.add(Note(content)); 12 notifyListeners(); 13 } 14}
Our ChangeNotifierProvider would then be:
1void main() { 2 runApp( 3 ChangeNotifierProvider( 4 create: (context) => NoteProvider(), 5 child: MyApp(), 6 ), 7 ); 8}
We can then access our notes for display through a consumer in our UI:
1class NoteList extends StatelessWidget { 2 @override 3 Widget build(BuildContext context) { 4 final notes = context.watch<NoteProvider>().notes; 5 6 return ListView.builder( 7 itemCount: notes.length, 8 itemBuilder: (context, index) => Text(notes[index].content), 9 ); 10 } 11}
Here are some common mistakes Flutter developers might make when working with Flutter ChangeNotifier and ChangeNotifierProvider, along with solutions:
context.read
in build function. Solution: It's best practice to avoid using context.read<ChangeNotifier>()
inside the build function, as it can lead to unnecessary rebuilds of your widget. Instead, use context.watch<ChangeNotifier>()
or Provider.of<ChangeNotifier>(context)
.notifyListeners()
after updating state. Solution: Always ensure notifyListeners()
is called after any function that modifies the data within the ChangeNotifier. This triggers the change notification and causes the listeners to rebuild, thus reflecting changes in the UI.While ChangeNotifier in Flutter provides a straightforward way to manage state, alternative approaches are available depending upon the complexity and requirements of your applications.
Choosing the appropriate state management solution depends largely on the specific requirements of your project and the complexity of the state to manage.
As we've explored in this guide, Flutter ChangeNotifier and ChangeNotifierProvider offer a powerful yet simple solution for managing your app's state. By efficiently decoupling your business logic from the UI, these tools enable smoother, more scalable applications. Whether you're building a small app or a more complex project, mastering ChangeNotifier equips you with a flexible and reliable method for handling state changes.
While we've touched on alternatives like BLoC, Redux, and Riverpod, the choice of state management solution depends largely on your app's size and complexity. The key takeaway here is that with the right approach, you can create apps that are not only functional but also easy to debug and maintain. We hope this post has sparked new insights into your Flutter development journey. Keep experimenting, coding, and pushing the boundaries of what you can create with Flutter. Happy coding!
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.