Open In App

Flutter – Provider Package

Last Updated : 01 Feb, 2021
Improve
Improve
Like Article
Like
Save
Share
Report

The provider package is an easy to use package which is basically a wrapper around the InheritedWidgets that makes it easier to use and manage. It provides a state management technique that is used for managing a piece of data around the app.

The basic classes available in the provider package are –

  • ChangeNotifierProvider<T extends ChangeNotifier> It listens to a ChangeNotifier extended by the model class, exposes it to its children and descendants, and rebuilds depends whenever notifyListeners is called.
ChangeNotifierProvider(
  create: (context) => DataModel(),
  child: ...
)
  • Consumer<T> It obtains the provider from its ancestors and passes the value obtained to the builder.
@override
 Widget build(BuildContext context) {
   return Consumer<DataModel>(
     builder: (context, data, child) => DataWidget(par1: par1, par2: par2),
     child: Text(data.first),
   );
 }
  • FutureProvider<T> This class listens for a Future and then passes its values to its children and descendants.
Constructors
FutureProvider<T>(
    {Key key,
    @required Create<Future<T>> create,
    T initialData,
    ErrorBuilder<T> catchError,
    UpdateShouldNotify<T> updateShouldNotify,
    bool lazy,
    TransitionBuilder builder,
    Widget child}
)
This creates a Future from create and subscribes to it.

FutureProvider.value(
    {Key key, 
    @required Future<T> value, 
    T initialData, 
    ErrorBuilder<T> catchError, 
    UpdateShouldNotify<T> updateShouldNotify, 
    TransitionBuilder builder, 
    Widget child}
    )
This constructor notifies the changed values to the FutureProvider children.

Ex: FutureProvider<Model>(create: (context) =>
 Model(),)
  • InheritedProvider<T> The InheritedProvider provides a general implementation of the InheritedWidget.
  • MultiProvider A provider that is used to provide more than one class at the same time.
MultiProvider(
  providers: [
    Provider<Model1>(create: (context) => Model1()),
    StreamProvider<Model2>(create: (context) => Model2()),
    FutureProvider<Model3>(create: (context) => Model3()),
  ],
  child: someWidget,
)
  • Provider<T> It is the basic provider.
  • ProxyProvider<T, R> This provider depends on other providers for value. The value can be used by create or update.
Constructor
ProxyProvider(
    {Key key, 
    Create<R> create, 
    @required ProxyProviderBuilder<T, R> update, 
    UpdateShouldNotify<R> updateShouldNotify, 
    Dispose<R> dispose, bool lazy, 
    TransitionBuilder builder, 
    Widget child}
)
This initializes key for subclasses.
  • StreamProvider<T> This class listens for a Stream and then passes its values to its children and descendants. This can be used as
Constructors
StreamProvider<T>(
    {Key key,
    @required Create<Stream<T>> create,
    T initialData,
    ErrorBuilder<T> catchError,
    UpdateShouldNotify<T> updateShouldNotify,
    bool lazy,
    TransitionBuilder builder,
    Widget child}
)
This creates a stream using create and subscribes to it.

StreamProvider.value(
    {Key key, 
    @required Stream<T> value, 
    T initialData, 
    ErrorBuilder<T> catchError, 
    UpdateShouldNotify<T> updateShouldNotify, 
    bool lazy, 
    TransitionBuilder builder, 
    Widget child}
)
This constructor notifies the changed values to the StreamProvider children.

Ex: StreamProvider<Model>(create: (context) =>
 Model(),)
  • ValueListenableProvider<T> This class receives changes in value by subscribing to a ValueListenable.
ValueListenableProvider<T>.value(
    {Key key,
    @required ValueListenable<T> value,
    UpdateShouldNotify<T> updateShouldNotify,
    TransitionBuilder builder,
    Widget child}
)
This constructor shows the changed values to its children.

Apart from these, there are a number of other classes that are available depending upon the need but these are the most used classes.

For using the provider package we need to add the provider package to the dependencies section of pubspec.yaml and click on the get button to get the dependencies.

dependencies:
  flutter:
    sdk: flutter
  provider: ^4.3.2+4 #ADD

We will be looking at a simple example app

First of all, we will be defining a model library inside of the lib folder which consists of item.dart and item_data.dart. Apart from these, the lib will have 3 more dart files namely the main.dart, home.dart, and item_list.dart. 

The item.dart is a simple class that defines what are the attributes that the item class will hold and a toggle method.

Dart




import 'package:flutter/foundation.dart';
  
class Item {
  String item;
  bool completed;
  Item({@required this.item, this.completed = false});
  void toggle() {
    completed = !completed;
  }
}


The item_data.dart contains a list that will hold the data of the Item class defined above. There are methods to perform tasks such as add, toggle, and remove an item from the list.

Dart




import 'dart:collection';
import 'package:flutter/foundation.dart';
import '../model/item.dart';
  
class ItemData with ChangeNotifier {
  List<Item> _items = [];
  UnmodifiableListView<Item> get items => UnmodifiableListView(_items);
  
  get size => _items.length;
  
  void addItem(Item item) {
    _items.add(item);
    notifyListeners();
  }
  
  void toggleItem(Item item) {
    item.toggle();
    notifyListeners();
  }
  
  void removeItem(Item item) {
    _items.remove(item);
    notifyListeners();
  }
}


Now that the model is defined we will clean up the main.dart as

Dart




import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'model/item_data.dart';
import 'home.dart';
  
void main() {
  runApp(MyApp());
}
  
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => ItemData(),
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'Provider Demo',
        theme: ThemeData(
          primarySwatch: Colors.green,
        ),
        home: Home(),
      ),
    );
  }
}


The main.dart has a ChangeNotifierProvider which acts as a parent to the material app. As our app is quite small we have defined the provider at the top only. In case your app is quite large you can place the provider at the top of the widget that needs the data and not on the top.

The item_list.dart creates a ListView builder of the data coming from the list. It uses the Consumer to get the data.

Dart




import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'model/item_data.dart';
  
class ItemList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<ItemData>(builder: (context, data, child) {
      return ListView.builder(
        scrollDirection: Axis.vertical,
        shrinkWrap: true,
        itemCount: data.size,
        itemBuilder: (context, index) {
          final item = data.items[index];
          return GestureDetector(
            onLongPress: () => data.removeItem(item),
            child: Container(
              padding: EdgeInsets.symmetric(vertical: 5),
              child: ListTile(
                leading: CircleAvatar(
                  backgroundColor: Colors.blueGrey,
                  child: Text(item.item[0]),
                ),
                title: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(
                      item.item,
                      style: TextStyle(
                          decoration: item.completed
                              ? TextDecoration.lineThrough
                              : null,
                          fontSize: 16,
                          fontWeight: FontWeight.bold),
                    ),
                    Checkbox(
                      value: item.completed,
                      onChanged: (c) => data.toggleItem(item),
                    ),
                  ],
                ),
              ),
            ),
          );
        },
      );
    });
  }
}


At last, we will be writing the code for the home.dart file which contains the data that is to be displayed on the screen. It also contains a TextField and a button to add the data to the list.

Output:

Apart from Provider, there are other State Management also available such as –

  • StatefulWidget: These are the widgets provided in the material package. These widgets have an internal state which can be re-built if the input changes or if the widget’s state changes.
  • InheritedWidget: These are simple widgets that hold the data that are to be used by its children or descendants. These provide a simple mechanism to move data to a child much below in the widget tree. There is an element associated with it that changes the data when the element updates. The provider package is basically a wrapper around the InheritedWidgets.
  • ScopedModel: This library is taken from the Fuchsia codebase which provides a method to pass the data from Parents to their children. When the model changes the children are rebuilt. Our class can extend the Model class to create our own Models. ScopedModel Widget is wrapped around the widget whose data needs to be sent down the widget tree. ScopedModelDescendant Widget is used to listen to changes that happen to the Model and rebuild the child.
  • BLoC: Business Logic Component. This technique allows us to handle data as a Stream of events. It comes in between the UI and the data that handles the logic for the application. The main components of BLoC are Sink and Stream. The StreamController handles these components. The sink is used to add data/events and the Stream is used to listen to them.


Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads