In this section, we will explore the Provider package, a popular state management solution in Flutter. The Provider package is built on top of InheritedWidget and offers a simple and efficient way to manage state in your Flutter applications.
What is the Provider Package?
The Provider package is a wrapper around InheritedWidget, making it easier to manage and propagate state changes throughout your widget tree. It helps in:
- Decoupling business logic from UI code.
- Simplifying state management.
- Improving code readability and maintainability.
Key Concepts
- Provider
The core class that allows you to expose a value to the widget tree. It listens for changes and rebuilds the widgets that depend on the provided value.
- ChangeNotifier
A class that provides change notifications to its listeners. It is commonly used with Provider to notify widgets about state changes.
- Consumer
A widget that listens to changes in the provided value and rebuilds itself when the value changes.
Setting Up Provider
To use the Provider package, you need to add it to your pubspec.yaml
file:
Then, run flutter pub get
to install the package.
Basic Example
Let's create a simple counter app using the Provider package.
Step 1: Create a ChangeNotifier Class
First, create a class that extends ChangeNotifier
to manage the state:
import 'package:flutter/foundation.dart'; class Counter with ChangeNotifier { int _count = 0; int get count => _count; void increment() { _count++; notifyListeners(); } }
Step 2: Provide the ChangeNotifier
Wrap your app with a ChangeNotifierProvider
to provide the Counter
instance to the widget tree:
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'counter.dart'; // Import the Counter class void main() { runApp( ChangeNotifierProvider( create: (context) => Counter(), child: MyApp(), ), ); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: CounterScreen(), ); } }
Step 3: Consume the Provided Value
Use the Consumer
widget to listen to changes in the Counter
instance and rebuild the UI accordingly:
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'counter.dart'; // Import the Counter class class CounterScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Provider Counter Example'), ), body: Center( child: Consumer<Counter>( builder: (context, counter, child) { return Text( 'Count: ${counter.count}', style: TextStyle(fontSize: 24), ); }, ), ), floatingActionButton: FloatingActionButton( onPressed: () { context.read<Counter>().increment(); }, child: Icon(Icons.add), ), ); } }
Practical Exercise
Exercise: Create a Todo App
Objective: Create a simple Todo app using the Provider package.
Steps:
- Create a
Todo
class with propertiesid
,title
, andisCompleted
. - Create a
TodoList
class that extendsChangeNotifier
to manage a list of todos. - Provide the
TodoList
instance to the widget tree usingChangeNotifierProvider
. - Create a screen to display the list of todos and a form to add new todos.
- Use
Consumer
to listen to changes in theTodoList
and rebuild the UI.
Solution:
- Create the
Todo
class:
class Todo { final String id; final String title; bool isCompleted; Todo({ required this.id, required this.title, this.isCompleted = false, }); }
- Create the
TodoList
class:
import 'package:flutter/foundation.dart'; import 'todo.dart'; // Import the Todo class class TodoList with ChangeNotifier { List<Todo> _todos = []; List<Todo> get todos => _todos; void addTodo(Todo todo) { _todos.add(todo); notifyListeners(); } void toggleTodoStatus(String id) { final todo = _todos.firstWhere((todo) => todo.id == id); todo.isCompleted = !todo.isCompleted; notifyListeners(); } }
- Provide the
TodoList
instance:
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'todo_list.dart'; // Import the TodoList class void main() { runApp( ChangeNotifierProvider( create: (context) => TodoList(), child: MyApp(), ), ); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: TodoScreen(), ); } }
- Create the
TodoScreen
:
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'todo_list.dart'; // Import the TodoList class import 'todo.dart'; // Import the Todo class class TodoScreen extends StatelessWidget { final TextEditingController _controller = TextEditingController(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Provider Todo Example'), ), body: Column( children: [ Padding( padding: const EdgeInsets.all(8.0), child: TextField( controller: _controller, decoration: InputDecoration( labelText: 'Add Todo', border: OutlineInputBorder(), ), ), ), ElevatedButton( onPressed: () { final todo = Todo( id: DateTime.now().toString(), title: _controller.text, ); context.read<TodoList>().addTodo(todo); _controller.clear(); }, child: Text('Add'), ), Expanded( child: Consumer<TodoList>( builder: (context, todoList, child) { return ListView.builder( itemCount: todoList.todos.length, itemBuilder: (context, index) { final todo = todoList.todos[index]; return ListTile( title: Text(todo.title), trailing: Checkbox( value: todo.isCompleted, onChanged: (value) { todoList.toggleTodoStatus(todo.id); }, ), ); }, ); }, ), ), ], ), ); } }
Summary
In this section, we learned about the Provider package and how it simplifies state management in Flutter applications. We covered:
- The key concepts of Provider, ChangeNotifier, and Consumer.
- How to set up and use the Provider package.
- A practical example of a counter app.
- An exercise to create a Todo app using Provider.
By mastering the Provider package, you can efficiently manage state in your Flutter applications, leading to cleaner and more maintainable code. In the next section, we will explore another state management solution: Riverpod.
Flutter Development Course
Module 1: Introduction to Flutter
- What is Flutter?
- Setting Up the Development Environment
- Understanding Flutter Architecture
- Creating Your First Flutter App
Module 2: Dart Programming Basics
- Introduction to Dart
- Variables and Data Types
- Control Flow Statements
- Functions and Methods
- Object-Oriented Programming in Dart
Module 3: Flutter Widgets
- Introduction to Widgets
- Stateless vs Stateful Widgets
- Basic Widgets
- Layout Widgets
- Input and Form Widgets
Module 4: State Management
Module 5: Navigation and Routing
Module 6: Networking and APIs
- Fetching Data from the Internet
- Parsing JSON Data
- Handling Network Errors
- Using REST APIs
- GraphQL Integration
Module 7: Persistence and Storage
- Introduction to Persistence
- Shared Preferences
- File Storage
- SQLite Database
- Using Hive for Local Storage
Module 8: Advanced Flutter Concepts
- Animations in Flutter
- Custom Paint and Canvas
- Platform Channels
- Isolates and Concurrency
- Performance Optimization
Module 9: Testing and Debugging
Module 10: Deployment and Maintenance
- Preparing for Release
- Building for iOS
- Building for Android
- Continuous Integration/Continuous Deployment (CI/CD)
- Maintaining and Updating Your App