当前位置: 首页 > 知识库问答 >
问题:

如何迭代集合(例如Map)并为集合中的每个子类型呈现单独的小部件?

程淮晨
2023-03-14

我有一个对象列表,这些对象都是动物的子类型。我将它们全部转换为动物列表中的supertype,以便将它们的引用保存在一个位置。到现在为止,一直都还不错。

现在,当我呈现每个动物对象的状态信息时,我希望根据每个动物对象的子类型使用不同的小部件。当我从超类型转换为子类型时,即使我指定字段是动态的,也会出现错误。

Type 'type' is not a subtype of type '<subtype>'

看来,dart编译器使用的类型信息,从动画Tiles映射强制所有元素的类型是动物,即使我传递到的类型的构造函数采取了一个动态命名参数,我希望这将是松散的。

// ANIMAL LIST
class AnimalList extends StatelessWidget {

final Map<String, Animal> animals;

    @override
    Widget build(BuildContext context) {

        List<Widget> animalTiles = [];
        animals.forEach(
          (key, setting) => animalTiles.add(
            AnimalTile(animal: animal),
          ),
        );

        return Column(
            children: animalTiles,
        );
    }
}

// ANIMAL TILE

class AnimalTile extends StatelessWidget {
  AnimalTile({
    required animal,
  }) {
    _buildDescription(animal);
  }
  late dynamic animal;
  late Widget description;

    Widget _buildDescription(dynamic setting) {
    if (animal.type == Animal.CAT) {
      Cat cat = animal;
      return CatWidget(cat: cat);
    } else if (animal.type == Animal.DOG) {
      Dog dog = animal;
      return DogWidget(dog: dog);
    } else if (animal.type == Animal.MOUSE) {
      Mouse mouse = animal;
      return MouseWidget(mouse: mouse);
    } else {
      return Container(child: Text(':('));
    }
  }

    @override
    Widget build(BuildContext context) {
        return(
            ...
        );
    }
}

Dart类型系统要求我在初始化列表中的构造函数体之前初始化最终字段,或者直接分配给参数列表中的实例字段。如果我在构造函数的主体中调用一个辅助方法,那就不被认为是最终的,所以我不想将其用于不可变的无状态小部件。

这一定是一种常见的模式。。我试图将所有子类型保持在同一个列表中,然后使用子类型显示不同的小部件。为什么这种方法失败了?在Dart中实现这种模式的有效方法是什么?

共有1个答案

宣煜
2023-03-14

Flatter Cookbook有一个创建不同子类型项目列表的示例。然而,我发现自己在重复我希望基类可以使用的数据字段。因为,如果我对抽象动物类使用extends代替实现(即接口上的继承),我会得到一个异常,表明Dart正在查看基类的方法,而不是子类中被重写的方法。这不是保持代码干燥的最佳解决方案,但它确实有效。

不幸的是,这意味着我没有一个解决方案,使我能够将我的许多动物使用的数据字段保留在一个类中,但是我发布这个来演示一个有效的模式。

更新:

您还可以跨子类型重用数据字段,同时使用extends(继承)和implements(接口)确保调用的方法是子类型重写的方法。

这是我的解决方案,请胜过我:

// Base Class
abstract class AnimalBase {
  AnimalBase({
    required this.height,
    required this.weight,
    required this.name,
    required this.genus,
    required this.species,  
  });

  final int height;
  final double weight;
  final String name;
  final String genus;
  final String species;

  static const CAT = 0;
  static const DOG = 1;
  static const MOUSE = 2;

  static AnimalFactory(Map<String, dynamic> json, int type) {
    // IN MY CASE I PASS THE TYPE TO A FACTORY

    switch (type) {
      case CAT:
        return Cat.fromJson(json);
      case DOG:
        return Dog.fromJson(json);
      case MOUSE:
        return Mouse.fromJson(json);
      default:
        return null;
    }
  }
}

// Interface Class
abstract class Animal; {
  Widget buildTile();

  Widget description();
}


// Example SubType
class Dog extends AnimalBase with Animal {
  Dog({
    required height,
    required weight,
    required name,
    required genus,
    required species,  
  }) : super(height: height, weight: weight, name: name, genus: genus, species: species);

  @override
  Dog.fromJson(Map<String, dynamic> json)
    throw new UnimplementedError();
  }

  Map<String, dynamic> toJson() {
    throw new UnimplementedError();
  }

  @override
  Widget buildTile() {
    return DogTile(dog: this);
  }

  @override
  Widget description() {
    return DogDescription(dog: this);
  }
}

class AnimalList extends StatelessWidget {
  AnimalList({
    Key? key,
    required this.animals,
  }) : super(key: key);

  final Map<String, Animal> animals;

  @override
  Widget build(BuildContext context) {
    List<Widget> animalTiles = [];
    animals.forEach(
      (key, animal) => animalTiles.add(
        AnimalTile(animal: animal),
      ),
    );

    return CustomScrollView(
      shrinkWrap: true,
      slivers: [
        SliverToBoxAdapter(
          child: Column(
            children: ...animalTiles.map(animal => animal.buildTile()).toList(),
          ),
        ),
        SliverFillRemaining(
          hasScrollBody: false,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              Divider(
                color: Colors.black54,
                height: 18,
                thickness: 4,
                indent: 20,
                endIndent: 20,
              ),
            ],
          ),
        ),
      ],
    );
  }
}

我希望我有一个只需要一个关键字(extends或implements)的解决方案,但这是我最好的解决方案:)

 类似资料:
  • 我正在使用Flutter和Cloud FiRecovery构建一个Instagram克隆,我正在尝试像这样构建数据库: 收藏(“时间线”) 实际上看起来像这样: 阅读项目标题,例如,很容易。我可以渲染一个Listview.builder并使用: 但是我如何遍历每个项目的评论子集合?我尝试了嵌套的Listview和this,但我无法访问elementAt(index)的子集合: 或者有没有更好的方法

  • 是否可以在java中并行地迭代一个集合。我正在中寻找类似C#的东西-命名空间

  • 问题内容: 我正在研究一个难题,其中涉及分析所有大小的k个子集,并找出哪个子集是最佳的。我写了一个解决方案,当子集的数量很少时可以使用,但是对于较大的问题,它用尽了内存。现在,我正在尝试将用python编写的迭代函数转换为java,以便我可以在创建每个子集时对其进行分析,并仅获取代表其优化程度的值,而不是整个集的值,以便不会耗尽记忆。这是我到目前为止的内容,即使很小的问题也似乎还没有解决: 有人可

  • 出身背景我有数字1到20(黑色背景上的白色数字),可以出现在屏幕上,我希望识别这些数字。由于它们不能简单地复制粘贴,我将比较屏幕上数字的白色像素位置与所有20个数字的白色像素位置列表。然而,每个数字可以有大量的像素,并且可能不需要比较所有这些像素来识别该数字。因此,我希望尽可能少地进行比较。 算法问题:我有多个集合,其中的元素在每个集合中是唯一的,但在所有集合中可能不是唯一的。如何找到每个集合的最

  • 问题内容: 在Java中为集合的集合设计一个迭代器。迭代器应隐藏嵌套,使您可以迭代属于所有集合的所有元素,就像使用单个集合一样 问题答案: 这是一个可能的实现。请注意,我没有执行remove():

  • 有没有一种方法我可以用某种方式迭代光标我可以暂停它,然后再继续?MongodbCursor保存最后访问的项目?我只知道foreach迭代,但是否有一些像这样的迭代? 如果有类似的东西存在,我可以保存查询的最后一个项目。提前致谢