使用 Provider.of 后如何保持实例化变量不变

发布于 2025-01-15 04:01:30 字数 5143 浏览 4 评论 0原文

我有一个页面,可以在其中选择在我的应用程序中用作颜色的用户主题颜色。我已对其进行设置,以便弹出带有颜色选项的模态底部表,然后使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

凉风有信 2025-01-22 04:01:30

我相信这与复制构造函数有关。

例如,此代码:

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);
}

输出

10
10

,因为变量是对对象的引用。当您将一个对象分配给另一个对象时,它指向内存中的同一位置,而不是复制其元素。

Provider.of(context, Listen: false).user; 每次调用时都会返回相同的对象。所以,你改变它的值。因此,loggedInUser 也会发生变化。

尝试创建一个新对象并在其中存储数据。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文