Создание нескольких виджетов из одного списка

#list #flutter #dart #oop

Вопрос:

В настоящее время я учусь писать мобильные приложения в Flutter с помощью Dart, и недавно я хотел попробовать создать приложение, в котором вы можете сохранить фильмы, которые вы хотите посмотреть, и получить некоторую базовую информацию о них. Теперь это только для меня, чтобы потренироваться в понимании основных концепций flutter, и я хотел спросить, как сделать несколько виджетов из одного списка. Я покажу вам свой код, а затем подробнее расскажу о нем.

 import 'package:flutter/material.dart';

class MovieList {
  String name;
  String url;
  String description;
  String actors;
  int id;

  MovieList(this.name, this.url, this.description, this.actors, this.id);

  var movies = [
    {
      "21",
      "assets/images/21_movie.jpeg",
      "randomDescriptionfor21",
      "idkwhichactors.1",
      1
    },
    {
      "Dirty Dancing",
      "assets/images/dirty_dancing_movie.jpeg",
      "randomDescriptionforDirtyDancing",
      "idkwhichactors.2",
      2
    },
    {
      "Endless love",
      "assets/images/endless_love_movie.jpeg",
      "randomDescriptionforEndlesslove",
      "idkwhichactors.3",
      3
    },
    {
      "Gut gegen Nordwind",
      "assets/images/gut_gegen_nordwind_movie.jpeg",
      "randomDescriptionforNordwind",
      "idkwhichactors.4",
      4
    },
    {
      "Illuminati",
      "assets/images/illuminati_movie.jpeg",
      "randomDescriptionforIlluminati",
      "idkwhichactors.5",
      5
    },
    {
      "Bridget Jones",
      "assets/images/jones_movie.jpeg",
      "randomDescriptionforBridgetJones",
      "idkwhichactors.6",
      6
    },
    {
      "Kevin allein Zuhaus",
      "assets/images/kevin_allein_zuhaus_movie.jpeg",
      "randomDescriptionforKevin",
      "idkwhichactors.7",
      7
    },
    {
      "Little Woman",
      "assets/images/little_woman_movie.jpeg",
      "randomDescriptionforLittleWoman",
      "idkwhichactors.8",
      8
    },
    {
      "Liebe braucht keine Ferien",
      "assets/images/love_movie.jpeg",
      "randomDescriptionforLiebeundFerien",
      "idkwhichactors.9",
      9
    },
    {
      "Marvel-Filme",
      "assets/images/marvel_movie.jpeg",
      "randomDescriptionforMarvel",
      "idkwhichactors.10",
      10
    },
    {
      "Oceans 12",
      "assets/images/oceans_12_movie.jpeg",
      "randomDescriptionforOceans12",
      "idkwhichactors.11",
      11
    },
    {
      "Pirates of the Carribean",
      "assets/images/pirates_carribean_movie.jpeg",
      "randomDescriptionforPiraten",
      "idkwhichactors.12",
      12
    },
    {
      "Romeo und Julia",
      "assets/images/romeo_and_julia_movie.jpeg",
      "randomDescriptionforRomeoxJulia",
      "idkwhichactors.13",
      13
    },
    {
      "A star is born",
      "assets/images/star_movie.jpeg",
      "randomDescriptionforStar",
      "idkwhichactors.14",
      14
    },
    {
      "Die Entdeckung der Unendlichkeit",
      "assets/images/stephen_hawking_movie.jpeg",
      "randomDescriptionforStephen",
      "idkwhichactors.15",
      15
    },
    {
      "Frühstück bei Tiffany",
      "assets/images/tiffany_movie.jpeg",
      "randomDescriptionforTIffany",
      "idkwhichactors.16",
      16
    }
  ];
}
 

Итак, как вы видите, я определяю класс MovieList, в котором я определяю определенные переменные (имя, URL, описание, действующие лица, идентификатор). Теперь под инициализацией тех, кто определяет список с несколькими фильмами, у которых есть все эти атрибуты.

  1. Вопрос: Как можно использовать список для создания нескольких объектов списка фильмов и как я мог бы реализовать цикл (или что-то еще, как бы это было сделано) для отображения нескольких виджетов, соответствующих фильмам, которые я определил в списке «фильмы»?
  2. Вопрос: Является ли это вообще правильным подходом и сработает ли он вообще? Как я уже сказал, я в значительной степени начинаю с дротика и флаттера, так что мне действительно нужна ваша помощь.

Заранее спасибо 🙂

Так что большое спасибо, вы уже очень мне помогли. Теперь я хотел сделать виджет своей карты, и именно так я изменил код: 1:

 ``
import 'package:flutter/material.dart';
import './header.dart';
import './image_widget.dart';
import './movies_to_watch.dart';

var movies = [
  {
    'title': '21',
    'bannerPath': 'assets/images/21_movie.jpeg',
    'description': 'randomDescriptionforPirates',
    'actors': [
      'some actor',
    ]
  },
  {
    'title': 'Dirty Dancing',
    'bannerPath': 'assets/images/dirty_dancing_movie.jpeg',
    'description': 'randomDescriptionforPirates',
    'actors': [
      'some actor',
    ]
  },
  {
    'title': 'Endless Love',
    'bannerPath': 'assets/images/endless_love_movie.jpeg',
    'description': 'randomDescriptionforPirates',
    'actors': [
      'some actor',
    ]
  },
  {
    'title': 'Gut gegen Nordwind',
    'bannerPath': 'assets/images/gut_gegen_nordwind_movie.jpeg',
    'description': 'randomDescriptionforPirates',
    'actors': [
      'some actor',
    ]
  },
  {
    'title': 'Illuminati',
    'bannerPath': 'assets/images/illuminati_movie.jpeg',
    'description': 'randomDescriptionforPirates',
    'actors': [
      'some actor',
    ]
  },
  {
    'title': 'Bridget Jones',
    'bannerPath': 'assets/images/jones_movie.jpeg',
    'description': 'randomDescriptionforPirates',
    'actors': [
      'some actor',
    ]
  },
  {
    'title': 'Kevin allein zuhaus',
    'bannerPath': 'assets/images/kevin_allein_zuhaus_movie.jpeg',
    'description': 'randomDescriptionforPirates',
    'actors': [
      'some actor',
    ],
  },
];

class Body extends StatelessWidget {
  const Body({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: EdgeInsets.only(bottom: 20.0),
            child: HeaderWidget(),
          ),
          Padding(
            padding: EdgeInsets.only(right: 20.0, left: 20.0),
            child: FilmstoWatch(),
          ),
          Padding(
            padding: EdgeInsets.only(right: 20.0, left: 20.0),
            child: SingleChildScrollView(
              scrollDirection: Axis.horizontal,
              child: Row(
                children: [
                  for (var movie in movies)
                    MovieWidget(model: MovieModel.fromJson(movie)),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}
``
 

и 2:

 ``
import 'package:flutter/material.dart';

class MyClass {
  // here I am generating fake movies but you should use the data that you already have
  List<Map<String, dynamic>> movies = List.generate(
      15,
      (index) => {
            'title': 'fake title $index',
            'actors': ['fake actor $index'],
            'bannerPath': 'some/fake/path/$index',
            'description': 'some fake description $index',
          });

  List<MovieModel> get models =>
      movies.map((movie) => MovieModel.fromJson(movie)).toList();

  List<Widget> get widgets =>
      models.map((model) => MovieWidget(model: model)).toList();
}

class MovieModel {
  MovieModel({
    required this.title,
    required this.actors,
    required this.bannerPath,
    required this.description,
  });

  factory MovieModel.fromJson(Map<String, dynamic> json) {
    return MovieModel(
      title: json['title'],
      actors: json['actors'],
      bannerPath: json['bannerPath'],
      description: json['description'],
    );
  }

  String title;
  String bannerPath;
  String description;
  List<String> actors;
}

class MovieWidget extends StatelessWidget {
  const MovieWidget(
      {required this.model}); // if you decide to not make a model class, you would pass each value individually

  final MovieModel model;

  @override
  Widget build(BuildContext context) {
    // obviously this can be any widget you want
    return SingleChildScrollView(
      child: Card(
        shape:
            RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
        elevation: 10.0,
        margin: const EdgeInsets.all(10.0),
        child: Column(
          children: [
            Text(model.title),
            Container(
              height: 200,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(10.0),
                image: DecorationImage(
                  image: AssetImage(model.bannerPath),
                ),
              ),
            ),
            Text(model.description),
            if (model.actors.isNotEmpty) Text(model.actors.first),
          ],
        ),
      ),
    );
  }
}
``
 

Если я запущу это, я получу эту ошибку:

https://drive.google.com/file/d/1zbmsOq3lQIPUIZ92mvrg4sCcT21W8cc4/view?usp=sharing

когда я заменяю model.bannerPath фактической строкой, которая должна быть вставлена туда, это работает. Еще раз большое спасибо за вашу помощь!

Ответ №1:

Я буду честен с вами, я не большой поклонник наборов и понятия не имею, почему вы решили их использовать, поэтому я изменил ваши наборы на карты, а также удалил целое число, которое у них было, потому что я не могу сказать, почему это было бы полезно. Как я уже сказал, именно так я кодирую, и я надеюсь, что вы сможете решить, хотите ли вы сохранить наборы или нет

 var movies = [
  ...
  {
    'title': 'Pirates of the Carribean',
    'banner': 'assets/images/pirates_carribean_movie.jpeg',
    'description': 'randomDescriptionforPiraten',
    'actors': ['some actor'],
  },
  ...
];
 

Я бы также рекомендовал вам создать MovieModel класс, который может хранить эти значения безопасным для типов способом

 class MovieModel {
  String title;
  String bannerPath;
  String description;
  List<String> actors;
}
 

Затем вам следует создать какой-нибудь виджет, который может отображать нужную вам информацию

 class MovieWidget extends StatelessWidget {
  MovieWidget({required this.model}); // if you decide to not make a model class, you would pass each value individually

  final MovieModel model;

  @override
  Widget build(BuildContext context) {
    // obviously this can be any widget you want
    return Column(
      children: [
        Text(model.title),
        Text(model.banner),
        Text(model.description),
        Text(model.actors.first),
      ],
    );
  }
}
 

Наконец, чтобы превратить ваш список фильмов в список виджетов, сначала вам нужно превратить его в список моделей

 final List<MovieModel> models = movies.map((movie) => MovieModel(title: movie['title'], banner: movie['banner'], description: movie['description'], actors: movie['actors']));
 

Затем вы можете сделать то же самое с виджетами:

 List<Widget> widgets = models.map((model) => MovieWidget(model: model));
 

Редактировать:

Что касается вашего комментария об использовании переменной «Старые фильмы».

Если вы хотите добавить все эти виджеты в столбец, например:

 return Column(
  children: [
    for (var movie in movies)
      MovieWidget(model: MovieModel.fromJson(movie)),
  ],
);
 

Очевидно, что в приведенном выше примере, если вы хотите добавить заполнение, вам просто нужно окружить MovieWidget его заполнением.

Комментарии:

1. Привет, большое спасибо за помощь. Я использовал наборы больше, чем карты, что привело к тому, что я не был по-настоящему уверен в использовании карт, но я вижу преимущества, которые они приносят. В принципе, у меня все еще есть вопрос. Возможно, я добавляю две переменные списка (последние две строки, которые вы мне велели добавить) или делаю что-то еще не так, но до сих пор это всегда подчеркивалось красным с этим сообщением: The instance member 'movies' can't be accessed in an initializer. Try replacing the reference to the instance member with a different expression Еще раз большое спасибо за помощь, и я надеюсь, что у вас будет хороший день 🙂

2. Вы пытаетесь создать переменные класса виджетов и моделей? Если да, то ошибка исходит оттуда, возможное решение состоит в том, чтобы сделать их функциями получения , от type variableName = value; до type get variableName => value , так List<MovieModel> get models => movies.map((movie) => MovieModel(...)); и List<Widget> get widgets => models.map((model) => MovieWidget(model: model)); , но я не уверен, что это решит проблему.

3. Поэтому я получаю несколько стрелок. Просто чтобы убедиться, что я не получу эти стрелы, потому что я сделал что-то не так… Не могли бы вы показать, как будет выглядеть код в файле? Я не уверен, что положил все туда, куда следовало. Это было бы большим подспорьем. Заранее спасибо:)

4. В итоге я внес некоторые небольшие изменения в код, чтобы он работал, но вот в чем все дело: pastebin.com/MzMq6fKR

5. Большое спасибо, что мне передать виджету, если я хочу использовать его в другом месте?