Managing state in Flutter using Provider

495 views Asked by At

I'm trying to implement Provider state management on counter application to understand Provider's functionality better. I have added two buttons with respect to two different text widget. So, now whenever I click any of the two widget both the Text widgets get update and give same value. I want both the widgets independent to each other. I have used ScopedModel already and got the desire result but now I want to try with provider.

Image Link : https://i.stack.imgur.com/ma3tR.png

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("====Home Page Rebuilt====");
    return Scaffold(
      appBar: AppBar(
        title: Text("HomePage"),
      ),
      body: Container(
          child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        //crossAxisAlignment:CrossAxisAlignment.center,
        children: [
          Consumer<CounterModel>(
            builder: (context, value, child) {
              return CustomWidget(
                number: value.count.toString(),
              );
            },
          ),
          Consumer<CounterModel>(
            builder: (context, value, child) {
              return CustomWidget(
                number: value.count.toString(),
              );
            },
          ),
        ],
      )),
    );
  }
}

class CustomWidget extends StatelessWidget {
  final String number;

  const CustomWidget({Key key, this.number}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    print("====Number Page Rebuilt====");
    return ButtonBar(
      alignment: MainAxisAlignment.center,
      children: [
        Consumer<CounterModel>(
          builder: (context, value, child) {
            return Text(
              value.count.toString(),
              style: Theme.of(context).textTheme.headline3,
            );
          },
        ),
        FlatButton(
          color: Colors.blue,
          onPressed: () =>
              Provider.of<CounterModel>(context, listen: false).increment(),
          child: Text("Click"),
        ),
      ],
    );
  }
}


 
1

There are 1 answers

2
Mad World On

If you want them independent from each other, then you need to differentiate them somehow. I have a bit of a different style to implement the Provider and it hasn't failed me yet. Here is a complete example.

You should adapt your implementation to something like this:

Define your provider class that extends ChangeNotifier in a CounterProvider.dart file

import 'package:flutter/material.dart';

class CounterProvider extends ChangeNotifier {
  /// You can either set an initial value here or use a UserProvider object
  /// and call the setter to give it an initial value somewhere in your app, like in main.dart

  int _counter = 0; // This will set the initial value of the counter to 0

  int get counter => _counter;

  set counter(int newValue) {
    _counter = newValue;

    /// MAKE SURE YOU NOTIFY LISTENERS IN YOUR SETTER
    notifyListeners();
  }
}

Wrap your app with a Provider Widget like so

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
/// don't forget to import it here too
import 'package:app/CounterProvider.dart';


void main() {
  runApp(
    MaterialApp(
      initialRoute: '/root',
      routes: {
        '/root': (context) => MyApp(),
      },
      title: "Your App Title",
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        /// Makes data available to everything below it in the Widget tree
        /// Basically the entire app.
        ChangeNotifierProvider<CounterProvider>.value(value: CounterProvider()),
      ],
      child: MaterialApp(
        home: HomeScreen(),
      ),
    );
  }
}

Access and update data anywhere in the app

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
/// MAKE SURE TO IMPORT THE CounterProvider.dart file
import 'package:app/CounterProvider.dart';

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  CounterProvider counterProvider;

  @override
  Widget build(BuildContext context) {
    /// LISTEN TO THE CHANGES / UPDATES IN THE PROVIDER
    counterProvider = Provider.of<CounterProvider>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text("HomePage"),
      ),
      body: Container(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          //crossAxisAlignment:CrossAxisAlignment.center,
          children: [
            _showCounterButton(1),
            _showCounterButton(2),
          ],
        ),
      ),
    );
  }

  Widget _showCounterButton(int i) {
    return ButtonBar(
      alignment: MainAxisAlignment.center,
      children: [
        Text(
          i == 1
              ? counterProvider.counter1.toString()
              : counterProvider.counter2.toString(),
          style: Theme.of(context).textTheme.headline3,
        ),
        FlatButton(
          color: Colors.blue,
          onPressed: () {
            /// UPDATE DATA IN THE PROVIDER. BECAUSE YOU're USING THE SETTER HERE,
            /// THE LISTENERS WILL BE NOTIFIED AND UPDATE ACCORDINGLY
            /// you can do this in any other file anywhere in the Widget tree, as long as
            /// it it beneath the main.dart file where you defined the MultiProvider
            i == 1
                ? counterProvider.counter1 += 1
                : counterProvider.counter2 += 1;
            setState(() {});
          },
          child: Text("Click"),
        ),
      ],
    );
  }
}

If you want, you can change the implementation a bit. If you have multiple counters, for multiple widgets, then just create more variables in the CounterProvider.dart file with separate setters and getters for each counter. Then, to display/update them properly, just use a switch case inside the _showCounterButton() method and inside the onPressed: (){ switch case here, before setState((){}); }.

Hope this helps and gives you a better understanding of how Provider works.