【问题标题】:Managing mutable state in StatefulWidget / State在 StatefulWidget / State 中管理可变状态
【发布时间】:2022-01-06 17:28:34
【问题描述】:

我对如何使用 StatefulWidget 管理状态有点困惑。以下是我遇到的问题的简化示例:

我有一个StatefulWidget,用于显示事物列表。它由从 API 检索到的列表构成。该小部件还允许用户将新项目添加到列表中,点击 API 即可。 API 在添加项目后依次返回整个列表。

我遇到的挑战是小部件是用api.List 对象构造的。 Dart 要求 StatefulWdigets 是不可变的(因此所有字段都是 final 的),因此当用户向其中添加项目时,该列表不能被 API 返回的新列表替换。这部分对我来说很有意义:可变状态应该存在于状态对象中,而不是小部件中。所以这表明状态对象应该跟踪列表。这会导致两个不同的问题:

  1. 状态对象是在StatefulWidgetcreateState 方法中创建的。如果我们想将列表传递给状态对象的构造函数,则列表也需要作为最终字段存储在StatefulWidget 上。现在小部件和它的状态对象都会引用列表。当用户将项目添加到列表中时,状态对象将其列表替换为 API 返回的新副本,这两者将不同步。

  2. 即使你这样做了,linter 也会抱怨在 createState 方法中包含逻辑:https://dart-lang.github.io/linter/lints/no_logic_in_create_state.html。他们似乎强烈建议状态对象应始终具有 0 参数构造函数,并且您永远不应该将任何东西从 StatefulWidget 传递给 State 对象。他们建议始终通过State 对象的widget 访问此类数据。但这会让您回到 StatefulWidget 不可变的问题:如果列表是 StatefulWidget 对象上的最终字段,您将无法替换该列表。

有问题的设计 1

在这里,我们避免了 createState 方法中存在逻辑的 linting 问题。但是你不能用newList 替换widget.list,因为它是最终的。

class ListWidget extends StatefulWidget {
  const ListWidget({Key? key, required this.list }) : super(key: key);
  
  final api.List list;
  
  @override
  State<StatefulWidget> createState() => _ListWidgetState();
}

class _ListWidgetState extends State<ListWidget> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      ...  // bunch of widgets
      ElevatedButton(
        ...
        onPressed: () async {
          final newList = await api.addNewListItem(...);
          // Can't do this because widget.list is final. Makes sense as it just seems wrong to store mutable state in the widget instead of the state.
          setState(() => widget.list = newList);
        },
      )
    );
  }
}

如果不是替换 widget.list 对象,而是“内部”更改其所有字段以匹配 newList,则可以使此设计起作用。这似乎很尴尬且容易出错。此外,您最终还是会(内部)变异widget.list,这感觉非常错误。看起来flutter的意图是可变状态真的不应该存在于StatefulWidget中。

有问题的设计 2

这可行,但 linter 抱怨将列表传递给 createState 中的 _ListWidgetState 构造函数。此外,当用户向列表中添加项目时,小部件的 list 对象和状态的 list 对象变得不同步。

class ListWidget extends StatefulWidget {
  const ListWidget({Key? key, required this.list }) : super(key: key);

  final api.List list;

  @override
  State<StatefulWidget> createState() => _ListWidgetState(list); // linter complains
}

class _ListWidgetState extends State<ListWidget> {
  _ListWidgetState(this.list);

  api.List list;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      ...  // bunch of widgets
      ElevatedButton(
        ...
        onPressed: () async {
          final newList = await api.addNewListItem(...);
          // This works, but now widget.list and list are out of sync.
          setState(() => list = newList);
        },
      )
    );
  }
}

Perils of Flutter state in StatefulWidget rather than State? 似乎正在解决类似的问题。那里的建议是创建小部件的新实例。不过,我不确定您如何将 ListWidget 完全替换为带有 newList 的新构造的 onPressed 回调。

【问题讨论】:

    标签: flutter flutter-state


    【解决方案1】:

    第二个 sn-p 更接近解决方案。从本质上讲,ListWidget 中的list_ListWidgetState 中的list 没有必要处于“同步”状态。 其实最好看的是ListWidget中的列表是初始数据,_ListWidgetState中的列表是更新后的数据,如果用户有任何改变的话。 当用户进行更改时,您可以依赖第二个list 中的数据来维护它。

    import 'package:flutter/material.dart';
    
    class ListWidget extends StatefulWidget {
      const ListWidget({Key? key, required this.list }) : super(key: key);
    
      final api.List list;
    
      @override
      State<StatefulWidget> createState() => _ListWidgetState();
    }
    
    class _ListWidgetState extends State<ListWidget> {
      _ListWidgetState();
    
      late api.List list;
    
      @override
      void initState() {
        list = widget.list;
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          ... 
          ElevatedButton(
            ...
            onPressed: () async {
              list = await api.addNewListItem(...);
              setState(() {});
            },
          )
        );
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-19
      • 1970-01-01
      • 1970-01-01
      • 2021-09-26
      • 1970-01-01
      • 1970-01-01
      • 2022-01-21
      • 1970-01-01
      相关资源
      最近更新 更多