il tipo "List <dynamic>" non è un sottotipo di tipo "List <Widget>"


94

Ho uno snippet di codice che ho copiato dall'esempio di 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(),
        );
      },
    );
  }

Ma ottengo questo errore

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

Cosa va storto qui?

Risposte:


211

Il problema qui è che l'inferenza del tipo fallisce in modo imprevisto. La soluzione è fornire un argomento di tipo al mapmetodo.

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

La risposta più complicata è che mentre il tipo di childrenè List<Widget>, quell'informazione non fluisce indietro verso l' mapinvocazione. Ciò potrebbe essere dovuto al fatto che mapè seguito da toListe perché non è possibile digitare annotare il ritorno di una chiusura.


1
Può essere correlato a modalità forte o flutter utilizzando Dart 2.
Rémi Rousselet

Questa modifica specifica è probabilmente correlata alla dinamica come fondo noto anche come "frecce sfocate" - in precedenza era consentito assegnare una Lista <dinamica> a una Lista <X>. Questo ha coperto molte lacune di inferenza.
Jonah Williams il

1
TBH Non sono riuscito a riprodurre il suo bug. Poiché il suo codice viene dedotto come un List<ListTile>anche senza specificarlo map.
Rémi Rousselet

2
Bene, questo ha risolto il problema. Ma è un po 'strano, sono nuovo a sfrecciare, quindi non posso davvero dire di aver capito.
Arash

Stavo affrontando lo stesso problema (ma il mio scenario era diverso). Penso che questo stia accadendo a causa del tipo forte di Dart 2. Una volta modificata la dichiarazione della variabile in List <Widget>, ha iniziato a funzionare.
Manish Kumar

15

Puoi trasmettere da elenco dinamico a elenco con un tipo specifico:

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

7

Ho risolto il mio problema convertendomi MapinWidget

      children: snapshot.map<Widget>((data) => 
               _buildListItem(context, data)).toList(),

Grazie per una risposta così semplice!
user3079872

4

Penso che tu usi _buildBody nelle proprietà dei figli di alcuni widget, quindi i bambini si aspettano un List Widget (array di Widget) e _buildBody restituisce un 'List dynamic' .

In un modo molto semplice, puoi usare una variabile per restituirlo:

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

Esempio ( flutter create test1 ; cd test1 ; edit 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
        )
      )
    );
  }

}

Un altro esempio di utilizzo di un widget (oneWidget) all'interno di un elenco di widget (arrayOfWidgets). Mostro come si estende un widget (MyButton) per personalizzare un widget e ridurre la dimensione del codice:

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

Ho fatto un esempio completo che uso widget dinamici per mostrare e nascondere i widget sullo schermo, puoi vederlo in esecuzione online anche su dart fiddle .

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

Forse aiuta qualcuno. Se ti è stato utile, fammi sapere facendo clic sulla freccia su, per favore. Grazie.

https://dartpad.dev/b37b08cc25e0ccdba680090e9ef4b3c1


1
Questo sembra non funzionare più. Non può assegnare ad esempio Text('My Buttons')nella List<Widget>matrice. Ottenere The element type 'Text' can't be assigned to the list type 'Widget'. Quale sarebbe una soluzione alternativa per questo?
shaimo

Il testo è un widget e può essere un elemento di List <Widget>. Controlla questo pad https://dartpad.dev/6a908fe99f604474fd052731d59d059c e dimmi se funziona per te.
lynx_74

"Il tipo di elemento" List <Widget> "non può essere assegnato al tipo di elenco" Widget "." dal tuo primo esempio. Non sembra funzionare più
Petro

1

Questo funziona per meList<'YourModel'>.from(_list.where((i) => i.flag == true));


0

Per convertire ogni elemento in un widget, utilizza il costruttore ListView.builder ().

In generale, fornisci una funzione di creazione che controlla il tipo di oggetto con cui hai a che fare e restituisce il widget appropriato per quel tipo di oggetto.

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),
    );
  },
);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.