How can I use provider package inplace of setState to change button color?

Question

Asked by pannam on November 20, 2021 (source).

I have a widget tree that has a button that will change colors when pressed and perform some function but every time I press it the whole widget rebuilds (i.e map reloads), It's difficult for me as it is used with a map, I want to use a provider package in hope that it will cause just the button to change color and not reload the whole map. How can I use it? I would like to give it a try.


  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Stack(
      children: [
        CustomMap()
          Positioned(
          top: 60.0,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Padding(
                padding: EdgeInsets.symmetric(horizontal: 16.0),
                child: RaisedButton(
                  color: driverStatusColor,
                  onPressed: () {
                    if (isDriverAvailable != true) {
                      makeDriverOnlineNow();
                      getLocationLiveUpdates();

                      setState(() { //need to replace this with provider package
                        driverStatusColor = Colors.green;
                        driverStatusText = "You are Online Now";
                        isDriverAvailable = true;
                      });
                      displayToastMessage("You are Online now", context);
                    } else {
                      setState(() {
                        driverStatusColor = Colors.black;
                        driverStatusText = "Your are Offline";
                        isDriverAvailable = false;
                        displayToastMessage("You are Offline now", context);
                      });

                      driverOffline();
                    }
                  },
                  ]}

Answer

Question answered by h8moss (source).

I am sure there are many ways to use the provider package to manage state, but I will show you the one I know, sadly that means I don't know if it will be what you need:

First, you must declare a state class, in your case I believe this class should only hold the color of your button:

class DriverStatusState {
  Color driverStatusColor = Colors.black;
}

The reason why we need this class is that we need it to implement ChangeNotifier, and we need it to call notifyListeners every time it's value changes, so I will make some changes to the class

class DriverStatusState extends ChangeNotifier {
  Color _driverStatusColor = Colors.black;
  Color get driverStatusColor => _driverStatusColor;
  set driverStatusColor(Color color) {
    _driverStatusColor = color;
    notifyListeners();
  }
}

Now, at some point in your app, above where you will use the value, you need to declare a ChangeNorifierProvider, you can put this as low as you want, but it must be above your button

ChangeNotifierProvider<DriverStatusState>(
  create: (_) => DriverStatusState(),
  child: ...
)

We use the create method to create an instance of our state, and we continue our widget tree on the child parameter.

Now when it comes time to use the value, right above your button, you should add a consumer widget:

Consumer<DriverStatusState>(
  builder: (context, state, _) => ElevatedButton(
    child: Text('press me'),
    onPressed: () {
      if (!isDriverAvailable) {
        ...
        state.driverStatusColor = Colors.green;
      } else {
        ...
        state.driverStatusColor = Colors.black;
      }
    }
    style: ElevatedButton.styleFrom(primary: state.driverStatusColor),
  ),
)

The consumer's builder method provides you with a context, the value of your provided value, and a child widget if specified.

So when you assign state.driverStatusColor, the class runs notifyListeners, which notifies the provider, which notifies the consumer, which rebuilds.

FLUTTER FLUTTER-PROVIDER
SHARE: