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)