In this tutorial you will see the very basics of implementing “Provider” for State management in your Flutter Applications.
So Let’s get started
Before looking into providers lets see whatsis ChangeNotifier
this plugin uses ChangeNotifier
to to listen and update any changes.
ChangeNotifier
Form docs
A class that can be extended or mixed in that provides a change notification API using [VoidCallback] for notifications.> [ChangeNotifier] is optimized for small numbers (one or two) of listeners. It is O(N) for adding and removing listeners and O(N²) for dispatching notifications (where N is the number of listeners)##
provider
exposes a few different kinds of “provider” for different types of objects.
Let’s get started with our code
first things first let add plugin to pubspec.yaml
provider: ^2.0.1
http: ^0.12.0+2
Let’s Write our provider class first we name it AppState
import 'package:flutter/material.dart';
class AppState with ChangeNotifier {
AppState();
String _displayText = "";
void setDisplayText(String text) {
_displayText = text;
notifyListeners();
}
String get getDisplayText => _displayText;
}
Our AppState
is extended with ChangeNotifier
which is used to notify its listeners when we call notifyListeners()
In the code, we declared two methods setDisplayText
and getDisplayText
which are used to read and write the value in our state
Now we move to our main.dart
import 'package:flutter/material.dart';
import 'package:flutter_demo_provider/app_state.dart';
import 'package:flutter_demo_provider/text_display.dart';
import 'package:flutter_demo_provider/text_edit.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ChangeNotifierProvider<AppState>(
builder: (_) => AppState(),
child: MyHomePage(),
));
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
padding: const EdgeInsets.all(16.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
TextDisplay(),
TextEditWidget(),
],
),
),
),
);
}
}
In the code, we can notice we have used ChangeNotifierProvider
which is provided from out provider
plugin
It accepts two parameters one is builder
and the other is child
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ChangeNotifierProvider<AppState>(
builder: (_) => AppState(),
child: MyHomePage(),
));
}
Inside the MyHomePage
we have a Scaffold
with Column
which has two Widgets TextDisplay()
and TextEditWidget()
TextDisplay(),
TextEditWidget(),
Here is out TextDisplay()
in text_display.dart
import 'package:flutter/material.dart';
import 'package:flutter_demo_provider/app_state.dart';
import 'package:provider/provider.dart';
class TextDisplay extends StatefulWidget {
@override
_TextDisplayState createState() => _TextDisplayState();
}
class _TextDisplayState extends State<TextDisplay> {
@override
Widget build(BuildContext context) {
final appState = Provider.of<AppState>(context);
return Container(
padding: const EdgeInsets.all(16.0),
child: Text(
appState.getDisplayText,
style: TextStyle(
fontSize: 24.0,
),
),
);
}
}
In the above code we see
Widget build(BuildContext context) {
final appState = Provider.of<AppState>(context);
return Container(
padding: const EdgeInsets.all(16.0),
child: Text(
appState.getDisplayText,
style: TextStyle(
fontSize: 24.0,
),
),
);
}
final appState = Provider.of<AppState>(context);
This above line of code will get the provider for listening for any changes optionally we can also opt-out for listening by proving listen: false
final appState = Provider.of<AppState>(context, listen: false);
Now in order to access text, we have a function in our provider called getDisplayText
appState.getDisplayText()
Here is out TextEditWidget()
in text_edit.dart
import 'package:flutter/material.dart';
import 'package:flutter_demo_provider/app_state.dart';
import 'package:provider/provider.dart';
class TextEditWidget extends StatefulWidget {
@override
_TextEditWidgetState createState() => _TextEditWidgetState();
}
class _TextEditWidgetState extends State<TextEditWidget> {
TextEditingController _textEditingController = TextEditingController();
@override
Widget build(BuildContext context) {
final appState = Provider.of<AppState>(context);
return Container(
child: TextField(
controller: _textEditingController,
decoration: InputDecoration(
labelText: "Some Text",
border: OutlineInputBorder(),
),
onChanged: (changed) => appState.setDisplayText(changed),
onSubmitted: (submitted) => appState.setDisplayText(submitted),
),
);
}
}
In the above code, we get our appState inside the build
function
final appState = Provider.of<AppState>(context);
In order to manipulate the text in the state we call setDisplayText(text)
function
TextField(
controller: _textEditingController,
decoration: InputDecoration(
labelText: "Some Text",
border: OutlineInputBorder(),
),
onChanged: (changed) => appState.setDisplayText(changed),
onSubmitted: (submitted) => appState.setDisplayText(submitted),
)
we are updating the state whenever out text is changes
onChanged: (changed) => appState.setDisplayText(changed)
Now inside our app state, we have some additional functions and variables
String _dataUrl = "https://reqres.in/api/users?per_page=20";
String _jsonResonse = "";
bool _isFetching = false;
bool get isFetching => _isFetching;
Future<void> fetchData() async {
_isFetching = true;
notifyListeners();
var response = await http.get(_dataUrl);
if (response.statusCode == 200) {
_jsonResonse = response.body;
}
_isFetching = false;
notifyListeners();
}
String get getResponseText => _jsonResonse;
List<dynamic> getResponseJson() {
if (_jsonResonse.isNotEmpty) {
Map<String, dynamic> json = jsonDecode(_jsonResonse);
return json['data'];
}
return null;
}
Here we have a few more functions fetchData
, getResponseText
and getResponseJson
fetchData
will perform the network operation and update the variable with the response data (you can parse your JSON to custom model here and save it in a List
)
getResponseText
will return the plain text response
getResponseJson
will convert the response text to a Map
and returndata
field inside it which is a list of Map
To see the final app_state.dart
visit below link
Now inside out MyHomePage
widget we add two more widgets to our column
RaisedButton(
onPressed: () => appState.fetchData(),
child: Text("Fetch Data from Network"),
),
ResponseDisplay(),
So I am calling appState.fetchData()
whenever I press the button now fetchData
will take care of all the updating of the state on Github
Here is our ResponseDisplay
Widget named response_display.dart
import 'package:flutter/material.dart';
import 'package:flutter_demo_provider/app_state.dart';
import 'package:provider/provider.dart';
class ResponseDisplay extends StatefulWidget {
@override
_ResponseDisplayState createState() => _ResponseDisplayState();
}
class _ResponseDisplayState extends State<ResponseDisplay> {
@override
Widget build(BuildContext context) {
final appState = Provider.of<AppState>(context);
return Container(
padding: const EdgeInsets.all(16.0),
child: appState.isFetching
? CircularProgressIndicator()
: appState.getResponseJson() != null
? ListView.builder(
primary: false,
shrinkWrap: true,
itemCount: appState.getResponseJson().length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(
appState.getResponseJson()[index]['avatar']),
),
title: Text(
appState.getResponseJson()[index]["first_name"],
),
);
},
)
: Text("Press Button above to fetch data"),
);
}
}
Here in the above code we parse the JSON and build a list of data
To get the code it’s here on GitHub
#flutter #mobile-apps