StateManagement in flutter with provider.

Provider provides a good way to manage the state and separate the business logic and UI components using the provider.
Installing
Add the provider dependencies in pubspec.yaml file. Get the latest version from https://pub.dev/packages/provider/install
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
provider: ^5.0.0
Know before you dive inside the code
Basically provider allow to pass the data to all its children. Notifier provider works as a bridge to listen the changes or events in model and pass the information to consumer. Finally consumer are the child widgets who changes or act when changes are needed.
let take an example, we need to show the car list that is available from API. first we will show the loader and later on when data is fetched from server the list view is updated to show the actual data.
This process can be break down in three simple steps
- Car model
- CarChangeNotificationProvider
- Consumer
CarModel
import 'package:flutter/cupertino.dart';
import 'package:json_annotation/json_annotation.dart';part 'Car.g.dart';
@JsonSerializable()
class Car extends ChangeNotifier {
String? id;
@JsonKey(name: "unique_id")
String? uniqueId;
String? title;
String? description;
@JsonKey(ignore: true)
List<Car>? carList;factory Car.fromJson(Map<String, dynamic> json) =>
_$CarFromJson(json);
Map<String, dynamic> toJson() => _$CarToJson(this);
syncFromNetwork(Map<String, String> filterParams) {
print("syncFromNetwork");
http.get(ENDPOINT_URL).then((List<Car> mCarList) {
carList = mCarList;
notifyListeners();
}, onError: (error) {
this.errorMessage = error.toString();
});
} static toList(List data) =>
List.generate(data.length, (index) =>
Car.fromJson(data[index]));}
To know more about serialization refer to this link https://pub.dev/packages/json_serializable
In the above example, Car extends the ChangeNotifer and when car items are fetched it notifies by notifyListener.
ChangeNotifier Provider
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Provider Sample app',
debugShowCheckedModeBanner: false,
initialRoute: "/",
routes: {
"/": (context) => HomePage(),
},
onGenerateRoute: (settings) {
if (settings.name == "/${CarlistPage.TAG}") {
CarFilter mCarFilter = settings.arguments as CarFilter;
return MaterialPageRoute(
builder: (context) {
return Provider(
providers: [
ChangeNotifierProvider(create: (context) =>
Car()),
],
child: CarlistPage();
},
);
}
},
);
}
}
Provider pass the information to its child when model notifies. Since it rebuild all child below, we should be careful while placing the provider.
And finally list page to show the data.
import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:smcsale/model/Car.dart';
class CarlistPage extends StatelessWidget {
static String TAG = "carlist";
Car? carNotifier;
CarlistPage();
_carListItem(BuildContext context, Car car) {
return InkWell(
onTap: () {
Navigator.pushNamed(context, "/${CarDetailPage.TAG}", arguments: car);
},
child: Container(
),
);
}
@override
Widget build(BuildContext context) {
carNotifier = Provider.of<Car>(context, listen: false);
carNotifier!.syncFromNetwork(carFilter!.toMap());
return Scaffold(
backgroundColor: Colors.white70,
appBar: AppBar(
centerTitle: true,
title: Text(
carFilter!.title,
style: TextStyle(fontWeight: FontWeight.bold),
),
actions: [
],
),
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Colors.white),
padding: EdgeInsets.only(left: 16.0, right: 16.0),
child: Consumer<Car>(builder: (context, car, child) {
if (car.carList != null && car.carList!.length > 0)
return ListView.separated(
shrinkWrap: true,
separatorBuilder: (context, position) => Container(
width: MediaQuery.of(context).size.width,
height: 2.0,
decoration: BoxDecoration(color: Colors.black12),
),
itemCount: car.carList!.length,
itemBuilder: (context, position) =>
_carListItem(context, car.carList![position]),
);
} else {
return Center(child: Text("No car list found"));
}
}),
));
}
}
Car is initialized where it is necessary. Listen is set here false as we don’t need to rebuild the widget every time there new data list is fetched. Consumer will only update its child data.
Sorry for my english if grammars are not correct as i am not native english speaker.Happy Coding.