flutter - 在 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);
}
}
你能帮助你的开发者伙伴吗?
解决方案
您对激活和停用类型所做的事情是危险的。
相反,您可以为流派创建一个对象,例如:
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 中运行此代码来检查它。
推荐阅读
- python - 无论如何强制 PyCharm 始终使用绝对导入?
- c# - Azure 移动应用 - 多语言 SQL 列
- php - 如何使用php获取主硬盘的序列号
- scala - 火花配置与输入结构的相互作用
- selenium - 为 Selenium 独立服务器设置最大实例和最大会话
- javascript - 一段时间内每天的状态值的 C3/d3 条形图
- php - 如何从数组中的条目循环到末尾?树枝/php
- android - 适用于设备的 Android Management API 更改策略
- c# - 单元测试 .Net CORE 2.0 WebAPI - 带有文本流主体(非模型)的模拟(MOQ)HTTP POST
- python - python http服务器线程通过cli