feature: add bookmarking functionality

main
daniel-michel 2024-01-08 12:57:36 +01:00
parent 688fa63da2
commit 618f5d135b
7 changed files with 77 additions and 17 deletions

View File

@ -43,10 +43,30 @@ class HomePage extends StatelessWidget {
child: const Icon(Icons.delete))
],
),
body: MovieManagerList(manager),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.refresh),
onPressed: () => manager.loadUpcomingMovies(),
body: DefaultTabController(
length: 2,
child: Column(
children: [
Expanded(
child: TabBarView(
children: [
Scaffold(
body: MovieManagerList(manager),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.refresh),
onPressed: () => manager.loadUpcomingMovies(),
),
),
MovieManagerList(manager, filter: (movie) => movie.bookmarked)
],
),
),
const TabBar(tabs: [
Tab(icon: Icon(Icons.list), child: Text("Upcoming")),
Tab(icon: Icon(Icons.bookmark), child: Text("Bookmarked")),
]),
],
),
),
);
}

View File

@ -4,6 +4,7 @@ import 'package:intl/intl.dart';
class MovieData extends ChangeNotifier {
String _title;
DateWithPrecisionAndCountry _releaseDate;
bool _bookmarked = false;
bool _hasDetails = false;
List<DateWithPrecisionAndCountry>? _releaseDates;
@ -21,6 +22,10 @@ class MovieData extends ChangeNotifier {
return _releaseDate;
}
bool get bookmarked {
return _bookmarked;
}
List<DateWithPrecisionAndCountry>? get releaseDates {
return _releaseDates;
}
@ -41,7 +46,9 @@ class MovieData extends ChangeNotifier {
return _hasDetails;
}
void updateWithNew(MovieData movie) {
/// Updates the information with that of a new version of the movie
/// but ignores fields that are user controlled, like whether the movie was bookmarked.
void updateWithNewIgnoringUserControlled(MovieData movie) {
setDetails(
title: movie.title,
releaseDate: movie.releaseDate,
@ -54,6 +61,7 @@ class MovieData extends ChangeNotifier {
void setDetails(
{String? title,
DateWithPrecisionAndCountry? releaseDate,
bool? bookmarked,
List<DateWithPrecisionAndCountry>? releaseDates,
List<String>? genres,
List<TitleInLanguage>? titles,
@ -64,6 +72,9 @@ class MovieData extends ChangeNotifier {
if (releaseDate != null) {
_releaseDate = releaseDate;
}
if (bookmarked != null) {
_bookmarked = bookmarked;
}
if (releaseDates != null) {
_releaseDates = releaseDates;
}
@ -85,6 +96,10 @@ class MovieData extends ChangeNotifier {
return "$title (${_releaseDate.toString()}${_genres?.isNotEmpty ?? true ? "; ${_genres?.join(", ")}" : ""})";
}
bool same(MovieData other) {
return title == other.title && releaseDate.date == other.releaseDate.date;
}
Map toJsonEncodable() {
List? releaseDatesByCountry =
_releaseDates?.map((e) => e.toJsonEncodable()).toList();
@ -92,6 +107,7 @@ class MovieData extends ChangeNotifier {
return {
"title": title,
"releaseDate": _releaseDate.toJsonEncodable(),
"bookmarked": _bookmarked,
"releaseDates": releaseDatesByCountry,
"genres": genres,
"titles": titlesByCountry,
@ -99,15 +115,12 @@ class MovieData extends ChangeNotifier {
};
}
bool same(MovieData other) {
return title == other.title && releaseDate.date == other.releaseDate.date;
}
MovieData.fromJsonEncodable(Map json)
: _title = json["title"],
_releaseDate =
DateWithPrecisionAndCountry.fromJsonEncodable(json["releaseDate"]) {
setDetails(
bookmarked: json["bookmarked"] as bool,
genres: (json["genres"] as List<dynamic>?)
?.map((genre) => genre as String)
.toList(),

View File

@ -54,7 +54,7 @@ class MovieManager extends ChangeNotifier {
added = true;
actualMovies.add(movie);
} else {
existing.updateWithNew(movie);
existing.updateWithNewIgnoringUserControlled(movie);
actualMovies.add(existing);
}
}

View File

@ -12,9 +12,16 @@ class MovieItem extends StatelessWidget {
animation: movie,
builder: (context, widget) {
return ListTile(
title: Text(movie.title),
subtitle: Text(
"${dateRelativeToNow(movie.releaseDate.date)}, ${movie.releaseDate.toString()}, ${movie.genres?.join(", ") ?? ""}"));
title: Text(movie.title),
subtitle: Text(
"${dateRelativeToNow(movie.releaseDate.date)}, ${movie.releaseDate.toString()}, ${movie.genres?.join(", ") ?? ""}"),
trailing: TextButton(
child: Icon(movie.bookmarked
? Icons.bookmark_added
: Icons.bookmark_border),
onPressed: () => movie.setDetails(bookmarked: !movie.bookmarked),
),
);
},
);
}

View File

@ -4,10 +4,28 @@ import 'package:release_schedule/view/movie_item.dart';
class MovieList extends StatelessWidget {
final List<MovieData> movies;
const MovieList(this.movies, {super.key});
final bool Function(MovieData)? filter;
const MovieList(this.movies, {this.filter, super.key});
@override
Widget build(Object context) {
final localFilter = filter;
if (localFilter != null) {
List<int> indexMap = [];
int index = 0;
for (var movie in movies) {
if (localFilter(movie)) {
indexMap.add(index);
}
index++;
}
return ListView.builder(
itemCount: indexMap.length,
itemBuilder: (context, index) {
return MovieItem(movies[indexMap[index]]);
},
);
}
return ListView.builder(
itemCount: movies.length,
itemBuilder: (context, index) {

View File

@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:release_schedule/model/movie.dart';
import 'package:release_schedule/model/movie_manager.dart';
import 'package:release_schedule/view/movie_list.dart';
class MovieManagerList extends StatelessWidget {
final MovieManager manager;
const MovieManagerList(this.manager, {super.key});
final bool Function(MovieData)? filter;
const MovieManagerList(this.manager, {this.filter, super.key});
@override
Widget build(BuildContext context) {
@ -14,7 +16,7 @@ class MovieManagerList extends StatelessWidget {
return Column(
children: [
manager.loading ? const LinearProgressIndicator() : Container(),
Expanded(child: MovieList(manager.movies))
Expanded(child: MovieList(manager.movies, filter: filter))
],
);
},

View File

@ -23,7 +23,7 @@ void main() {
], reviews: [
Review('8.5', 'John Doe', DateTime(2023, 1, 1), 100)
]);
movie1.updateWithNew(movie2);
movie1.updateWithNewIgnoringUserControlled(movie2);
expect(movie1.title, equals('Title 2'));
expect(movie1.releaseDate.country, equals('UK'));
expect(movie1.releaseDates!.length, equals(1));