feature: add bookmarking functionality
parent
688fa63da2
commit
618f5d135b
|
@ -43,10 +43,30 @@ class HomePage extends StatelessWidget {
|
||||||
child: const Icon(Icons.delete))
|
child: const Icon(Icons.delete))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: MovieManagerList(manager),
|
body: DefaultTabController(
|
||||||
floatingActionButton: FloatingActionButton(
|
length: 2,
|
||||||
child: const Icon(Icons.refresh),
|
child: Column(
|
||||||
onPressed: () => manager.loadUpcomingMovies(),
|
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")),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:intl/intl.dart';
|
||||||
class MovieData extends ChangeNotifier {
|
class MovieData extends ChangeNotifier {
|
||||||
String _title;
|
String _title;
|
||||||
DateWithPrecisionAndCountry _releaseDate;
|
DateWithPrecisionAndCountry _releaseDate;
|
||||||
|
bool _bookmarked = false;
|
||||||
|
|
||||||
bool _hasDetails = false;
|
bool _hasDetails = false;
|
||||||
List<DateWithPrecisionAndCountry>? _releaseDates;
|
List<DateWithPrecisionAndCountry>? _releaseDates;
|
||||||
|
@ -21,6 +22,10 @@ class MovieData extends ChangeNotifier {
|
||||||
return _releaseDate;
|
return _releaseDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get bookmarked {
|
||||||
|
return _bookmarked;
|
||||||
|
}
|
||||||
|
|
||||||
List<DateWithPrecisionAndCountry>? get releaseDates {
|
List<DateWithPrecisionAndCountry>? get releaseDates {
|
||||||
return _releaseDates;
|
return _releaseDates;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +46,9 @@ class MovieData extends ChangeNotifier {
|
||||||
return _hasDetails;
|
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(
|
setDetails(
|
||||||
title: movie.title,
|
title: movie.title,
|
||||||
releaseDate: movie.releaseDate,
|
releaseDate: movie.releaseDate,
|
||||||
|
@ -54,6 +61,7 @@ class MovieData extends ChangeNotifier {
|
||||||
void setDetails(
|
void setDetails(
|
||||||
{String? title,
|
{String? title,
|
||||||
DateWithPrecisionAndCountry? releaseDate,
|
DateWithPrecisionAndCountry? releaseDate,
|
||||||
|
bool? bookmarked,
|
||||||
List<DateWithPrecisionAndCountry>? releaseDates,
|
List<DateWithPrecisionAndCountry>? releaseDates,
|
||||||
List<String>? genres,
|
List<String>? genres,
|
||||||
List<TitleInLanguage>? titles,
|
List<TitleInLanguage>? titles,
|
||||||
|
@ -64,6 +72,9 @@ class MovieData extends ChangeNotifier {
|
||||||
if (releaseDate != null) {
|
if (releaseDate != null) {
|
||||||
_releaseDate = releaseDate;
|
_releaseDate = releaseDate;
|
||||||
}
|
}
|
||||||
|
if (bookmarked != null) {
|
||||||
|
_bookmarked = bookmarked;
|
||||||
|
}
|
||||||
if (releaseDates != null) {
|
if (releaseDates != null) {
|
||||||
_releaseDates = releaseDates;
|
_releaseDates = releaseDates;
|
||||||
}
|
}
|
||||||
|
@ -85,6 +96,10 @@ class MovieData extends ChangeNotifier {
|
||||||
return "$title (${_releaseDate.toString()}${_genres?.isNotEmpty ?? true ? "; ${_genres?.join(", ")}" : ""})";
|
return "$title (${_releaseDate.toString()}${_genres?.isNotEmpty ?? true ? "; ${_genres?.join(", ")}" : ""})";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool same(MovieData other) {
|
||||||
|
return title == other.title && releaseDate.date == other.releaseDate.date;
|
||||||
|
}
|
||||||
|
|
||||||
Map toJsonEncodable() {
|
Map toJsonEncodable() {
|
||||||
List? releaseDatesByCountry =
|
List? releaseDatesByCountry =
|
||||||
_releaseDates?.map((e) => e.toJsonEncodable()).toList();
|
_releaseDates?.map((e) => e.toJsonEncodable()).toList();
|
||||||
|
@ -92,6 +107,7 @@ class MovieData extends ChangeNotifier {
|
||||||
return {
|
return {
|
||||||
"title": title,
|
"title": title,
|
||||||
"releaseDate": _releaseDate.toJsonEncodable(),
|
"releaseDate": _releaseDate.toJsonEncodable(),
|
||||||
|
"bookmarked": _bookmarked,
|
||||||
"releaseDates": releaseDatesByCountry,
|
"releaseDates": releaseDatesByCountry,
|
||||||
"genres": genres,
|
"genres": genres,
|
||||||
"titles": titlesByCountry,
|
"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)
|
MovieData.fromJsonEncodable(Map json)
|
||||||
: _title = json["title"],
|
: _title = json["title"],
|
||||||
_releaseDate =
|
_releaseDate =
|
||||||
DateWithPrecisionAndCountry.fromJsonEncodable(json["releaseDate"]) {
|
DateWithPrecisionAndCountry.fromJsonEncodable(json["releaseDate"]) {
|
||||||
setDetails(
|
setDetails(
|
||||||
|
bookmarked: json["bookmarked"] as bool,
|
||||||
genres: (json["genres"] as List<dynamic>?)
|
genres: (json["genres"] as List<dynamic>?)
|
||||||
?.map((genre) => genre as String)
|
?.map((genre) => genre as String)
|
||||||
.toList(),
|
.toList(),
|
||||||
|
|
|
@ -54,7 +54,7 @@ class MovieManager extends ChangeNotifier {
|
||||||
added = true;
|
added = true;
|
||||||
actualMovies.add(movie);
|
actualMovies.add(movie);
|
||||||
} else {
|
} else {
|
||||||
existing.updateWithNew(movie);
|
existing.updateWithNewIgnoringUserControlled(movie);
|
||||||
actualMovies.add(existing);
|
actualMovies.add(existing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,16 @@ class MovieItem extends StatelessWidget {
|
||||||
animation: movie,
|
animation: movie,
|
||||||
builder: (context, widget) {
|
builder: (context, widget) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(movie.title),
|
title: Text(movie.title),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
"${dateRelativeToNow(movie.releaseDate.date)}, ${movie.releaseDate.toString()}, ${movie.genres?.join(", ") ?? ""}"));
|
"${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),
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,28 @@ import 'package:release_schedule/view/movie_item.dart';
|
||||||
|
|
||||||
class MovieList extends StatelessWidget {
|
class MovieList extends StatelessWidget {
|
||||||
final List<MovieData> movies;
|
final List<MovieData> movies;
|
||||||
const MovieList(this.movies, {super.key});
|
final bool Function(MovieData)? filter;
|
||||||
|
const MovieList(this.movies, {this.filter, super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(Object context) {
|
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(
|
return ListView.builder(
|
||||||
itemCount: movies.length,
|
itemCount: movies.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:release_schedule/model/movie.dart';
|
||||||
import 'package:release_schedule/model/movie_manager.dart';
|
import 'package:release_schedule/model/movie_manager.dart';
|
||||||
import 'package:release_schedule/view/movie_list.dart';
|
import 'package:release_schedule/view/movie_list.dart';
|
||||||
|
|
||||||
class MovieManagerList extends StatelessWidget {
|
class MovieManagerList extends StatelessWidget {
|
||||||
final MovieManager manager;
|
final MovieManager manager;
|
||||||
const MovieManagerList(this.manager, {super.key});
|
final bool Function(MovieData)? filter;
|
||||||
|
const MovieManagerList(this.manager, {this.filter, super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -14,7 +16,7 @@ class MovieManagerList extends StatelessWidget {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
manager.loading ? const LinearProgressIndicator() : Container(),
|
manager.loading ? const LinearProgressIndicator() : Container(),
|
||||||
Expanded(child: MovieList(manager.movies))
|
Expanded(child: MovieList(manager.movies, filter: filter))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -23,7 +23,7 @@ void main() {
|
||||||
], reviews: [
|
], reviews: [
|
||||||
Review('8.5', 'John Doe', DateTime(2023, 1, 1), 100)
|
Review('8.5', 'John Doe', DateTime(2023, 1, 1), 100)
|
||||||
]);
|
]);
|
||||||
movie1.updateWithNew(movie2);
|
movie1.updateWithNewIgnoringUserControlled(movie2);
|
||||||
expect(movie1.title, equals('Title 2'));
|
expect(movie1.title, equals('Title 2'));
|
||||||
expect(movie1.releaseDate.country, equals('UK'));
|
expect(movie1.releaseDate.country, equals('UK'));
|
||||||
expect(movie1.releaseDates!.length, equals(1));
|
expect(movie1.releaseDates!.length, equals(1));
|
||||||
|
|
Loading…
Reference in New Issue