Discount !! E-commerce App With Backend Source Code Video and Voice Chatting App Firebase Chatting App Source Code Complete Gym App BLoC State Management Source Code Complete Study App Buy Ticket Booking App Source Code Payment App Buy Travel App With Backend Source Code Complete Chat App Udemy Course Special Offer Online Learning Course App (BLoC)
Flutter Riverpod state management is an upgrade of Provider state management. It provides very convenient way to manage states and seperate the business logic from UI.
To install Riverpod run
flutter pub get flutter_riverpod
After that the entry point would be your main() function of your app. You need to wrap your child inside ProviderScope . It's the entry point our Riverpod state management.
void main() {
runApp(ProviderScope(child: MyApp()));
}
You see ProviderScope() holds our MyApp()
In Riverpod when you create a state you need to do it using Providers.
What is state then?
States actually refers to special memory in the system where you data is stored. When we create states using Riverpod, it holds our data and manage the changes of the data and let the UI know about it.
Providers are the most important components of Riverpod. In short, you can think of providers as an access point to a shared state.
There are many Providers in state management. There are six types of them and each of them have different usage.
From the very simplest to the complex one.
Provider
StateProvider
StateNotifierProvider
FutureProvider
StreamProvider
ChangeNotifierProvider
The above six Providers, they all have different usage.
Provider & StateProvider
Look how to use Provider and StateProvider in the below example.
In the example Provider and StateProvider work together to work on boolean values and string values.
The very basic Provider can not be changed from ConsumerWidget, we can only read it. But StateProvider could be changed from ConsumerWidget.
StateProvider deals with basic data types like integer, string and boolean while Provider can deal with any types of data.
final selectedButtonProvider = StateProvider<String>((ref) => '');
For now, we just returned an empty String from StateProvider. Later we will see how change the String value.
selectedButtonProvider is our new provider and we will change the data value by invoking notifer object. Once you Riverpod for state management, notifier object and state object would be available.
Inside the ElevatedButton() we used this property to toggle the string values.
ElevatedButton(
onPressed: () => ref.read(selectedButtonProvider.notifier).state = 'red',
child: Text('Red'),
),
This notifier object is not available for basic Provider. The other elevated button has done the same thing.
Here we used ref.read() to access our Provider and change it's value. In general we will use ref.read() for changing data, and ref.watch() for reading data.
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Riverpod App"),
),
body: SelectedButton(),
),
);
}
}
final isRedProvider = Provider<bool>((ref){
final color = ref.watch(selectedButtonProvider);
return color == 'red'; // true if red
});
final selectedButtonProvider = StateProvider<String>((ref) => '');
class SelectedButton extends ConsumerWidget {
const SelectedButton({ Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final isRed = ref.watch(isRedProvider);
final selectedButton = ref.watch(selectedButtonProvider);
return Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(selectedButton),
ElevatedButton(
onPressed: () => ref.read(selectedButtonProvider.notifier).state = 'red',
child: Text('Red'),
),
ElevatedButton(
// 5
onPressed: () => ref.read(selectedButtonProvider.notifier).state = 'blue',
child: Text('Blue'),
),
isRed ? Text('Color is red') : Text('Color is blue')
],
),
);
}
}
StateNotifierProvider
This provider is used to expose the held by StateNotifier. With StateNotifier you can deal with any kinds of data type.
So if your data type is complex like List, Maps or custom objects, then StateNotifier is the way to go. Your data would be held inside StateNotifier and they would be exposed to the outside world, I mean to the ConsumerWidgets by StateNotifierProvider.
Here we used StateNotifier and StateNotifierProvider to do CRUD operations. Inside StateNotifier we use List<String> type data.
StateNotifier must be extended by another class, In the super class constructor there should be initialization of data.
class NumberNotifier extends StateNotifier<List<String>> {
NumberNotifier() : super(['number 12', 'number 30']);
}
We created a class name NumberNotifier which extends StateNotifier. Let's see how exposed the shared data to ConsumerWidget.
final numbersProvider =
StateNotifierProvider<NumberNotifier, List<String>>((ref) {
return NumberNotifier();
});
Now using numbersProvider, we would be able to access the data List of Strings from UI.
We can use NumberNotifier() class as a normal class and add different kinds of methods for our useage. Later we will see that we added CRUD methods to it.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main(){
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
final numbersProvider =
StateNotifierProvider<NumberNotifier, List<String>>((ref) {
return NumberNotifier();
});
class NumberNotifier extends StateNotifier<List<String>> {
NumberNotifier() : super(['number 12', 'number 30']);
void add(String number) {
state = [...state, number];
}
void remove(String number) {
state = [...state.where((element) => element != number)];
}
void update(String number, String updatedNumber) {
final updatedList = <String>[];
for (var i = 0; i < state.length; i++) {
if (state[i] == number) {
updatedList.add(updatedNumber);
} else {
updatedList.add(state[i]);
}
}
state = updatedList;
}
}
class HomePage extends ConsumerWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final numbers = ref.watch(numbersProvider);
return Scaffold(
appBar: AppBar(title: const Text('Riverpod')),
floatingActionButton: FloatingActionButton(
onPressed: () {
ref
.read(numbersProvider.notifier)
.add('number ${Random().nextInt(100)}');
},
child: const Icon(Icons.add),
),
body: Center(
child: Column(
children: numbers.map((e) => GestureDetector(
onLongPress: () {
ref
.read(numbersProvider.notifier)
.update(e, '${e } '+Random().nextInt(1000).toString());
},
onTap: () {
ref.read(numbersProvider.notifier).remove(e);
},
child: Padding(
padding: EdgeInsets.all(10),
child: Text(e),
),
)).toList()
),
),
);
}
}