#flutter #dart #mapping
Вопрос:
У меня есть список, который в настоящее время имеет следующий формат:
final List<Map<String, List<Object>>> _userWords = [
{
DateTime.now().toString(): [
English(
meaning: 'Dog',
date: DateTime.now(),
type: 'Noun',
id: DateTime.now().toString(),
),
German(
meaning: 'Hund',
article: 'der',
plural: 'Hunde',
date: DateTime.now(),
id: DateTime.now().toString(),
type: 'Noun',
),
],
},
{
DateTime.now().toString(): [
English(
meaning: 'Cat',
date: DateTime.now(),
type: 'Noun',
id: DateTime.now().toString(),
),
German(
meaning: 'Katze',
article: 'die',
plural: 'Katzen',
date: DateTime.now(),
id: DateTime.now().toString(),
type: 'Noun',
),
],
} //map
];
В настоящее время я пытаюсь отобразить для каждого слова (внешняя карта) значение на каждом языке на отдельной карточке.
WordList(this.words);
@override
Widget build(BuildContext context) {
return Container(
height: 300,
child: SingleChildScrollView(
child: Column(
//iterating through the outer list, creating one card for each word
//wrds represents each outer map (key=DateTime)
children: words.map((wrds) {
return Card(
child: Container(
margin: EdgeInsets.symmetric(vertical: 0, horizontal: 0),
child: Column(
//iterating through each language thats associated with the key
//creating a card for each meaning in a different language
children: (wrds[wrds.keys.first]).map((lng) {
return Card(
child: Text(
lng.meaning,
),
);
}).toList(),
),
),
);
}).toList(),
),
),
);
}
Now this throws the error
type 'List<dynamic>' is not a subtype of type 'List<'Widget>'
I tried to remove the attribute from lng.meaning to just lng and then I get the error
type 'English' is not a subtype of type 'String'
So it appears that the indexing works correctly.
I tried to be specific with the inner toList() function, changing it to ‘.toList()’
which gives the error
NoSuchMethodError: Class'MappedListIterable<Object,dynamic>'
has no instance method 'toList' with matching arguments.
If I just use the outer map and hard code the indexing for each language thats associated to the key I also get the intended result, but I want to create this dynamically. Can anyone explain me what is going on here, and what I am doing wrong?
The way I understand the error is that the result of my mapping is not a list of Widgets. But the way I understand my code is, that I should get a list of Containers. One for each language in the map. Which is what I get with the outer mapping. (changing container to another card does not change the error)
Edit: I had different parts of the app in different files. I restructured it to provide a minimum runnable example to the following
void main() {
runApp(MyApp());
}
class English {
final _properties = [
{'articles': 'the'},
//SubjectVerbObject
{'structure': 'svo'},
];
static String plural = 's';
final String meaning;
final DateTime date;
final String id;
final String type;
English(
{required this.meaning,
required this.date,
required this.id,
required this.type});
}
class German {
final String meaning;
final String article;
final String plural;
final DateTime date;
final String id;
final String type;
German({
required this.meaning,
required this.article,
required this.plural,
required this.date,
required this.id,
required this.type,
});
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'VocTrainer',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
final List<Map<String, List<Object>>> _userWords = [
{
DateTime.now().toString(): [
English(
meaning: 'Dog',
date: DateTime.now(),
type: 'Noun',
id: DateTime.now().toString(),
),
German(
meaning: 'Hund',
article: 'der',
plural: 'Hunde',
date: DateTime.now(),
id: DateTime.now().toString(),
type: 'Noun',
),
],
},
{
DateTime.now().toString(): [
English(
meaning: 'Cat',
date: DateTime.now(),
type: 'Noun',
id: DateTime.now().toString(),
),
German(
meaning: 'Katze',
article: 'die',
plural: 'Katzen',
date: DateTime.now(),
id: DateTime.now().toString(),
type: 'Noun',
),
],
} //map
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('VocTrainer')),
body: SingleChildScrollView(
child: Column(children: <Widget>[
Container(
height: 300,
child: SingleChildScrollView(
child: Column(
children: _userWords.map((wrds) {
return Card(
child: Container(
margin: EdgeInsets.symmetric(vertical: 0, horizontal: 0),
decoration: BoxDecoration(
border: Border.all(
color: Colors.amber.shade800,
width: 5,
),
),
padding: EdgeInsets.all(10),
child: Column(
children: (wrds[wrds.keys.first]!).map((lng) {
Row(children: [
Text(
lng.runtimeType.toString() ': ',
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 17,
color: Colors.amber.shade900,
),
),
Text(
lng.meaning,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
color: Colors.amber.shade900,
),
),
]);
}).toList(),
),
),
);
}).toList(),
),
),
),
]),
),
);
}
}
Интересно, что я уже получаю ошибку здесь, в коде, в котором говорится, что Null списка не может быть присвоен виджету списка.
Однако, если я изменю строку на жестко закодированную
child: Column(children: [
Row(children: [
Text(
wrds[wrds.keys.first]![0].runtimeType.toString()
': ',
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 17,
color: Colors.amber.shade900,
),
),
Text(
wrds[wrds.keys.first]![0].toString(),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
color: Colors.amber.shade900,
),
),
]),
Row(children: [
Text(
wrds[wrds.keys.first]![1].runtimeType.toString()
': ',
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 17,
color: Colors.amber.shade900,
),
),
Text(
wrds[wrds.keys.first]![1].toString(),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
color: Colors.amber.shade900,
),
),
]),
Это работает. Интересно, что здесь я не могу вызвать wrds[wrds.keys.first]![1].значение из-за ошибки
wrds[wrds.keys.first]![1].toString()
Хотя это работало без каких-либо проблем, когда мой код был разделен на несколько файлов.
Ответ №1:
Я попытался воспроизвести проблему, построив минимальный пример. Поскольку вы не показали , как вы объявили свои классы English
и German
, я сделал предположение и добавил абстрактный класс Word
, который они расширяют.
Кажется, все работает, и я не смог воспроизвести вашу проблему. Однако мне пришлось изменить тип карты на List<Map<String, List<Word>>>
вместо List<Map<String, List<Object>>>
. С учетом этих изменений код отображает две карты (собаки и кошки), содержащие оба перевода в качестве внутренних карт.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
abstract class Word {
final String meaning;
final DateTime date;
String? article;
String? plural;
final String type;
final String id;
Word(
this.meaning,
this.date,
this.type,
this.id,
this.article,
this.plural,
);
}
class German extends Word {
German({
required String meaning,
required DateTime date,
required String type,
required String id,
String? article,
String? plural,
}) : super(meaning, date, type, id, article, plural);
}
class English extends Word {
English({
required String meaning,
required DateTime date,
required String type,
required String id,
String? article,
String? plural,
}) : super(meaning, date, type, id, article, plural);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final List<Map<String, List<Word>>> _userWords = [
{
DateTime.now().toString(): [
English(
meaning: 'Dog',
date: DateTime.now(),
type: 'Noun',
id: DateTime.now().toString(),
),
German(
meaning: 'Hund',
article: 'der',
plural: 'Hunde',
date: DateTime.now(),
id: DateTime.now().toString(),
type: 'Noun',
),
],
},
{
DateTime.now().toString(): [
English(
meaning: 'Cat',
date: DateTime.now(),
type: 'Noun',
id: DateTime.now().toString(),
),
German(
meaning: 'Katze',
article: 'die',
plural: 'Katzen',
date: DateTime.now(),
id: DateTime.now().toString(),
type: 'Noun',
),
],
} //map
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
height: 300,
child: SingleChildScrollView(
child: Column(
//iterating through the outer list, creating one card for each word
//wrds represents each outer map (key=DateTime)
children: _userWords.map((wrds) {
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
width: MediaQuery.of(context).size.width / 2,
child: Container(
margin:
EdgeInsets.symmetric(vertical: 0, horizontal: 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: (wrds[wrds.keys.first]!).map((lng) {
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
lng.meaning,
),
),
);
}).toList(),
),
),
),
),
);
}).toList(),
),
),
),
),
);
}
}
Обновить
После того, как был приведен пример выполнения, я смог обнаружить ошибку. Сопоставление слов создает экземпляры виджета строки, но не return
создает их.
Это должно сработать для отображения:
children: (wrds[wrds.keys.first]!).map((lng) {
**return** Row(children: [
Text(
lng.runtimeType.toString() ': ',
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 17,
color: Colors.amber.shade900,
),
),
Text(
lng.meaning,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
color: Colors.amber.shade900,
),
),
]);
}).toList(),
Тем не менее, я все же предлагаю ввести общий интерфейс для слов, чтобы избежать проверок типа и приведения типов.
Комментарии:
1. Это действительно странно. Ваш код работает безупречно и для меня. Я не определил классы таким же образом, но попробовал использовать ваши определения классов, и я получаю ту же ошибку, так что это не может быть так. Я действительно озадачен этим. Когда я просто копирую ваш код, он работает, когда я пытаюсь настроить свой код так, чтобы он выглядел так же, как ваш (у меня он разделен на несколько файлов). Я просто получаю ошибку: тип аргумента «Список<Null>» не может быть присвоен типу параметра » Список<Null><Виджет>». непосредственно в моем коде, хотя я скопировал ваш список.
2. Если вы не возражаете, не стесняйтесь поделиться проектом со мной, и я посмотрю.
3. Это действительно было бы очень полезно. Я уже много раз перепробовал, так что все в полном беспорядке. Завтра я приведу его в более разумное состояние. Я также отредактировал свой оригинальный пост с выполняемым примером (ну, на этот раз он уже показывает ошибку в коде), так что, надеюсь, это облегчит устранение неполадок. Большое спасибо за ваши усилия.
4. Я понял это на вашем примере. Я обновил свой ответ.