Flutter - State Management using PROVIDER

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.

What is 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

Existing providers

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 we perform network operation

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

Flutter - State Management using PROVIDER
1 Likes855.80 GEEK