首页 > 解决方案 > 在 Flutter 中过滤掉电影

问题描述

我正在 Flutter 中开发一个电影应用程序。TMDB API 链接https://developers.themoviedb.org/3/getting-started/introduction。我想知道如何按流派过滤电影列表。这是我的应用程序的屏幕截图。

在此处输入图像描述

流派 UI 是一个水平列表,我们可以选择任何流派。这是它的屏幕截图。

在此处输入图像描述

我想根据流派选择过滤我的电影列表 UI,是否相应地选择了所需的流派显示列表,如果未选择则显示原始列表。如果可能,还提供一些关于多类型过滤的见解。

这是我的流派 UI 代码。

import 'package:flutter/material.dart';
import 'package:flutter_movies_app/utility/Size_Config.dart';

import '../../../constants.dart';

class Genres extends StatefulWidget {
  const Genres({Key? key}) : super(key: key);

  @override
  _GenresState createState() => _GenresState();
}

class _GenresState extends State<Genres> {
  var genres = [
    [28, "Action"],
    [12, "Adventure"],
    [16, "Animation"],
    [35, "Comedy"],
    [80, "Crime"],
    [99, "Documentary"],
    [18, "Drama"],
    [10751, "Family"],
    [14, "Fantasy"],
    [36, "History"],
    [27, "Horror"],
    [10402, "Music"],
    [9648, "Mystery"],
    [10749, "Romance"],
    [878, "Science Fiction"],
    [10770, "TV Movie"],
    [53, "Thriller"],
    [10752, "War"],
    [37, "Western"]
  ];
  List<bool> temp = [];

  @override
  void initState() {
    super.initState();
    for (int i = 0; i < genres.length; i++) temp.add(false);
  }

  @override
  Widget build(BuildContext context) {
    SizeConfig().init(context);

    return Container(
      margin: const EdgeInsets.symmetric(vertical: kDefaultPadding / 2),
      height: SizeConfig.screenHeight * 0.05,
      //color: Colors.red,
      child: ListView.builder(
          scrollDirection: Axis.horizontal,
          itemCount: genres.length,
          itemBuilder: (ctx, index) => InkWell(
                onTap: () {
                  if (temp[index]) {
                    setState(() {
                      temp[index] = false;
                    });
                  } else {
                    setState(() {
                      temp[index] = true;
                    });
                  }
                },
                child: Container(
                  alignment: Alignment.center,
                  margin: const EdgeInsets.only(left: kDefaultPadding / 2),
                  padding: const EdgeInsets.symmetric(
                      horizontal: kDefaultPadding,
                      vertical: kDefaultPadding / 4),
                  decoration: BoxDecoration(
                      color: temp[index] ? Colors.amber : Colors.transparent,
                      border: Border.all(
                          color: temp[index]
                              ? Colors.transparent
                              : Colors.black26),
                      borderRadius: BorderRadius.circular(20.0)),
                  child: Text(
                    genres[index].last.toString(),
                    style: TextStyle(
                        color: temp[index]
                            ? Colors.white
                            : kTextColor.withOpacity(0.8),
                        fontSize: SizeConfig.safeBlockHorizontal * 4),
                  ),
                ),
              )),
    );
  }
}

我正在使用提供程序包进行状态管理和 API(TMDB 电影 API)集成。这是我的提供者 dart 文件中的一些代码。

List<nowPlaying.Result> _nowPlayingMovies = [];
List<nowPlaying.Result> get getNowPlaying => [..._nowPlayingMovies];

Future<void> getNowPlayingMovies({required String type}) async {
    try {
      nowPlaying.NowPlayingModel? nowPlayingModel =
          await GetMoviesTvShows.nowPlayingMovies(type: type, page: 1);
      if (nowPlayingModel != null) {
        List<nowPlaying.Result> temp = [];
        nowPlayingModel.results.forEach((result) {
          if (result.title.isNotEmpty && result.overview.isNotEmpty) {
            temp.add(result);
          }
        });
        _nowPlayingMovies = temp;
        notifyListeners();
      }
    } catch (e, s) {
      print(e);
      print(s);
    }
  }

这是我提出的过滤代码。但我不想更改原始列表,因为在未选择流派时我需要它。

Future<void> filterByGenre({required int genreId}) async {
    try {
      List<nowPlaying.Result> nowPlayingTemp = [];
      _nowPlayingMovies.forEach((element) {
        if (element.genreIds.contains(genreId)) nowPlayingTemp.add(element);
      });
      List<moviesModel.Result> popularTemp = [];
      _popularLS.forEach((element) {
        if (element.genreIds.contains(genreId)) popularTemp.add(element);
      });
      List<moviesModel.Result> topRatedTemp = [];
      _topRatedLS.forEach((element) {
        if (element.genreIds.contains(genreId)) topRatedTemp.add(element);
      });
      List<moviesModel.Result> upcomingTemp = [];
      _upComingLS.forEach((element) {
        if (element.genreIds.contains(genreId)) upcomingTemp.add(element);
      });
      _nowPlayingMovies = nowPlayingTemp;
      _popularLS = popularTemp;
      _topRatedLS = topRatedTemp;
      _upComingLS = upcomingTemp;
      notifyListeners();
    } catch (e, s) {
      print(e);
      print(s);
    }
  }

你能帮助你的开发者伙伴吗?

标签: flutterdartprovider

解决方案


您对激活和停用类型所做的事情是危险的。

相反,您可以为流派创建一个对象,例如:

class Genre {
  int id;
  String title;
  bool active;

  Genre(this.id, this.title, {this.active = false});

  void toggleActive() {
    active = !active;
  }
}

有了这个,您将有一个更简单的界面来激活和停用流派。

现在,让我们看看如何过滤电影。

考虑一些将所有电影放在一起的方法。

class Movie {
  List<int> genreIds;
  String name;

  Movie(this.name,this.genreIds);
}

class MovieGenerator {
  static Future<List<Movie>> getMovies() async{
    return [
      Movie("Dairy kid", [1,2]),
      Movie("Dairy Man", [1]),
    ];
  }
}

过滤可以通过以下方式完成:

         FutureBuilder<List<Movie>>(
          future: MovieGenerator.getMovies(),
          builder: (c, snapshot) {
            if(snapshot.hasData) {
              var movies = snapshot.data;
              var selectedGenres = genres.where((e) => e.active).map((e) => e.id);
              var filteredMovies = movies.where((a) {
                bool shouldAdd = false;                
                for(int i = 0; i<a.genreIds.length; i++) {
                  if(selectedGenres.contains(a.genreIds[i])) {
                    shouldAdd = true;
                    break;
                  }
                }
                
                return shouldAdd;
              }).toList();
              return Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: List.generate(
                 filteredMovies.length,
                  (i) => Text(filteredMovies[i].name) 
                ),
              );
            }else {
              return Container();
            }
          }
        ),

因此,整个代码可能类似于:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Home(),
    );
  }
}

class Genre {
  int id;
  String title;
  bool active;

  Genre(this.id, this.title, {this.active = false});

  void toggleActive() {
    active = !active;
  }
}

class Movie {
  List<int> genreIds;
  String name;
  
  Movie(this.name,this.genreIds);
}

class MovieGenerator {
  static Future<List<Movie>> getMovies() async{
    return [
      Movie("Dairy kid", [1,2]),
      Movie("Dairy Man", [1]),
    ];
  }
}

class Home extends StatefulWidget {
  @override
  State<Home> createState() => HomeState();
}

class HomeState extends State<Home> {
  List<Genre> genres;

  @override
  void initState() {
    super.initState();
    genres = [
      Genre(1, "Action", active: true),
      Genre(2, "Comedy"),
    ];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView(children: [
        SingleChildScrollView(
          scrollDirection: Axis.horizontal,
          child: Row(
            children: List.generate(genres.length, (i) {
              var cur = genres[i];
              return Chip(
                  title: cur.title,
                  active: cur.active,
                  onTap: () {
                    setState(() {
                      genres[i].toggleActive();
                    });
                  });
            }),
          ),
        ),
        FutureBuilder<List<Movie>>(
          future: MovieGenerator.getMovies(),
          builder: (c, snapshot) {
            if(snapshot.hasData) {
              var movies = snapshot.data;
              var selectedGenres = genres.where((e) => e.active).map((e) => e.id);
              var filteredMovies = movies.where((a) {
                bool shouldAdd = false;                
                for(int i = 0; i<a.genreIds.length; i++) {
                  if(selectedGenres.contains(a.genreIds[i])) {
                    shouldAdd = true;
                    break;
                  }
                }
                
                return shouldAdd;
              }).toList();
              return Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: List.generate(
                 filteredMovies.length,
                  (i) => Text(filteredMovies[i].name) 
                ),
              );
            }else {
              return Container();
            }
          }
        ),
      ]),
    );
  }
}

class Chip extends StatelessWidget {
  final String title;
  final bool active;
  final Function onTap;

  const Chip({Key key, this.title, this.active, this.onTap}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
          padding: const EdgeInsets.all(10.0),
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(999.0),
            color: active ? Colors.red : Colors.grey,
          ),
          child: Text(title,
              style: TextStyle(
                color: active ? Colors.white : Colors.black,
              ))),
    );
  }
}

您可以在 dartpad.dev 中运行此代码来检查它。


推荐阅读