tipo 'List <dynamic>' não é um subtipo do tipo 'List <Widget>'

94

Tenho um trecho de código que copiei do exemplo do Firestore:

Widget _buildBody(BuildContext context) {
    return new StreamBuilder(
      stream: _getEventStream(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) return new Text('Loading...');
        return new ListView(
          children: snapshot.data.documents.map((document) {
            return new ListTile(
              title: new Text(document['name']),
              subtitle: new Text("Class"),
            );
          }).toList(),
        );
      },
    );
  }

Mas eu recebo este erro

type 'List<dynamic>' is not a subtype of type 'List<Widget>'

O que está errado aqui?

Arash
fonte

Respostas:

211

O problema aqui é que a inferência de tipo falha de maneira inesperada. A solução é fornecer um argumento de tipo para o mapmétodo.

snapshot.data.documents.map<Widget>((document) {
  return new ListTile(
    title: new Text(document['name']),
    subtitle: new Text("Class"),
  );
}).toList()

A resposta mais complicada é que, embora o tipo childrenseja List<Widget>, essa informação não flui de volta para a mapinvocação. Isso pode ser porque mapé seguido por toListe porque não há como digitar anotar o retorno de um fechamento.

Jonah Williams
fonte
1
Pode estar relacionado ao modo forte ou vibração usando o Dart 2.
Rémi Rousselet
Esta mudança específica está provavelmente relacionada a dinâmico como inferior, também conhecido como "setas difusas" - anteriormente, era normal atribuir uma Lista <dinâmica> a uma Lista <X>. Isso encobriu muitas lacunas de inferência.
Jonah Williams,
1
TBH Não consegui reproduzir o bug dele. Uma vez que seu código é inferido como um List<ListTile>mesmo sem especificá-lo para map.
Rémi Rousselet,
2
Bem, isso resolveu o problema. Mas é um pouco estranho, sou novo no dardo, então não posso dizer se entendi.
Arash
Eu estava enfrentando o mesmo problema (mas meu cenário era diferente). Eu acho que isso está acontecendo por causa do tipo forte do Dart 2. Depois de alterar a declaração da variável para List <Widget>, ela começou a funcionar.
Manish Kumar
15

Você pode lançar lista dinâmica para lista com tipo específico:

List<'YourModel'>.from(_list.where((i) => i.flag == true));
Desenvolvedor C
fonte
7

Resolvi meu problema ao converter MapparaWidget

      children: snapshot.map<Widget>((data) => 
               _buildListItem(context, data)).toList(),
Abdurahman Popal
fonte
Obrigado por uma resposta tão simples!
user3079872
4

Acho que você usa _buildBody nas propriedades dos filhos de algum widget, então as crianças esperam um List Widget (array de Widget) e _buildBody retorna uma 'Lista dinâmica' .

De uma forma muito simples, você pode usar uma variável para retorná-lo:

// you can build your List of Widget's like you need
List<Widget> widgets = [
  Text('Line 1'),
  Text('Line 2'),
  Text('Line 3'),
];

// you can use it like this
Column(
  children: widgets
)

Exemplo ( flutter criar test1 ; cd test1 ; editar lib / main.dart ; flutter executar ):

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<Widget> widgets = [
    Text('Line 1'),
    Text('Line 2'),
    Text('Line 3'),
  ];

  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("List of Widgets Example")),
        body: Column(
          children: widgets
        )
      )
    );
  }

}

Outro exemplo usando um Widget (oneWidget) em uma Lista de Widgets (arrayOfWidgets). Mostro como amplia um widget (MyButton) para personalizar um widget e reduzir o tamanho do código:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<Widget> arrayOfWidgets = [
    Text('My Buttons'),
    MyButton('Button 1'),
    MyButton('Button 2'),
    MyButton('Button 3'),
  ];

  Widget oneWidget(List<Widget> _lw) { return Column(children: _lw); }

  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("Widget with a List of Widget's Example")),
        body: oneWidget(arrayOfWidgets)
      )
    );
  }

}

class MyButton extends StatelessWidget {
  final String text;

  MyButton(this.text);

  @override
  Widget build(BuildContext context) {
    return FlatButton(
      color: Colors.red,
      child: Text(text),
      onPressed: (){print("Pressed button '$text'.");},
    );
  }
}

Fiz um exemplo completo de uso de widgets dinâmicos para mostrar e ocultar widgets na tela, você pode vê-lo rodando online no dart fiddle também.

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List item = [
    {"title": "Button One", "color": 50},
    {"title": "Button Two", "color": 100},
    {"title": "Button Three", "color": 200},
    {"title": "No show", "color": 0, "hide": '1'},
  ];

  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("Dynamic Widget - List<Widget>"),backgroundColor: Colors.blue),
        body: Column(
          children: <Widget>[
            Center(child: buttonBar()),
            Text('Click the buttons to hide it'),
          ]
        )
      )
    );
  }

  Widget buttonBar() {
    return Column(
      children: item.where((e) => e['hide'] != '1').map<Widget>((document) {
        return new FlatButton(
          child: new Text(document['title']),
          color: Color.fromARGB(document['color'], 0, 100, 0),
          onPressed: () {
            setState(() {
              print("click on ${document['title']} lets hide it");
              final tile = item.firstWhere((e) => e['title'] == document['title']);
              tile['hide'] = '1';
            });
          },
        );
      }
    ).toList());
  }
}

Talvez ajude alguém. Se foi útil para você, me avise clicando na seta para cima, por favor. Obrigado.

https://dartpad.dev/b37b08cc25e0ccdba680090e9ef4b3c1

lynx_74
fonte
1
Isso parece não funcionar mais. Não é possível atribuir, por exemplo, Text('My Buttons')ao List<Widget>array. Obtendo The element type 'Text' can't be assigned to the list type 'Widget'. Qual seria uma solução alternativa para isso?
shaimo
Text é um widget e pode ser um elemento de List <Widget>. Verifique este pad https://dartpad.dev/6a908fe99f604474fd052731d59d059c e diga-me se funciona para você.
lynx_74
"O tipo de elemento 'List <Widget>' não pode ser atribuído ao tipo de lista 'Widget'." desde o seu primeiro exemplo. Não parece funcionar mais
Petro
1

Isso funciona para mimList<'YourModel'>.from(_list.where((i) => i.flag == true));

Nilotpal Kapri
fonte
0

Para converter cada item em um widget, use o construtor ListView.builder ().

Em geral, fornece uma função de construtor que verifica com qual tipo de item você está lidando e retorna o widget apropriado para esse tipo de item.

ListView.builder(
  // Let the ListView know how many items it needs to build.
  itemCount: items.length,
  // Provide a builder function. This is where the magic happens.
  // Convert each item into a widget based on the type of item it is.
  itemBuilder: (context, index) {
    final item = items[index];

    return ListTile(
      title: item.buildTitle(context),
      subtitle: item.buildSubtitle(context),
    );
  },
);
Paresh Mangukiya
fonte