Skip to content

Commit

Permalink
Pagination improvements (#16)
Browse files Browse the repository at this point in the history
* Modify paginated providers to return TMDBMoviesResponse

* Updated UI

* Improved interceptor

* Cleanup

* Update Xcode project
  • Loading branch information
bizz84 authored Apr 11, 2024
1 parent 63b13b4 commit 7c4514d
Show file tree
Hide file tree
Showing 16 changed files with 285 additions and 262 deletions.
2 changes: 1 addition & 1 deletion ios/Flutter/AppFrameworkInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
<string>12.0</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,4 @@ post_install do |installer|
config.build_settings[deployment_target_key] = minimum_deployment_target
end
end
end
end
24 changes: 8 additions & 16 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,38 +1,30 @@
PODS:
- Flutter (1.0.0)
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- sqflite (0.0.3):
- Flutter
- FMDB (>= 2.7.5)
- FlutterMacOS

DEPENDENCIES:
- Flutter (from `Flutter`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)

SPEC REPOS:
trunk:
- FMDB
- sqflite (from `.symlinks/plugins/sqflite/darwin`)

EXTERNAL SOURCES:
Flutter:
:path: Flutter
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
sqflite:
:path: ".symlinks/plugins/sqflite/ios"
:path: ".symlinks/plugins/sqflite/darwin"

SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec

PODFILE CHECKSUM: a8bf662d8622f5e36dabb676533790e6f6c4777b
PODFILE CHECKSUM: a35cc51c57d2268949e0897e6d913905cee03701

COCOAPODS: 1.14.2
COCOAPODS: 1.15.2
2 changes: 1 addition & 1 deletion ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
2 changes: 1 addition & 1 deletion lib/src/common_widgets/top_gradient.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class TopGradient extends StatelessWidget {
Colors.black87,
Colors.transparent,
],
stops: [0.0, 0.3],
stops: [0.1, 0.5],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
tileMode: TileMode.repeated,
Expand Down
13 changes: 5 additions & 8 deletions lib/src/features/movies/data/movies_repository.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'dart:async';

import 'package:dio/dio.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:tmdb_movie_app_riverpod/env/env.dart';
Expand All @@ -16,7 +15,7 @@ class MoviesRepository {
final Dio client;
final String apiKey;

Future<List<TMDBMovie>> searchMovies(
Future<TMDBMoviesResponse> searchMovies(
{required int page, String query = '', CancelToken? cancelToken}) async {
final url = Uri(
scheme: 'https',
Expand All @@ -30,11 +29,10 @@ class MoviesRepository {
},
).toString();
final response = await client.get(url, cancelToken: cancelToken);
final movies = TMDBMoviesResponse.fromJson(response.data);
return movies.results;
return TMDBMoviesResponse.fromJson(response.data);
}

Future<List<TMDBMovie>> nowPlayingMovies(
Future<TMDBMoviesResponse> nowPlayingMovies(
{required int page, CancelToken? cancelToken}) async {
final url = Uri(
scheme: 'https',
Expand All @@ -47,8 +45,7 @@ class MoviesRepository {
},
).toString();
final response = await client.get(url, cancelToken: cancelToken);
final movies = TMDBMoviesResponse.fromJson(response.data);
return movies.results;
return TMDBMoviesResponse.fromJson(response.data);
}

Future<TMDBMovie> movie(
Expand Down Expand Up @@ -89,7 +86,7 @@ Future<TMDBMovie> movie(

/// Provider to fetch paginated movies data
@riverpod
Future<List<TMDBMovie>> fetchMovies(
Future<TMDBMoviesResponse> fetchMovies(
FetchMoviesRef ref, {
required MoviesPagination pagination,
}) async {
Expand Down
15 changes: 8 additions & 7 deletions lib/src/features/movies/data/movies_repository.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions lib/src/features/movies/domain/tmdb_movie.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ part of 'tmdb_movie.dart';
T _$identity<T>(T value) => value;

final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');

TMDBMovie _$TMDBMovieFromJson(Map<String, dynamic> json) {
return _TMDBMovieBasic.fromJson(json);
Expand Down Expand Up @@ -347,7 +347,7 @@ class _$TMDBMovieBasicImpl implements _TMDBMovieBasic {
}

@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$TMDBMovieBasicImpl &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ part of 'tmdb_movies_response.dart';
T _$identity<T>(T value) => value;

final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');

TMDBMoviesResponse _$TMDBMoviesResponseFromJson(Map<String, dynamic> json) {
return _TMDBMoviesResponse.fromJson(json);
Expand Down Expand Up @@ -196,7 +196,7 @@ class _$TMDBMoviesResponseImpl implements _TMDBMoviesResponse {
}

@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$TMDBMoviesResponseImpl &&
Expand Down
10 changes: 6 additions & 4 deletions lib/src/features/movies/presentation/movies/movie_list_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class MovieListTile extends StatelessWidget {
final int? debugIndex;
final VoidCallback? onPressed;

static const posterHeight = 80.0;

@override
Widget build(BuildContext context) {
return Padding(
Expand All @@ -26,15 +28,15 @@ class MovieListTile extends StatelessWidget {
child: Stack(
children: [
SizedBox(
width: MoviePoster.width,
height: MoviePoster.height,
width: posterHeight * MoviePoster.width / MoviePoster.height,
height: posterHeight,
child: MoviePoster(imagePath: movie.posterPath),
),
if (debugIndex != null) ...[
const Positioned.fill(child: TopGradient()),
Positioned(
left: 8,
top: 8,
left: 4,
top: 4,
child: Text(
'$debugIndex',
style: const TextStyle(color: Colors.white, fontSize: 14),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:shimmer/shimmer.dart';
import 'package:tmdb_movie_app_riverpod/src/common_widgets/movie_poster.dart';
import 'package:tmdb_movie_app_riverpod/src/features/movies/presentation/movies/movie_list_tile.dart';

class MovieListTileShimmer extends StatelessWidget {
const MovieListTileShimmer({super.key});
Expand All @@ -15,8 +16,10 @@ class MovieListTileShimmer extends StatelessWidget {
child: Row(
children: [
Container(
width: MoviePoster.width,
height: MoviePoster.height,
width: MovieListTile.posterHeight *
MoviePoster.width /
MoviePoster.height,
height: MovieListTile.posterHeight,
color: Colors.black,
),
const SizedBox(width: 8),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ class MoviesSearchScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final query = ref.watch(moviesSearchTextProvider);
// * get the first page so we can retrieve the total number of results
final moviesResponse = ref.watch(
fetchMoviesProvider(pagination: MoviesPagination(page: 1, query: query)),
);
final totalResults = moviesResponse.valueOrNull?.totalResults;
return Scaffold(
appBar: AppBar(
title: const Text('TMDB Movies'),
Expand All @@ -35,40 +40,50 @@ class MoviesSearchScreen extends ConsumerWidget {
).future,
);
},
// TODO: Limit item count to pagination results
child: ListView.custom(
childrenDelegate: SliverChildBuilderDelegate((context, index) {
final page = index ~/ pageSize + 1;
final indexInPage = index % pageSize;
// use the fact that this is an infinite list to fetch a new page
// as soon as the index exceeds the page size
// Note that ref.watch is called for up to pageSize items
// with the same page and query arguments (but this is ok since data is cached)
final moviesList = ref.watch(
fetchMoviesProvider(
pagination: MoviesPagination(page: page, query: query)),
);
return moviesList.when(
// TODO: Improve error handling
error: (err, stack) => Text('Error $err'),
loading: () => const MovieListTileShimmer(),
data: (movies) {
if (indexInPage >= movies.length) {
return const MovieListTileShimmer();
}
final movie = movies[indexInPage];
return MovieListTile(
movie: movie,
debugIndex: index,
onPressed: () => context.goNamed(
AppRoute.movie.name,
pathParameters: {'id': movie.id.toString()},
extra: movie,
),
);
},
);
}),
childrenDelegate: SliverChildBuilderDelegate(
(context, index) {
final page = index ~/ pageSize + 1;
final indexInPage = index % pageSize;
// use the fact that this is an infinite list to fetch a new page
// as soon as the index exceeds the page size
// Note that ref.watch is called for up to pageSize items
// with the same page and query arguments (but this is ok since data is cached)
final moviesResponse = ref.watch(
fetchMoviesProvider(
pagination:
MoviesPagination(page: page, query: query)),
);
return moviesResponse.when(
// * Only show error on the first item of the page
error: (err, stack) => indexInPage == 0
? Padding(
padding: const EdgeInsets.all(16.0),
child: Text(err.toString()),
)
: const SizedBox.shrink(),
loading: () => const MovieListTileShimmer(),
data: (movies) {
// * This condition should not happen if a non-null
// * childCount is given
if (indexInPage >= movies.results.length) {
return null;
}
final movie = movies.results[indexInPage];
return MovieListTile(
movie: movie,
debugIndex: index,
onPressed: () => context.goNamed(
AppRoute.movie.name,
pathParameters: {'id': movie.id.toString()},
extra: movie,
),
);
},
);
},
childCount: totalResults,
),
),
),
),
Expand Down
Loading

0 comments on commit 7c4514d

Please sign in to comment.