How do I keep a instantiated variable unchanged after using Provider.of

Issue

I have a page where I can choose a user theme color that is used as colors in my app. I have it set up so a modal bottom sheet pops up with the color options and then sets it using Provider so when the navigation is popped the new color can be seen on MyView.

Problem

When the user makes a change BUT hits the close button I essentially want to revert all changes made, so to try and tackle this I have a variable called loggedInUser which I initialise in my init State function and I keep out of the build method. So its set once and that’s it. The plan is that if the user hits the close button I use Provider to set the details back to the data in loggedInUser (which shoulldn’t have the updated color choices).

This does not happen and loggedInUser though not reinitialised has the new colors I chose.

Code

class MyView extends StatefulWidget {
  static const String id = "my_view";

  @override
  State<MyView> createState() => _MyViewState();
}

class _MyViewState extends State<MyView> {
  UserDto loggedInUser;

  @override
  void initState() {
    super.initState();
    loggedInUser = Provider.of<UserData>(context, listen: false).user;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: kThemeColor,
      body: Column(
        children: [
          SafeArea(
            child: CloseButton(
              onPressed: () {
                var test = loggedInUser;
                //when debugged, test has the new color, not the old one it was initialised to back in initState();
                //i want the old values to persist
                Navigator.pop(context);
              },
              color: Colors.white,
            ),
          ),
          Expanded(
            child: Container(
              height: double.infinity,
              width: double.infinity,
              decoration: kCurvedContainerBoxDecoration,
              child: Padding(
                padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    ElevatedButton(
                      onPressed: () {
                        showModalBottomSheet(
                          context: context,
                          isScrollControlled: true,
                          builder: (context) => SingleChildScrollView(
                            child: Container(
                              padding: EdgeInsets.only(
                                  bottom:
                                      MediaQuery.of(context).viewInsets.bottom),
                              child: AccountThemePickerView(),
                            ),
                          ),
                        );
                      },
                      style: ButtonStyle(
                        backgroundColor: MaterialStateProperty.all<Color>(
                          UserHelper.getColorFromString(
                              Provider.of<UserData>(context).user.themeColor),
                        ),
                        shape:
                            MaterialStateProperty.all<RoundedRectangleBorder>(
                          RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(20),
                          ),
                        ),
                      ),
                    )
                  ],
                ),
              ),
            ),
          )
        ],
      ),
    );
  }
}

class AccountThemePickerView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Color(0xff757575),
      child: Container(
        decoration: kModalBottomSheetBoxDecoration,
        padding: EdgeInsets.only(left: 15, bottom: 30, right: 15, top: 15),
        child: GridView.count(
          shrinkWrap: true,
          crossAxisCount: 3,
          crossAxisSpacing: 30,
          mainAxisSpacing: 30,
          children: [
            AccountThemePickerColor(
                colorName: "Coral Red", color: Color(0xffff6961)),
            AccountThemePickerColor(
                colorName: "Forest Green", color: Color(0xff129a7d)),
          ],
        ),
      ),
    );
  }
}

class AccountThemePickerColor extends StatelessWidget {
  final Color color;
  final String colorName;

  AccountThemePickerColor({this.colorName, this.color});

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () async {
        Provider.of<UserData>(context, listen: false)
            .updateUserThemeColor(colorName, color.toString());
        Navigator.pop(context);
      },
      style: ButtonStyle(
        shape: MaterialStateProperty.all<RoundedRectangleBorder>(
          RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(20),
          ),
        ),
        backgroundColor: MaterialStateProperty.all<Color>(color),
      ),
    );
  }
}

UserData class

class UserData extends ChangeNotifier{
  UserDto user;

  void setUser(UserDto userDto){
    user = userDto;
    notifyListeners();
  }

  void updateUserThemeColor(String themeColorName, String themeColor){
    //note I have a helper method which simply converts string to color, for your debug purposes you can just use an actual Color value
    user.themeColor = themeColor;
    user.themeColorName = themeColorName;
    notifyListeners();
  }
}

Solution

I believe it has something to do with copy constructors.

For example, this code:

class X{
  int y;
  int z;
  X(this.y, this.z);
}
void main() {
  X obj1 = X(2,3);
  X obj2 = obj1;
  X obj3 = obj2;
  
  obj1.y = 10;
  print(obj2.y);
  print(obj3.y);
}

outputs

10
10

because variables are references to objects. And when you assign an object to another object, it points to the same location in memory instead of copying its elements.

Provider.of<UserData>(context, listen: false).user; would return the same object each time it is called. So, you change its value. And hence, the loggedInUser also changes.

Try to create a new object and store data in it.

Answered By – Mohamed Akram

Answer Checked By – Jay B. (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *