Skip to content

Commit

Permalink
Add season filter and improve UI with elevated_dropdown_button.dart
Browse files Browse the repository at this point in the history
  • Loading branch information
Ziedelth committed May 23, 2024
1 parent a0b837f commit 2daa100
Show file tree
Hide file tree
Showing 13 changed files with 413 additions and 80 deletions.
2 changes: 1 addition & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,4 @@ dart_code_linter:
- prefer-conditional-expressions
- no-equal-then-else
- prefer-moving-to-variable:
allowed-duplicated-chains: 3
allowed-duplicated-chains: 4
90 changes: 90 additions & 0 deletions lib/components/elevated_dropdown_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import 'package:flutter/material.dart';

class ElevatedDropdownButton<T> extends StatelessWidget {
final GlobalKey globalKey;
final T? value;
final List<ElevatedPopupMenuItem<T>> items;
final Function(T) onChanged;
final bool showIcon;
final Widget? child;

const ElevatedDropdownButton({
required this.globalKey,
required this.value,
required this.items,
required this.onChanged,
this.showIcon = true,
this.child,
}) : super(key: globalKey);

@override
Widget build(BuildContext context) {
return ElevatedButton(
key: globalKey,
onPressed: () {
final renderBox =
globalKey.currentContext!.findRenderObject() as RenderBox;
// Get position of the season button
final position = renderBox.localToGlobal(Offset.zero);

showMenu<T>(
context: context,
position: RelativeRect.fromLTRB(
position.dx,
position.dy,
position.dx,
position.dy,
),
items: [...items.map((item) => item.build())],
).then((value) {
if (value != null) {
onChanged(value);
}
});
},
child: Flex(
direction: Axis.horizontal,
children: [
if (child != null)
child!
else
items
.firstWhere(
(item) => item.value == value,
orElse: () => items.first,
)
.child,
if (showIcon) ...[
const SizedBox(width: 8),
const Icon(Icons.arrow_drop_down),
],
],
),
);
}
}

class ElevatedPopupMenuItem<T> {
final T value;
final Widget? leading;
final Widget child;

const ElevatedPopupMenuItem({
required this.value,
this.leading,
required this.child,
});

PopupMenuItem<T> build() {
return PopupMenuItem<T>(
value: value,
child: Row(
children: [
if (leading != null) leading!,
const SizedBox(width: 8),
child,
],
),
);
}
}
2 changes: 1 addition & 1 deletion lib/components/episodes/episode_type_component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class EpisodeTypeComponent extends StatelessWidget {
AppLocalizations.of(context)!.information(
_episodeType(context),
episode.number,
episode.season,
AppLocalizations.of(context)!.season(episode.season),
),
style: Theme.of(context).textTheme.bodyMedium,
overflow: TextOverflow.ellipsis,
Expand Down
5 changes: 4 additions & 1 deletion lib/controllers/anime_details_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';

import 'package:application/dtos/anime_dto.dart';
import 'package:application/dtos/episode_mapping_dto.dart';
import 'package:application/dtos/season_dto.dart';
import 'package:application/utils/http_request.dart';
import 'package:flutter/material.dart';

Expand Down Expand Up @@ -33,6 +34,7 @@ class AnimeDetailsController {
bool canLoadMore = true;

AnimeDto? anime;
SeasonDto? season;
Sort sort = Sort.oldest;

Future<void> init() async {
Expand All @@ -58,6 +60,7 @@ class AnimeDetailsController {

void dispose() {
anime = null;
season = null;
sort = Sort.oldest;
episodes.clear();
streamController.add(episodes);
Expand All @@ -80,7 +83,7 @@ class AnimeDetailsController {

try {
final pageableDto = await HttpRequest.instance.getPage(
'/v1/episode-mappings?anime=${anime?.uuid}&${sort.value}&page=$page&limit=4',
'/v1/episode-mappings?anime=${anime?.uuid}${season != null ? '&season=${season!.number}' : ''}&${sort.value}&page=$page&limit=4',
);

episodes.addAll(
Expand Down
2 changes: 2 additions & 0 deletions lib/dtos/anime_dto.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:application/dtos/season_dto.dart';
import 'package:application/dtos/simulcast_dto.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

Expand All @@ -20,6 +21,7 @@ class AnimeDto with _$AnimeDto {
required List<SimulcastDto> simulcasts,
required List<String> audioLocales,
required List<String> langTypes,
required List<SeasonDto> seasons,
required String? status,
}) = _AnimeDto;

Expand Down
32 changes: 30 additions & 2 deletions lib/dtos/anime_dto.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ mixin _$AnimeDto {
List<SimulcastDto> get simulcasts => throw _privateConstructorUsedError;
List<String> get audioLocales => throw _privateConstructorUsedError;
List<String> get langTypes => throw _privateConstructorUsedError;
List<SeasonDto> get seasons => throw _privateConstructorUsedError;
String? get status => throw _privateConstructorUsedError;

Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
Expand Down Expand Up @@ -60,6 +61,7 @@ abstract class $AnimeDtoCopyWith<$Res> {
List<SimulcastDto> simulcasts,
List<String> audioLocales,
List<String> langTypes,
List<SeasonDto> seasons,
String? status});
}

Expand Down Expand Up @@ -89,6 +91,7 @@ class _$AnimeDtoCopyWithImpl<$Res, $Val extends AnimeDto>
Object? simulcasts = null,
Object? audioLocales = null,
Object? langTypes = null,
Object? seasons = null,
Object? status = freezed,
}) {
return _then(_value.copyWith(
Expand Down Expand Up @@ -144,6 +147,10 @@ class _$AnimeDtoCopyWithImpl<$Res, $Val extends AnimeDto>
? _value.langTypes
: langTypes // ignore: cast_nullable_to_non_nullable
as List<String>,
seasons: null == seasons
? _value.seasons
: seasons // ignore: cast_nullable_to_non_nullable
as List<SeasonDto>,
status: freezed == status
? _value.status
: status // ignore: cast_nullable_to_non_nullable
Expand Down Expand Up @@ -174,6 +181,7 @@ abstract class _$$AnimeDtoImplCopyWith<$Res>
List<SimulcastDto> simulcasts,
List<String> audioLocales,
List<String> langTypes,
List<SeasonDto> seasons,
String? status});
}

Expand Down Expand Up @@ -201,6 +209,7 @@ class __$$AnimeDtoImplCopyWithImpl<$Res>
Object? simulcasts = null,
Object? audioLocales = null,
Object? langTypes = null,
Object? seasons = null,
Object? status = freezed,
}) {
return _then(_$AnimeDtoImpl(
Expand Down Expand Up @@ -256,6 +265,10 @@ class __$$AnimeDtoImplCopyWithImpl<$Res>
? _value._langTypes
: langTypes // ignore: cast_nullable_to_non_nullable
as List<String>,
seasons: null == seasons
? _value._seasons
: seasons // ignore: cast_nullable_to_non_nullable
as List<SeasonDto>,
status: freezed == status
? _value.status
: status // ignore: cast_nullable_to_non_nullable
Expand All @@ -281,10 +294,12 @@ class _$AnimeDtoImpl implements _AnimeDto {
required final List<SimulcastDto> simulcasts,
required final List<String> audioLocales,
required final List<String> langTypes,
required final List<SeasonDto> seasons,
required this.status})
: _simulcasts = simulcasts,
_audioLocales = audioLocales,
_langTypes = langTypes;
_langTypes = langTypes,
_seasons = seasons;

factory _$AnimeDtoImpl.fromJson(Map<String, dynamic> json) =>
_$$AnimeDtoImplFromJson(json);
Expand Down Expand Up @@ -333,12 +348,20 @@ class _$AnimeDtoImpl implements _AnimeDto {
return EqualUnmodifiableListView(_langTypes);
}

final List<SeasonDto> _seasons;
@override
List<SeasonDto> get seasons {
if (_seasons is EqualUnmodifiableListView) return _seasons;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_seasons);
}

@override
final String? status;

@override
String toString() {
return 'AnimeDto(uuid: $uuid, countryCode: $countryCode, name: $name, shortName: $shortName, slug: $slug, releaseDateTime: $releaseDateTime, lastReleaseDateTime: $lastReleaseDateTime, image: $image, banner: $banner, description: $description, simulcasts: $simulcasts, audioLocales: $audioLocales, langTypes: $langTypes, status: $status)';
return 'AnimeDto(uuid: $uuid, countryCode: $countryCode, name: $name, shortName: $shortName, slug: $slug, releaseDateTime: $releaseDateTime, lastReleaseDateTime: $lastReleaseDateTime, image: $image, banner: $banner, description: $description, simulcasts: $simulcasts, audioLocales: $audioLocales, langTypes: $langTypes, seasons: $seasons, status: $status)';
}

@override
Expand Down Expand Up @@ -367,6 +390,7 @@ class _$AnimeDtoImpl implements _AnimeDto {
.equals(other._audioLocales, _audioLocales) &&
const DeepCollectionEquality()
.equals(other._langTypes, _langTypes) &&
const DeepCollectionEquality().equals(other._seasons, _seasons) &&
(identical(other.status, status) || other.status == status));
}

Expand All @@ -387,6 +411,7 @@ class _$AnimeDtoImpl implements _AnimeDto {
const DeepCollectionEquality().hash(_simulcasts),
const DeepCollectionEquality().hash(_audioLocales),
const DeepCollectionEquality().hash(_langTypes),
const DeepCollectionEquality().hash(_seasons),
status);

@JsonKey(ignore: true)
Expand Down Expand Up @@ -418,6 +443,7 @@ abstract class _AnimeDto implements AnimeDto {
required final List<SimulcastDto> simulcasts,
required final List<String> audioLocales,
required final List<String> langTypes,
required final List<SeasonDto> seasons,
required final String? status}) = _$AnimeDtoImpl;

factory _AnimeDto.fromJson(Map<String, dynamic> json) =
Expand Down Expand Up @@ -450,6 +476,8 @@ abstract class _AnimeDto implements AnimeDto {
@override
List<String> get langTypes;
@override
List<SeasonDto> get seasons;
@override
String? get status;
@override
@JsonKey(ignore: true)
Expand Down
4 changes: 4 additions & 0 deletions lib/dtos/anime_dto.g.dart

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

15 changes: 15 additions & 0 deletions lib/dtos/season_dto.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'package:freezed_annotation/freezed_annotation.dart';

part 'season_dto.freezed.dart';
part 'season_dto.g.dart';

@freezed
class SeasonDto with _$SeasonDto {
const factory SeasonDto({
required int number,
required String lastReleaseDateTime,
}) = _SeasonDto;

factory SeasonDto.fromJson(Map<String, dynamic> json) =>
_$SeasonDtoFromJson(json);
}
Loading

0 comments on commit 2daa100

Please sign in to comment.