我一直在慢慢地用Flatter构建一个应用程序,并努力在StatefulWidget/状态范例下工作.

我习惯于从其他类读取或更改某些UI组件的状态,但不太明白如何在Ffltter中做到这一点.例如,现在我正在构建一个包含CheckBoxListTiles的ListView.我想从另一个班级浏览并阅读列表磁贴中每个复选框的状态.

我觉得这在实践中应该非常简单,但我无法想出一种方法或示例来有效地外部化给定小部件的状态,以便它可以在其他地方使用.我知道,在这种明显的困难背后,很可能有一个有意的和哲学上的原因,我自己也能理解为什么它在一个框架(比如Flutter )中是至关重要的.然而,我感到受到限制,因为对于一个新手来说,如何在现成的模式下工作并不明显.

如何从ItemsList类中读取复选框的值?

项目分类:

class ListItem extends StatefulWidget {

  String _title = "";
  bool _isSelected = false;

  ListItem(this._title);

  @override
  State<StatefulWidget> createState() => new ListItemState(_title);
}

class ListItemState extends State<ListItem> {

  String _title = "";
  bool _isSelected = false;

  ListItemState(this._title);

  @override
  Widget build(BuildContext context) {

    return new CheckboxListTile(

      title: new Text(_title),
      value: _isSelected,
      onChanged: (bool value) {
        setState(() {
          _isSelected = value;
        });
      },

    );

  }

  String getTitle() {
    return _title;
  }

  bool isSelected() {
    return _isSelected;
  }

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

}

列表类:

class ItemsList extends StatefulWidget {

  @override
  State<StatefulWidget> createState() => new ItemsListState();

}

class ItemsListState extends State<ItemsList> {

  List<ListItem> _items = new List();

  @override
  Widget build(BuildContext context) {

    return new Scaffold(

      body: new ListView(
        children: _items.map((ListItem item) {
          return item;
        }).toList(),
      ),

      floatingActionButton: new FloatingActionButton(
        onPressed: addItem,
        child: new Icon(Icons.add),),

    );
  }

  void addItem() {
    setState(() {
      _items.add(new ListItem("Item"));
    });
  }

  List<String> getSelectedTitles() {
    List<String> selected = new List();

        ***This is where I would like to externalize the state***

    for(ListItem e in _items) {
      if(e.isSelected()) {
        selected.add(e.getTitle());
      }
    }

    return selected;
  }

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

}

推荐答案

Ffltter与你通常使用的应用程序不同.它使用类似于Reaction的原则,而这些原则本身也类似于函数式编程.

这意味着Flatters附带了一系列限制,迫使你以不同的方式构建应用程序:

  • 小部件是不可变的.你不需要更新它们.你重新创造它们.
  • 你不能访问你的state个子元素.您只能访问来自您父母的数据/州/州.

作为奖励,您几乎可以肯定,任何事件都不会对另一个不相关的类产生隐含的未知后果.


Okey, but what does this change to my example ?

这非常简单:这意味着,比方说,访问列表中某一列表项信息需要impossible美元.

相反,您应该做的是:将all个列表项的信息存储在列表中.然后把它传给每个人.

因此,首先我们需要更改ListItem,这样它就可以从它的父级获取所有内容.我们还可以使它成为无状态的,因为它不再存储任何信息.

有了这些,ListItem突然变得非常简单:

class ListItem extends StatelessWidget {
  final String title;
  final bool isSelected;
  final ValueChanged<bool> onChange;

  ListItem({ @required this.onChange, this.title = "", this.isSelected = false, });

  @override
  Widget build(BuildContext context) {
    return new CheckboxListTile(
      title: new Text(title),
      value: isSelected,
      onChanged: onChange
    );
  }
}

请注意,所有字段都是不可变的.传递给CheckboxListTileonChange事件也作为参数传递!

在这一点上,您可能会想"但是那个类现在不是毫无意义了吗?" 不怎么有意思.在我们的例子中,布局非常简单.但这是创建此类类的好做法,因为它会拆分布局.正在清除布局逻辑的父级代码.

不管怎样,我们继续吧.

现在,让我们修改父级,使其包含信息并向下传递:

class ItemsList extends StatefulWidget {
  State<StatefulWidget> createState() => new ItemsListState();
}

class ItemsListState extends State<ItemsList> {
  List<ListItem> items = new List<ListItem>();

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new ListView(
        children: items,
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: addItem,
        child: new Icon(Icons.add),
      ),
    );
  }

  void addItem() {
    final listItemIndex = items.length;
    final newList = new List<ListItem>.from(items)
      ..add(
        new ListItem(
          onChange: (checked) => onListItemChange(listItemIndex, checked),
          title: "Item n' $listItemIndex",
          isSelected: false,
        ),
      );

    this.setState(() {
      items = newList;
    });
  }

  onListItemChange(int listItemIndex, bool checked) {
    final newList = new List<ListItem>.from(items);
    final currentItem = items[listItemIndex];
    newList[listItemIndex] = new ListItem(
      onChange: currentItem.onChange,
      isSelected: checked,
      title: currentItem.title,
    );

    this.setState(() {
      items = newList;
    });
  }
}

请注意,我每次都会创建一个新的列表,而不是改变items个列表.

为什么会这样呢?这是因为,就像我之前解释的那样,字段应该是不可变的.ItemsList可能是有状态的,但这不是不遵循该原则的理由.因此,我们没有编辑列表,而是在旧列表的基础上创建了一个新列表.

事实上,如果没有new listListView人会想"嘿,你给我的列表和以前一样.所以我不需要刷新了,对吗?".


现在最酷的事情是,默认情况下,ItemsList拥有关于ListItem的所有信息.所以getSelectedTitles变得很容易做到:

Iterable<String> getSelectedTitles() {
  return items.where((item) => item.isSelected).map((item) => item.title);
}

Dart相关问答推荐

dart中如何从xpath获取属性值?

Dart StreamController().stream 等于 == 但不相同

为什么 Dart 程序在使用词法作用域时会根据声明变量的位置而表现不同?

Flutter android 生成apk

如何在 Dart 中设置文本框的值?

ProcessException:进程C:\..\myapp\android\gradlew.bat异常退出:

如何在 Flutter Web 中使用 Skia/CanvasKit?

从列表中删除项目后更新 UI

为什么 Dart 内置的 List 接口可以实例化?

替换 Flutter 中的片段等小部件

更改底片的灰色覆盖背景

如何在 Dart 中获取数字的长度?

如何在dart中获取 map 的键列表?

Flutter 中 ListView.builder 中的反向列表

什么时候在 Dart 中使用 num

如何将任意数据从 Dart polymer Web 组件传递到单击事件处理函数

安装 dart-sdk 后找不到 pub 命令

在 DART 中创建泛型类型的实例

如何在 Dart 中调用超级构造函数?

const 构造函数实际上是如何工作的?