使用 Provider.of 后如何保持实例化变量不变
我有一个页面,可以在其中选择在我的应用程序中用作颜色的用户主题颜色。我已对其进行设置,以便弹出带有颜色选项的模态底部表,然后使用 Provider 设置它,以便在弹出导航时可以在 MyView 上看到新颜色。
问题
当用户进行更改但点击关闭按钮时,我本质上想恢复所做的所有更改,因此为了尝试解决此问题,我有一个名为 LoggedInUser 的变量,我在 init State 函数中初始化该变量,并将其排除在构建方法之外。所以它设置一次就这样了。计划是,如果用户点击关闭按钮,我将使用 Provider 将详细信息设置回 LoggedInUser 中的数据(不应有更新的颜色选择)。
这种情况不会发生,loggedInUser 虽然没有重新初始化,但具有我选择的新颜色。
代码
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 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();
}
}
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();
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我相信这与复制构造函数有关。
例如,此代码:
输出
,因为变量是对对象的引用。当您将一个对象分配给另一个对象时,它指向内存中的同一位置,而不是复制其元素。
Provider.of(context, Listen: false).user;
每次调用时都会返回相同的对象。所以,你改变它的值。因此,loggedInUser 也会发生变化。尝试
创建
一个新对象并在其中存储数据。I believe it has something to do with copy constructors.
For example, this code:
outputs
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.