el tipo 'List <dynamic>' no es un subtipo del tipo 'List <Widget>'

85

Tengo un fragmento de código que copié del ejemplo de 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(),
        );
      },
    );
  }

Pero me sale este error

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

¿Qué sale mal aquí?

Arash
fuente

Respuestas:

184

El problema aquí es que la inferencia de tipos falla de una manera inesperada. La solución es proporcionar un argumento de tipo al mapmétodo.

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

La respuesta más complicada es que, si bien el tipo de childrenes List<Widget>, esa información no fluye hacia la mapinvocación. Esto podría deberse a que mapva seguido de toListy porque no hay forma de escribir anotar la devolución de un cierre.

Jonah Williams
fuente
1
Puede estar relacionado con el modo fuerte o el aleteo usando Dart 2.
Rémi Rousselet
Este cambio específico probablemente esté relacionado con la parte inferior dinámica, también conocida como "flechas difusas"; anteriormente, estaba bien asignar una Lista <dinámica> a una Lista <X>. Eso cubrió muchas lagunas de inferencia.
Jonah Williams
1
TBH no he logrado reproducir su error. Dado que su código se infiere como List<ListTile>par sin especificarlo map.
Rémi Rousselet
2
Bueno, eso resolvió el problema. Pero es un poco extraño, soy nuevo en dardos, así que no puedo decir que lo entendí.
Arash
Estaba enfrentando el mismo problema (pero mi escenario era diferente). Creo que esto está sucediendo debido al tipo fuerte de Dart 2. Una vez que cambié la declaración de variable a List <Widget>, comenzó a funcionar.
Manish Kumar
13

Puede convertir una lista dinámica en una lista con un tipo específico:

List<'YourModel'>.from(_list.where((i) => i.flag == true));
Desarrollador C
fuente
5

He resuelto mi problema convirtiéndome MapenWidget

      children: snapshot.map<Widget>((data) => 
               _buildListItem(context, data)).toList(),
Abdurahman Popal
fuente
¡Gracias por una respuesta tan sencilla!
user3079872
3

Creo que utilice _buildBody en los niños propiedades de algunos widgets, por lo que los niños esperan un widget Lista (variedad de widgets) y _buildBody devuelve una 'lista dinámica' .

De una manera muy simple, puedes usar una variable para devolverla:

// 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
)

Ejemplo ( flutter crea test1 ; cd test1 ; edita lib / main.dart ; flutter run ):

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

}

Otro ejemplo usando un widget (oneWidget) dentro de una lista de widgets (arrayOfWidgets). Muestro cómo se extiende un widget (MyButton) para personalizar un widget y reducir el tamaño del 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'.");},
    );
  }
}

Hice un ejemplo completo de que uso widgets dinámicos para mostrar y ocultar widgets en la pantalla, también puede verlo ejecutándose en línea en el violín de dardos .

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

Quizás ayude a alguien. Si fue útil para usted, hágamelo saber haciendo clic en la flecha hacia arriba, por favor. Gracias.

https://dartpad.dev/b37b08cc25e0ccdba680090e9ef4b3c1

lynx_74
fuente
Esto ya no parece funcionar. No se puede asignar, por ejemplo, Text('My Buttons')a la List<Widget>matriz. Conseguir The element type 'Text' can't be assigned to the list type 'Widget'. ¿Cuál sería una solución para esto?
shaimo
El texto es un widget y puede ser un elemento de List <Widget>. Consulta este pad https://dartpad.dev/6a908fe99f604474fd052731d59d059c y dime si te funciona.
lynx_74
1

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

Nilotpal Kapri
fuente
0

Para convertir cada elemento en un widget, use el constructor ListView.builder ().

En general, proporcione una función de generador que verifique el tipo de elemento con el que está tratando y devuelva el widget apropiado para ese tipo de elemento.

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
fuente