Typ 'List <dynamic>' ist kein Subtyp vom Typ 'List <Widget>'

85

Ich habe einen Codeausschnitt, den ich aus dem Firestore-Beispiel kopiert habe:

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

Aber ich bekomme diesen Fehler

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

Was geht hier schief?

Arash
quelle

Antworten:

186

Das Problem hierbei ist, dass die Typinferenz auf unerwartete Weise fehlschlägt. Die Lösung besteht darin, der mapMethode ein Typargument bereitzustellen .

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

Die kompliziertere Antwort ist , dass diese Informationen , obwohl die Art von childrenist List<Widget>, nicht zum mapAufruf zurückfließen . Dies kann daran mapliegen, dass gefolgt wird toListund dass es keine Möglichkeit gibt, die Rückgabe eines Abschlusses mit Anmerkungen zu versehen.

Jonah Williams
quelle
1
Kann mit starkem Modus oder Flattern mit Dart 2 zusammenhängen.
Rémi Rousselet
Diese spezifische Änderung hängt wahrscheinlich mit der Dynamik als unterer "Fuzzy-Pfeile" zusammen - zuvor war es in Ordnung, einer Liste <X> eine Liste <dynamisch> zuzuweisen. Das hat viele Inferenzlücken geschlossen.
Jonah Williams
1
TBH Ich habe es nicht geschafft, seinen Fehler zu reproduzieren. Da sein Code als List<ListTile>gerade abgeleitet wird, ohne ihn anzugeben map.
Rémi Rousselet
2
Nun, das hat das Problem gelöst. Aber es ist ein bisschen seltsam, ich bin neu im Dart, also kann ich nicht wirklich sagen, dass ich es verstanden habe.
Arash
Ich hatte das gleiche Problem (aber mein Szenario war anders). Ich denke, das passiert wegen des starken Dart 2-Typs. Nachdem ich die Variablendeklaration in List <Widget> geändert hatte, funktionierte sie.
Manish Kumar
13

Sie können dynamische Liste in Liste mit einem bestimmten Typ umwandeln:

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

Ich habe mein Problem durch Konvertieren Mapin gelöstWidget

      children: snapshot.map<Widget>((data) => 
               _buildListItem(context, data)).toList(),
Abdurahman Popal
quelle
Vielen Dank für eine so einfache Antwort!
user3079872
3

Ich denke , dass Sie _buildBody in den verwenden Kinder Eigenschaften einiger widget, so Kinder eine erwarten Liste Widget (array of Widget) und _buildBody eine zurückgibt ‚Liste dynamisch‘ .

Auf sehr einfache Weise können Sie eine Variable verwenden, um sie zurückzugeben:

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

Beispiel ( flatter create test1 ; cd test1 ; edit lib / main.dart ; flatter 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
        )
      )
    );
  }

}

Ein weiteres Beispiel unter Verwendung eines Widget (oneWidget) innerhalb einer Liste der Widgets (arrayOfWidgets). Ich zeige, wie weit ein Widget (MyButton) reicht, um ein Widget zu personalisieren und die Größe des Codes zu reduzieren:

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'.");},
    );
  }
}

Ich habe ein vollständiges Beispiel erstellt , in dem ich dynamische Widgets verwende , um Widgets auf dem Bildschirm anzuzeigen und auszublenden. Sie können sehen, dass sie auch online auf Dart Fiddle ausgeführt werden.

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

Vielleicht hilft es jemandem. Wenn es für Sie nützlich war, lassen Sie mich bitte wissen, indem Sie auf den Aufwärtspfeil klicken. Vielen Dank.

https://dartpad.dev/b37b08cc25e0ccdba680090e9ef4b3c1

lynx_74
quelle
Das scheint nicht mehr zu funktionieren. Kann zB nicht Text('My Buttons')in das List<Widget>Array zuweisen . Erhalten The element type 'Text' can't be assigned to the list type 'Widget'. Was wäre eine Problemumgehung dafür?
Shaimo
Text ist ein Widget und kann ein Element von List <Widget> sein. Überprüfen Sie dieses Pad https://dartpad.dev/6a908fe99f604474fd052731d59d059c und sagen Sie mir, ob es bei Ihnen funktioniert.
lynx_74
1

Das funktioniert bei mirList<'YourModel'>.from(_list.where((i) => i.flag == true));

Nilotpal Kapri
quelle
0

Verwenden Sie den Konstruktor ListView.builder (), um jedes Element in ein Widget zu konvertieren.

Stellen Sie im Allgemeinen eine Builder-Funktion bereit, die prüft, um welchen Elementtyp es sich handelt, und das entsprechende Widget für diesen Elementtyp zurückgibt.

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
quelle