feat(meta): Cleanup
This commit is contained in:
parent
8b17f68eed
commit
8712dbb9de
@ -1,29 +1,14 @@
|
|||||||
# This file configures the analyzer, which statically analyzes Dart code to
|
include: package:very_good_analysis/analysis_options.yaml
|
||||||
# check for errors, warnings, and lints.
|
|
||||||
#
|
|
||||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
|
||||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
|
||||||
# invoked from the command line by running `flutter analyze`.
|
|
||||||
|
|
||||||
# The following line activates a set of recommended lints for Flutter apps,
|
|
||||||
# packages, and plugins designed to encourage good coding practices.
|
|
||||||
include: package:flutter_lints/flutter.yaml
|
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
# The lint rules applied to this project can be customized in the
|
|
||||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
|
||||||
# included above or to enable additional rules. A list of all available lints
|
|
||||||
# and their documentation is published at
|
|
||||||
# https://dart-lang.github.io/linter/lints/index.html.
|
|
||||||
#
|
|
||||||
# Instead of disabling a lint rule for the entire project in the
|
|
||||||
# section below, it can also be suppressed for a single line of code
|
|
||||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
|
||||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
|
||||||
# producing the lint.
|
|
||||||
rules:
|
rules:
|
||||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
public_member_api_docs: false
|
||||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
lines_longer_than_80_chars: false
|
||||||
|
use_setters_to_change_properties: false
|
||||||
|
avoid_positional_boolean_parameters: false
|
||||||
|
avoid_bool_literals_in_conditional_expressions: false
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
analyzer:
|
||||||
# https://dart.dev/guides/language/analysis-options
|
exclude:
|
||||||
|
- "**/*.g.dart"
|
||||||
|
- "**/*.freezed.dart"
|
||||||
|
- "test/"
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:anitrack/src/service/database.dart';
|
||||||
import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart';
|
import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart';
|
||||||
import 'package:anitrack/src/ui/bloc/anime_search_bloc.dart';
|
import 'package:anitrack/src/ui/bloc/anime_search_bloc.dart';
|
||||||
import 'package:anitrack/src/ui/bloc/details_bloc.dart';
|
import 'package:anitrack/src/ui/bloc/details_bloc.dart';
|
||||||
@ -6,7 +7,6 @@ import 'package:anitrack/src/ui/constants.dart';
|
|||||||
import 'package:anitrack/src/ui/pages/anime_list.dart';
|
import 'package:anitrack/src/ui/pages/anime_list.dart';
|
||||||
import 'package:anitrack/src/ui/pages/anime_search.dart';
|
import 'package:anitrack/src/ui/pages/anime_search.dart';
|
||||||
import 'package:anitrack/src/ui/pages/details.dart';
|
import 'package:anitrack/src/ui/pages/details.dart';
|
||||||
import 'package:anitrack/src/service/database.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
@ -66,7 +66,6 @@ class MyApp extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'AniTrack',
|
title: 'AniTrack',
|
||||||
themeMode: ThemeMode.system,
|
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
primarySwatch: Colors.blue,
|
primarySwatch: Colors.blue,
|
||||||
|
@ -1,62 +1,9 @@
|
|||||||
|
import 'package:anitrack/src/data/type.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
part 'anime.freezed.dart';
|
part 'anime.freezed.dart';
|
||||||
part 'anime.g.dart';
|
part 'anime.g.dart';
|
||||||
|
|
||||||
/// The watch state of an anime
|
|
||||||
enum AnimeTrackingState {
|
|
||||||
watching, // 0
|
|
||||||
completed, // 1
|
|
||||||
planToWatch, // 2
|
|
||||||
dropped, // 3
|
|
||||||
/// This is a pseudo state, i.e. it should never be set
|
|
||||||
all, // -1
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AnimeTrackStateExtension on AnimeTrackingState {
|
|
||||||
int toInteger() {
|
|
||||||
assert(this != AnimeTrackingState.all, 'AnimeTrackingState.all must not be serialized');
|
|
||||||
switch (this) {
|
|
||||||
case AnimeTrackingState.watching: return 0;
|
|
||||||
case AnimeTrackingState.completed: return 1;
|
|
||||||
case AnimeTrackingState.planToWatch: return 2;
|
|
||||||
case AnimeTrackingState.dropped: return 3;
|
|
||||||
case AnimeTrackingState.all: return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String toNameString() {
|
|
||||||
assert(this != AnimeTrackingState.all, 'AnimeTrackingState.all must not be stringified');
|
|
||||||
|
|
||||||
switch (this) {
|
|
||||||
case AnimeTrackingState.watching: return 'Watching';
|
|
||||||
case AnimeTrackingState.completed: return 'Completed';
|
|
||||||
case AnimeTrackingState.planToWatch: return 'Plan to watch';
|
|
||||||
case AnimeTrackingState.dropped: return 'Dropped';
|
|
||||||
case AnimeTrackingState.all: return 'All';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AnimeTrackingStateConverter implements JsonConverter<AnimeTrackingState, int> {
|
|
||||||
const AnimeTrackingStateConverter();
|
|
||||||
|
|
||||||
@override
|
|
||||||
AnimeTrackingState fromJson(int json) {
|
|
||||||
switch (json) {
|
|
||||||
case 0: return AnimeTrackingState.watching;
|
|
||||||
case 1: return AnimeTrackingState.completed;
|
|
||||||
case 2: return AnimeTrackingState.planToWatch;
|
|
||||||
case 3: return AnimeTrackingState.dropped;
|
|
||||||
}
|
|
||||||
|
|
||||||
return AnimeTrackingState.planToWatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int toJson(AnimeTrackingState state) => state.toInteger();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data about a tracked anime
|
/// Data about a tracked anime
|
||||||
@freezed
|
@freezed
|
||||||
class AnimeTrackingData with _$AnimeTrackingData{
|
class AnimeTrackingData with _$AnimeTrackingData{
|
||||||
@ -64,7 +11,7 @@ class AnimeTrackingData with _$AnimeTrackingData{
|
|||||||
/// The ID of the anime
|
/// The ID of the anime
|
||||||
String id,
|
String id,
|
||||||
/// The state of the anime
|
/// The state of the anime
|
||||||
@AnimeTrackingStateConverter() AnimeTrackingState state,
|
@MediumTrackingStateConverter() MediumTrackingState state,
|
||||||
/// The title of the anime
|
/// The title of the anime
|
||||||
String title,
|
String title,
|
||||||
/// Episodes in total.
|
/// Episodes in total.
|
||||||
|
@ -24,8 +24,8 @@ mixin _$AnimeTrackingData {
|
|||||||
String get id => throw _privateConstructorUsedError;
|
String get id => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// The state of the anime
|
/// The state of the anime
|
||||||
@AnimeTrackingStateConverter()
|
@MediumTrackingStateConverter()
|
||||||
AnimeTrackingState get state => throw _privateConstructorUsedError;
|
MediumTrackingState get state => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// The title of the anime
|
/// The title of the anime
|
||||||
String get title => throw _privateConstructorUsedError;
|
String get title => throw _privateConstructorUsedError;
|
||||||
@ -52,7 +52,7 @@ abstract class $AnimeTrackingDataCopyWith<$Res> {
|
|||||||
_$AnimeTrackingDataCopyWithImpl<$Res>;
|
_$AnimeTrackingDataCopyWithImpl<$Res>;
|
||||||
$Res call(
|
$Res call(
|
||||||
{String id,
|
{String id,
|
||||||
@AnimeTrackingStateConverter() AnimeTrackingState state,
|
@MediumTrackingStateConverter() MediumTrackingState state,
|
||||||
String title,
|
String title,
|
||||||
int episodesWatched,
|
int episodesWatched,
|
||||||
int? episodesTotal,
|
int? episodesTotal,
|
||||||
@ -85,7 +85,7 @@ class _$AnimeTrackingDataCopyWithImpl<$Res>
|
|||||||
state: state == freezed
|
state: state == freezed
|
||||||
? _value.state
|
? _value.state
|
||||||
: state // ignore: cast_nullable_to_non_nullable
|
: state // ignore: cast_nullable_to_non_nullable
|
||||||
as AnimeTrackingState,
|
as MediumTrackingState,
|
||||||
title: title == freezed
|
title: title == freezed
|
||||||
? _value.title
|
? _value.title
|
||||||
: title // ignore: cast_nullable_to_non_nullable
|
: title // ignore: cast_nullable_to_non_nullable
|
||||||
@ -115,7 +115,7 @@ abstract class _$$_AnimeTrackingDataCopyWith<$Res>
|
|||||||
@override
|
@override
|
||||||
$Res call(
|
$Res call(
|
||||||
{String id,
|
{String id,
|
||||||
@AnimeTrackingStateConverter() AnimeTrackingState state,
|
@MediumTrackingStateConverter() MediumTrackingState state,
|
||||||
String title,
|
String title,
|
||||||
int episodesWatched,
|
int episodesWatched,
|
||||||
int? episodesTotal,
|
int? episodesTotal,
|
||||||
@ -150,7 +150,7 @@ class __$$_AnimeTrackingDataCopyWithImpl<$Res>
|
|||||||
state == freezed
|
state == freezed
|
||||||
? _value.state
|
? _value.state
|
||||||
: state // ignore: cast_nullable_to_non_nullable
|
: state // ignore: cast_nullable_to_non_nullable
|
||||||
as AnimeTrackingState,
|
as MediumTrackingState,
|
||||||
title == freezed
|
title == freezed
|
||||||
? _value.title
|
? _value.title
|
||||||
: title // ignore: cast_nullable_to_non_nullable
|
: title // ignore: cast_nullable_to_non_nullable
|
||||||
@ -174,7 +174,7 @@ class __$$_AnimeTrackingDataCopyWithImpl<$Res>
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class _$_AnimeTrackingData implements _AnimeTrackingData {
|
class _$_AnimeTrackingData implements _AnimeTrackingData {
|
||||||
_$_AnimeTrackingData(this.id, @AnimeTrackingStateConverter() this.state,
|
_$_AnimeTrackingData(this.id, @MediumTrackingStateConverter() this.state,
|
||||||
this.title, this.episodesWatched, this.episodesTotal, this.thumbnailUrl);
|
this.title, this.episodesWatched, this.episodesTotal, this.thumbnailUrl);
|
||||||
|
|
||||||
factory _$_AnimeTrackingData.fromJson(Map<String, dynamic> json) =>
|
factory _$_AnimeTrackingData.fromJson(Map<String, dynamic> json) =>
|
||||||
@ -186,8 +186,8 @@ class _$_AnimeTrackingData implements _AnimeTrackingData {
|
|||||||
|
|
||||||
/// The state of the anime
|
/// The state of the anime
|
||||||
@override
|
@override
|
||||||
@AnimeTrackingStateConverter()
|
@MediumTrackingStateConverter()
|
||||||
final AnimeTrackingState state;
|
final MediumTrackingState state;
|
||||||
|
|
||||||
/// The title of the anime
|
/// The title of the anime
|
||||||
@override
|
@override
|
||||||
@ -254,7 +254,7 @@ class _$_AnimeTrackingData implements _AnimeTrackingData {
|
|||||||
abstract class _AnimeTrackingData implements AnimeTrackingData {
|
abstract class _AnimeTrackingData implements AnimeTrackingData {
|
||||||
factory _AnimeTrackingData(
|
factory _AnimeTrackingData(
|
||||||
final String id,
|
final String id,
|
||||||
@AnimeTrackingStateConverter() final AnimeTrackingState state,
|
@MediumTrackingStateConverter() final MediumTrackingState state,
|
||||||
final String title,
|
final String title,
|
||||||
final int episodesWatched,
|
final int episodesWatched,
|
||||||
final int? episodesTotal,
|
final int? episodesTotal,
|
||||||
@ -270,8 +270,8 @@ abstract class _AnimeTrackingData implements AnimeTrackingData {
|
|||||||
@override
|
@override
|
||||||
|
|
||||||
/// The state of the anime
|
/// The state of the anime
|
||||||
@AnimeTrackingStateConverter()
|
@MediumTrackingStateConverter()
|
||||||
AnimeTrackingState get state;
|
MediumTrackingState get state;
|
||||||
@override
|
@override
|
||||||
|
|
||||||
/// The title of the anime
|
/// The title of the anime
|
||||||
|
@ -9,7 +9,7 @@ part of 'anime.dart';
|
|||||||
_$_AnimeTrackingData _$$_AnimeTrackingDataFromJson(Map<String, dynamic> json) =>
|
_$_AnimeTrackingData _$$_AnimeTrackingDataFromJson(Map<String, dynamic> json) =>
|
||||||
_$_AnimeTrackingData(
|
_$_AnimeTrackingData(
|
||||||
json['id'] as String,
|
json['id'] as String,
|
||||||
const AnimeTrackingStateConverter().fromJson(json['state'] as int),
|
const MediumTrackingStateConverter().fromJson(json['state'] as int),
|
||||||
json['title'] as String,
|
json['title'] as String,
|
||||||
json['episodesWatched'] as int,
|
json['episodesWatched'] as int,
|
||||||
json['episodesTotal'] as int?,
|
json['episodesTotal'] as int?,
|
||||||
@ -20,7 +20,7 @@ Map<String, dynamic> _$$_AnimeTrackingDataToJson(
|
|||||||
_$_AnimeTrackingData instance) =>
|
_$_AnimeTrackingData instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'id': instance.id,
|
'id': instance.id,
|
||||||
'state': const AnimeTrackingStateConverter().toJson(instance.state),
|
'state': const MediumTrackingStateConverter().toJson(instance.state),
|
||||||
'title': instance.title,
|
'title': instance.title,
|
||||||
'episodesWatched': instance.episodesWatched,
|
'episodesWatched': instance.episodesWatched,
|
||||||
'episodesTotal': instance.episodesTotal,
|
'episodesTotal': instance.episodesTotal,
|
||||||
|
@ -1,62 +1,9 @@
|
|||||||
|
import 'package:anitrack/src/data/type.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
part 'manga.freezed.dart';
|
part 'manga.freezed.dart';
|
||||||
part 'manga.g.dart';
|
part 'manga.g.dart';
|
||||||
|
|
||||||
/// The watch state of an manga
|
|
||||||
enum MangaTrackingState {
|
|
||||||
reading, // 0
|
|
||||||
completed, // 1
|
|
||||||
planToRead, // 2
|
|
||||||
dropped, // 3
|
|
||||||
/// This is a pseudo state, i.e. it should never be set
|
|
||||||
all, // -1
|
|
||||||
}
|
|
||||||
|
|
||||||
extension MangaTrackStateExtension on MangaTrackingState {
|
|
||||||
int toInteger() {
|
|
||||||
assert(this != MangaTrackingState.all, 'MangaTrackingState.all must not be serialized');
|
|
||||||
switch (this) {
|
|
||||||
case MangaTrackingState.reading: return 0;
|
|
||||||
case MangaTrackingState.completed: return 1;
|
|
||||||
case MangaTrackingState.planToRead: return 2;
|
|
||||||
case MangaTrackingState.dropped: return 3;
|
|
||||||
case MangaTrackingState.all: return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String toNameString() {
|
|
||||||
assert(this != MangaTrackingState.all, 'MangaTrackingState.all must not be stringified');
|
|
||||||
|
|
||||||
switch (this) {
|
|
||||||
case MangaTrackingState.reading: return 'Reading';
|
|
||||||
case MangaTrackingState.completed: return 'Completed';
|
|
||||||
case MangaTrackingState.planToRead: return 'Plan to read';
|
|
||||||
case MangaTrackingState.dropped: return 'Dropped';
|
|
||||||
case MangaTrackingState.all: return 'All';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MangaTrackingStateConverter implements JsonConverter<MangaTrackingState, int> {
|
|
||||||
const MangaTrackingStateConverter();
|
|
||||||
|
|
||||||
@override
|
|
||||||
MangaTrackingState fromJson(int json) {
|
|
||||||
switch (json) {
|
|
||||||
case 0: return MangaTrackingState.reading;
|
|
||||||
case 1: return MangaTrackingState.completed;
|
|
||||||
case 2: return MangaTrackingState.planToRead;
|
|
||||||
case 3: return MangaTrackingState.dropped;
|
|
||||||
}
|
|
||||||
|
|
||||||
return MangaTrackingState.planToRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int toJson(MangaTrackingState state) => state.toInteger();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data about a tracked anime
|
/// Data about a tracked anime
|
||||||
@freezed
|
@freezed
|
||||||
class MangaTrackingData with _$MangaTrackingData{
|
class MangaTrackingData with _$MangaTrackingData{
|
||||||
@ -64,7 +11,7 @@ class MangaTrackingData with _$MangaTrackingData{
|
|||||||
/// The ID of the manga
|
/// The ID of the manga
|
||||||
String id,
|
String id,
|
||||||
/// The state of the manga
|
/// The state of the manga
|
||||||
@MangaTrackingStateConverter() MangaTrackingState state,
|
@MediumTrackingStateConverter() MediumTrackingState state,
|
||||||
/// The title of the manga
|
/// The title of the manga
|
||||||
String title,
|
String title,
|
||||||
/// Chapters read.
|
/// Chapters read.
|
||||||
|
@ -24,8 +24,8 @@ mixin _$MangaTrackingData {
|
|||||||
String get id => throw _privateConstructorUsedError;
|
String get id => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// The state of the manga
|
/// The state of the manga
|
||||||
@MangaTrackingStateConverter()
|
@MediumTrackingStateConverter()
|
||||||
MangaTrackingState get state => throw _privateConstructorUsedError;
|
MediumTrackingState get state => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// The title of the manga
|
/// The title of the manga
|
||||||
String get title => throw _privateConstructorUsedError;
|
String get title => throw _privateConstructorUsedError;
|
||||||
@ -55,7 +55,7 @@ abstract class $MangaTrackingDataCopyWith<$Res> {
|
|||||||
_$MangaTrackingDataCopyWithImpl<$Res>;
|
_$MangaTrackingDataCopyWithImpl<$Res>;
|
||||||
$Res call(
|
$Res call(
|
||||||
{String id,
|
{String id,
|
||||||
@MangaTrackingStateConverter() MangaTrackingState state,
|
@MediumTrackingStateConverter() MediumTrackingState state,
|
||||||
String title,
|
String title,
|
||||||
int chaptersRead,
|
int chaptersRead,
|
||||||
int volumesOwned,
|
int volumesOwned,
|
||||||
@ -90,7 +90,7 @@ class _$MangaTrackingDataCopyWithImpl<$Res>
|
|||||||
state: state == freezed
|
state: state == freezed
|
||||||
? _value.state
|
? _value.state
|
||||||
: state // ignore: cast_nullable_to_non_nullable
|
: state // ignore: cast_nullable_to_non_nullable
|
||||||
as MangaTrackingState,
|
as MediumTrackingState,
|
||||||
title: title == freezed
|
title: title == freezed
|
||||||
? _value.title
|
? _value.title
|
||||||
: title // ignore: cast_nullable_to_non_nullable
|
: title // ignore: cast_nullable_to_non_nullable
|
||||||
@ -124,7 +124,7 @@ abstract class _$$_MangaTrackingDataCopyWith<$Res>
|
|||||||
@override
|
@override
|
||||||
$Res call(
|
$Res call(
|
||||||
{String id,
|
{String id,
|
||||||
@MangaTrackingStateConverter() MangaTrackingState state,
|
@MediumTrackingStateConverter() MediumTrackingState state,
|
||||||
String title,
|
String title,
|
||||||
int chaptersRead,
|
int chaptersRead,
|
||||||
int volumesOwned,
|
int volumesOwned,
|
||||||
@ -161,7 +161,7 @@ class __$$_MangaTrackingDataCopyWithImpl<$Res>
|
|||||||
state == freezed
|
state == freezed
|
||||||
? _value.state
|
? _value.state
|
||||||
: state // ignore: cast_nullable_to_non_nullable
|
: state // ignore: cast_nullable_to_non_nullable
|
||||||
as MangaTrackingState,
|
as MediumTrackingState,
|
||||||
title == freezed
|
title == freezed
|
||||||
? _value.title
|
? _value.title
|
||||||
: title // ignore: cast_nullable_to_non_nullable
|
: title // ignore: cast_nullable_to_non_nullable
|
||||||
@ -191,7 +191,7 @@ class __$$_MangaTrackingDataCopyWithImpl<$Res>
|
|||||||
class _$_MangaTrackingData implements _MangaTrackingData {
|
class _$_MangaTrackingData implements _MangaTrackingData {
|
||||||
_$_MangaTrackingData(
|
_$_MangaTrackingData(
|
||||||
this.id,
|
this.id,
|
||||||
@MangaTrackingStateConverter() this.state,
|
@MediumTrackingStateConverter() this.state,
|
||||||
this.title,
|
this.title,
|
||||||
this.chaptersRead,
|
this.chaptersRead,
|
||||||
this.volumesOwned,
|
this.volumesOwned,
|
||||||
@ -207,8 +207,8 @@ class _$_MangaTrackingData implements _MangaTrackingData {
|
|||||||
|
|
||||||
/// The state of the manga
|
/// The state of the manga
|
||||||
@override
|
@override
|
||||||
@MangaTrackingStateConverter()
|
@MediumTrackingStateConverter()
|
||||||
final MangaTrackingState state;
|
final MediumTrackingState state;
|
||||||
|
|
||||||
/// The title of the manga
|
/// The title of the manga
|
||||||
@override
|
@override
|
||||||
@ -282,7 +282,7 @@ class _$_MangaTrackingData implements _MangaTrackingData {
|
|||||||
abstract class _MangaTrackingData implements MangaTrackingData {
|
abstract class _MangaTrackingData implements MangaTrackingData {
|
||||||
factory _MangaTrackingData(
|
factory _MangaTrackingData(
|
||||||
final String id,
|
final String id,
|
||||||
@MangaTrackingStateConverter() final MangaTrackingState state,
|
@MediumTrackingStateConverter() final MediumTrackingState state,
|
||||||
final String title,
|
final String title,
|
||||||
final int chaptersRead,
|
final int chaptersRead,
|
||||||
final int volumesOwned,
|
final int volumesOwned,
|
||||||
@ -299,8 +299,8 @@ abstract class _MangaTrackingData implements MangaTrackingData {
|
|||||||
@override
|
@override
|
||||||
|
|
||||||
/// The state of the manga
|
/// The state of the manga
|
||||||
@MangaTrackingStateConverter()
|
@MediumTrackingStateConverter()
|
||||||
MangaTrackingState get state;
|
MediumTrackingState get state;
|
||||||
@override
|
@override
|
||||||
|
|
||||||
/// The title of the manga
|
/// The title of the manga
|
||||||
|
@ -9,7 +9,7 @@ part of 'manga.dart';
|
|||||||
_$_MangaTrackingData _$$_MangaTrackingDataFromJson(Map<String, dynamic> json) =>
|
_$_MangaTrackingData _$$_MangaTrackingDataFromJson(Map<String, dynamic> json) =>
|
||||||
_$_MangaTrackingData(
|
_$_MangaTrackingData(
|
||||||
json['id'] as String,
|
json['id'] as String,
|
||||||
const MangaTrackingStateConverter().fromJson(json['state'] as int),
|
const MediumTrackingStateConverter().fromJson(json['state'] as int),
|
||||||
json['title'] as String,
|
json['title'] as String,
|
||||||
json['chaptersRead'] as int,
|
json['chaptersRead'] as int,
|
||||||
json['volumesOwned'] as int,
|
json['volumesOwned'] as int,
|
||||||
@ -21,7 +21,7 @@ Map<String, dynamic> _$$_MangaTrackingDataToJson(
|
|||||||
_$_MangaTrackingData instance) =>
|
_$_MangaTrackingData instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'id': instance.id,
|
'id': instance.id,
|
||||||
'state': const MangaTrackingStateConverter().toJson(instance.state),
|
'state': const MediumTrackingStateConverter().toJson(instance.state),
|
||||||
'title': instance.title,
|
'title': instance.title,
|
||||||
'chaptersRead': instance.chaptersRead,
|
'chaptersRead': instance.chaptersRead,
|
||||||
'volumesOwned': instance.volumesOwned,
|
'volumesOwned': instance.volumesOwned,
|
||||||
|
@ -1,5 +1,68 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
/// The type of medium we are tracking. Useful for UI stuff.
|
/// The type of medium we are tracking. Useful for UI stuff.
|
||||||
enum TrackingMediumType {
|
enum TrackingMediumType {
|
||||||
anime,
|
anime,
|
||||||
manga,
|
manga,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The state of the medium we're tracking, i.e. reading/watching, dropped, ...
|
||||||
|
enum MediumTrackingState {
|
||||||
|
ongoing,
|
||||||
|
completed,
|
||||||
|
planned,
|
||||||
|
dropped,
|
||||||
|
all,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MediumStateExtension on MediumTrackingState {
|
||||||
|
int toInteger() {
|
||||||
|
assert(this != MediumTrackingState.all, 'MediumTrackingState.all must not be serialized');
|
||||||
|
switch (this) {
|
||||||
|
case MediumTrackingState.ongoing: return 0;
|
||||||
|
case MediumTrackingState.completed: return 1;
|
||||||
|
case MediumTrackingState.planned: return 2;
|
||||||
|
case MediumTrackingState.dropped: return 3;
|
||||||
|
case MediumTrackingState.all: return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String toNameString(TrackingMediumType type) {
|
||||||
|
assert(this != MediumTrackingState.all, 'MediumTrackingState.all must not be stringified');
|
||||||
|
|
||||||
|
switch (this) {
|
||||||
|
case MediumTrackingState.ongoing:
|
||||||
|
switch (type) {
|
||||||
|
case TrackingMediumType.anime: return 'Watching';
|
||||||
|
case TrackingMediumType.manga: return 'Reading';
|
||||||
|
}
|
||||||
|
case MediumTrackingState.completed: return 'Completed';
|
||||||
|
case MediumTrackingState.planned:
|
||||||
|
switch (type) {
|
||||||
|
case TrackingMediumType.anime: return 'Plan to watch';
|
||||||
|
case TrackingMediumType.manga: return 'Plan to read';
|
||||||
|
}
|
||||||
|
case MediumTrackingState.dropped: return 'Dropped';
|
||||||
|
case MediumTrackingState.all: return 'All';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MediumTrackingStateConverter implements JsonConverter<MediumTrackingState, int> {
|
||||||
|
const MediumTrackingStateConverter();
|
||||||
|
|
||||||
|
@override
|
||||||
|
MediumTrackingState fromJson(int json) {
|
||||||
|
switch (json) {
|
||||||
|
case 0: return MediumTrackingState.ongoing;
|
||||||
|
case 1: return MediumTrackingState.completed;
|
||||||
|
case 2: return MediumTrackingState.planned;
|
||||||
|
case 3: return MediumTrackingState.dropped;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MediumTrackingState.planned;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int toJson(MediumTrackingState state) => state.toInteger();
|
||||||
|
}
|
||||||
|
@ -47,7 +47,7 @@ class DatabaseService {
|
|||||||
|
|
||||||
return animes
|
return animes
|
||||||
.cast<Map<String, dynamic>>()
|
.cast<Map<String, dynamic>>()
|
||||||
.map((Map<String, dynamic> anime) => AnimeTrackingData.fromJson(anime))
|
.map(AnimeTrackingData.fromJson)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ class DatabaseService {
|
|||||||
|
|
||||||
return mangas
|
return mangas
|
||||||
.cast<Map<String, dynamic>>()
|
.cast<Map<String, dynamic>>()
|
||||||
.map((Map<String, dynamic> manga) => MangaTrackingData.fromJson(manga))
|
.map(MangaTrackingData.fromJson)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import 'dart:math';
|
|
||||||
import 'package:anitrack/src/data/anime.dart';
|
import 'package:anitrack/src/data/anime.dart';
|
||||||
import 'package:anitrack/src/data/manga.dart';
|
import 'package:anitrack/src/data/manga.dart';
|
||||||
import 'package:anitrack/src/data/type.dart';
|
import 'package:anitrack/src/data/type.dart';
|
||||||
|
@ -18,8 +18,10 @@ final _privateConstructorUsedError = UnsupportedError(
|
|||||||
mixin _$AnimeListState {
|
mixin _$AnimeListState {
|
||||||
List<AnimeTrackingData> get animes => throw _privateConstructorUsedError;
|
List<AnimeTrackingData> get animes => throw _privateConstructorUsedError;
|
||||||
List<MangaTrackingData> get mangas => throw _privateConstructorUsedError;
|
List<MangaTrackingData> get mangas => throw _privateConstructorUsedError;
|
||||||
AnimeTrackingState get animeFilterState => throw _privateConstructorUsedError;
|
MediumTrackingState get animeFilterState =>
|
||||||
MangaTrackingState get mangaFilterState => throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
|
MediumTrackingState get mangaFilterState =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
TrackingMediumType get trackingType => throw _privateConstructorUsedError;
|
TrackingMediumType get trackingType => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
@ -35,8 +37,8 @@ abstract class $AnimeListStateCopyWith<$Res> {
|
|||||||
$Res call(
|
$Res call(
|
||||||
{List<AnimeTrackingData> animes,
|
{List<AnimeTrackingData> animes,
|
||||||
List<MangaTrackingData> mangas,
|
List<MangaTrackingData> mangas,
|
||||||
AnimeTrackingState animeFilterState,
|
MediumTrackingState animeFilterState,
|
||||||
MangaTrackingState mangaFilterState,
|
MediumTrackingState mangaFilterState,
|
||||||
TrackingMediumType trackingType});
|
TrackingMediumType trackingType});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,11 +71,11 @@ class _$AnimeListStateCopyWithImpl<$Res>
|
|||||||
animeFilterState: animeFilterState == freezed
|
animeFilterState: animeFilterState == freezed
|
||||||
? _value.animeFilterState
|
? _value.animeFilterState
|
||||||
: animeFilterState // ignore: cast_nullable_to_non_nullable
|
: animeFilterState // ignore: cast_nullable_to_non_nullable
|
||||||
as AnimeTrackingState,
|
as MediumTrackingState,
|
||||||
mangaFilterState: mangaFilterState == freezed
|
mangaFilterState: mangaFilterState == freezed
|
||||||
? _value.mangaFilterState
|
? _value.mangaFilterState
|
||||||
: mangaFilterState // ignore: cast_nullable_to_non_nullable
|
: mangaFilterState // ignore: cast_nullable_to_non_nullable
|
||||||
as MangaTrackingState,
|
as MediumTrackingState,
|
||||||
trackingType: trackingType == freezed
|
trackingType: trackingType == freezed
|
||||||
? _value.trackingType
|
? _value.trackingType
|
||||||
: trackingType // ignore: cast_nullable_to_non_nullable
|
: trackingType // ignore: cast_nullable_to_non_nullable
|
||||||
@ -92,8 +94,8 @@ abstract class _$$_AnimeListStateCopyWith<$Res>
|
|||||||
$Res call(
|
$Res call(
|
||||||
{List<AnimeTrackingData> animes,
|
{List<AnimeTrackingData> animes,
|
||||||
List<MangaTrackingData> mangas,
|
List<MangaTrackingData> mangas,
|
||||||
AnimeTrackingState animeFilterState,
|
MediumTrackingState animeFilterState,
|
||||||
MangaTrackingState mangaFilterState,
|
MediumTrackingState mangaFilterState,
|
||||||
TrackingMediumType trackingType});
|
TrackingMediumType trackingType});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,11 +130,11 @@ class __$$_AnimeListStateCopyWithImpl<$Res>
|
|||||||
animeFilterState: animeFilterState == freezed
|
animeFilterState: animeFilterState == freezed
|
||||||
? _value.animeFilterState
|
? _value.animeFilterState
|
||||||
: animeFilterState // ignore: cast_nullable_to_non_nullable
|
: animeFilterState // ignore: cast_nullable_to_non_nullable
|
||||||
as AnimeTrackingState,
|
as MediumTrackingState,
|
||||||
mangaFilterState: mangaFilterState == freezed
|
mangaFilterState: mangaFilterState == freezed
|
||||||
? _value.mangaFilterState
|
? _value.mangaFilterState
|
||||||
: mangaFilterState // ignore: cast_nullable_to_non_nullable
|
: mangaFilterState // ignore: cast_nullable_to_non_nullable
|
||||||
as MangaTrackingState,
|
as MediumTrackingState,
|
||||||
trackingType: trackingType == freezed
|
trackingType: trackingType == freezed
|
||||||
? _value.trackingType
|
? _value.trackingType
|
||||||
: trackingType // ignore: cast_nullable_to_non_nullable
|
: trackingType // ignore: cast_nullable_to_non_nullable
|
||||||
@ -147,8 +149,8 @@ class _$_AnimeListState implements _AnimeListState {
|
|||||||
_$_AnimeListState(
|
_$_AnimeListState(
|
||||||
{final List<AnimeTrackingData> animes = const [],
|
{final List<AnimeTrackingData> animes = const [],
|
||||||
final List<MangaTrackingData> mangas = const [],
|
final List<MangaTrackingData> mangas = const [],
|
||||||
this.animeFilterState = AnimeTrackingState.watching,
|
this.animeFilterState = MediumTrackingState.ongoing,
|
||||||
this.mangaFilterState = MangaTrackingState.reading,
|
this.mangaFilterState = MediumTrackingState.ongoing,
|
||||||
this.trackingType = TrackingMediumType.anime})
|
this.trackingType = TrackingMediumType.anime})
|
||||||
: _animes = animes,
|
: _animes = animes,
|
||||||
_mangas = mangas;
|
_mangas = mangas;
|
||||||
@ -171,10 +173,10 @@ class _$_AnimeListState implements _AnimeListState {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
@JsonKey()
|
@JsonKey()
|
||||||
final AnimeTrackingState animeFilterState;
|
final MediumTrackingState animeFilterState;
|
||||||
@override
|
@override
|
||||||
@JsonKey()
|
@JsonKey()
|
||||||
final MangaTrackingState mangaFilterState;
|
final MediumTrackingState mangaFilterState;
|
||||||
@override
|
@override
|
||||||
@JsonKey()
|
@JsonKey()
|
||||||
final TrackingMediumType trackingType;
|
final TrackingMediumType trackingType;
|
||||||
@ -218,8 +220,8 @@ abstract class _AnimeListState implements AnimeListState {
|
|||||||
factory _AnimeListState(
|
factory _AnimeListState(
|
||||||
{final List<AnimeTrackingData> animes,
|
{final List<AnimeTrackingData> animes,
|
||||||
final List<MangaTrackingData> mangas,
|
final List<MangaTrackingData> mangas,
|
||||||
final AnimeTrackingState animeFilterState,
|
final MediumTrackingState animeFilterState,
|
||||||
final MangaTrackingState mangaFilterState,
|
final MediumTrackingState mangaFilterState,
|
||||||
final TrackingMediumType trackingType}) = _$_AnimeListState;
|
final TrackingMediumType trackingType}) = _$_AnimeListState;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -227,9 +229,9 @@ abstract class _AnimeListState implements AnimeListState {
|
|||||||
@override
|
@override
|
||||||
List<MangaTrackingData> get mangas;
|
List<MangaTrackingData> get mangas;
|
||||||
@override
|
@override
|
||||||
AnimeTrackingState get animeFilterState;
|
MediumTrackingState get animeFilterState;
|
||||||
@override
|
@override
|
||||||
MangaTrackingState get mangaFilterState;
|
MediumTrackingState get mangaFilterState;
|
||||||
@override
|
@override
|
||||||
TrackingMediumType get trackingType;
|
TrackingMediumType get trackingType;
|
||||||
@override
|
@override
|
||||||
|
@ -31,7 +31,7 @@ class AnimeFilterChangedEvent extends AnimeListEvent {
|
|||||||
AnimeFilterChangedEvent(this.filterState);
|
AnimeFilterChangedEvent(this.filterState);
|
||||||
|
|
||||||
/// The state to filter
|
/// The state to filter
|
||||||
final AnimeTrackingState filterState;
|
final MediumTrackingState filterState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Triggered when the view is changed from the anime or the manga view
|
/// Triggered when the view is changed from the anime or the manga view
|
||||||
@ -60,7 +60,7 @@ class MangaFilterChangedEvent extends AnimeListEvent {
|
|||||||
MangaFilterChangedEvent(this.filterState);
|
MangaFilterChangedEvent(this.filterState);
|
||||||
|
|
||||||
/// The state to filter
|
/// The state to filter
|
||||||
final MangaTrackingState filterState;
|
final MediumTrackingState filterState;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MangaChapterIncrementedEvent extends AnimeListEvent {
|
class MangaChapterIncrementedEvent extends AnimeListEvent {
|
||||||
|
@ -5,8 +5,8 @@ class AnimeListState with _$AnimeListState {
|
|||||||
factory AnimeListState({
|
factory AnimeListState({
|
||||||
@Default([]) List<AnimeTrackingData> animes,
|
@Default([]) List<AnimeTrackingData> animes,
|
||||||
@Default([]) List<MangaTrackingData> mangas,
|
@Default([]) List<MangaTrackingData> mangas,
|
||||||
@Default(AnimeTrackingState.watching) AnimeTrackingState animeFilterState,
|
@Default(MediumTrackingState.ongoing) MediumTrackingState animeFilterState,
|
||||||
@Default(MangaTrackingState.reading) MangaTrackingState mangaFilterState,
|
@Default(MediumTrackingState.ongoing) MediumTrackingState mangaFilterState,
|
||||||
@Default(TrackingMediumType.anime) TrackingMediumType trackingType,
|
@Default(TrackingMediumType.anime) TrackingMediumType trackingType,
|
||||||
}) = _AnimeListState;
|
}) = _AnimeListState;
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,9 @@ import 'package:anitrack/src/data/anime.dart';
|
|||||||
import 'package:anitrack/src/data/manga.dart';
|
import 'package:anitrack/src/data/manga.dart';
|
||||||
import 'package:anitrack/src/data/search_result.dart';
|
import 'package:anitrack/src/data/search_result.dart';
|
||||||
import 'package:anitrack/src/data/type.dart';
|
import 'package:anitrack/src/data/type.dart';
|
||||||
import 'package:anitrack/src/ui/constants.dart';
|
|
||||||
import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart' as list;
|
import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart' as list;
|
||||||
import 'package:anitrack/src/ui/bloc/navigation_bloc.dart';
|
import 'package:anitrack/src/ui/bloc/navigation_bloc.dart';
|
||||||
|
import 'package:anitrack/src/ui/constants.dart';
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
@ -34,7 +34,7 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
|
|||||||
|
|
||||||
GetIt.I.get<NavigationBloc>().add(
|
GetIt.I.get<NavigationBloc>().add(
|
||||||
PushedNamedEvent(
|
PushedNamedEvent(
|
||||||
NavigationDestination(animeSearchRoute),
|
const NavigationDestination(animeSearchRoute),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -101,7 +101,7 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
|
|||||||
list.AnimeAddedEvent(
|
list.AnimeAddedEvent(
|
||||||
AnimeTrackingData(
|
AnimeTrackingData(
|
||||||
event.result.id,
|
event.result.id,
|
||||||
AnimeTrackingState.watching,
|
MediumTrackingState.ongoing,
|
||||||
event.result.title,
|
event.result.title,
|
||||||
0,
|
0,
|
||||||
event.result.total,
|
event.result.total,
|
||||||
@ -111,14 +111,14 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
|
|||||||
list.MangaAddedEvent(
|
list.MangaAddedEvent(
|
||||||
MangaTrackingData(
|
MangaTrackingData(
|
||||||
event.result.id,
|
event.result.id,
|
||||||
MangaTrackingState.reading,
|
MediumTrackingState.ongoing,
|
||||||
event.result.title,
|
event.result.title,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
event.result.total,
|
event.result.total,
|
||||||
event.result.thumbnailUrl,
|
event.result.thumbnailUrl,
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
GetIt.I.get<NavigationBloc>().add(
|
GetIt.I.get<NavigationBloc>().add(
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import 'package:anitrack/src/data/anime.dart';
|
import 'package:anitrack/src/data/anime.dart';
|
||||||
import 'package:anitrack/src/data/manga.dart';
|
import 'package:anitrack/src/data/manga.dart';
|
||||||
import 'package:anitrack/src/data/type.dart';
|
import 'package:anitrack/src/data/type.dart';
|
||||||
|
import 'package:anitrack/src/service/database.dart';
|
||||||
import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart';
|
import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart';
|
||||||
import 'package:anitrack/src/ui/bloc/navigation_bloc.dart';
|
import 'package:anitrack/src/ui/bloc/navigation_bloc.dart';
|
||||||
import 'package:anitrack/src/ui/constants.dart';
|
import 'package:anitrack/src/ui/constants.dart';
|
||||||
import 'package:anitrack/src/service/database.dart';
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
@ -30,9 +30,7 @@ class DetailsBloc extends Bloc<DetailsEvent, DetailsState> {
|
|||||||
|
|
||||||
GetIt.I.get<NavigationBloc>().add(
|
GetIt.I.get<NavigationBloc>().add(
|
||||||
PushedNamedEvent(
|
PushedNamedEvent(
|
||||||
NavigationDestination(
|
const NavigationDestination(detailsRoute),
|
||||||
detailsRoute,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -47,9 +45,7 @@ class DetailsBloc extends Bloc<DetailsEvent, DetailsState> {
|
|||||||
|
|
||||||
GetIt.I.get<NavigationBloc>().add(
|
GetIt.I.get<NavigationBloc>().add(
|
||||||
PushedNamedEvent(
|
PushedNamedEvent(
|
||||||
NavigationDestination(
|
const NavigationDestination(detailsRoute),
|
||||||
detailsRoute,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
||||||
|
|
||||||
part 'navigation_event.dart';
|
part 'navigation_event.dart';
|
||||||
part 'navigation_state.dart';
|
part 'navigation_state.dart';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import 'package:anitrack/src/data/anime.dart';
|
|
||||||
import 'package:anitrack/src/data/manga.dart';
|
|
||||||
import 'package:anitrack/src/data/type.dart';
|
import 'package:anitrack/src/data/type.dart';
|
||||||
import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart';
|
import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart';
|
||||||
import 'package:anitrack/src/ui/bloc/anime_search_bloc.dart';
|
import 'package:anitrack/src/ui/bloc/anime_search_bloc.dart';
|
||||||
@ -11,6 +9,10 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class AnimeListPage extends StatelessWidget {
|
class AnimeListPage extends StatelessWidget {
|
||||||
|
AnimeListPage({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
static MaterialPageRoute<dynamic> get route => MaterialPageRoute<dynamic>(
|
static MaterialPageRoute<dynamic> get route => MaterialPageRoute<dynamic>(
|
||||||
builder: (_) => AnimeListPage(),
|
builder: (_) => AnimeListPage(),
|
||||||
settings: const RouteSettings(
|
settings: const RouteSettings(
|
||||||
@ -27,11 +29,36 @@ class AnimeListPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<PopupMenuItem<MediumTrackingState>> _getPopupButtonItems(TrackingMediumType type) {
|
||||||
|
return [
|
||||||
|
PopupMenuItem<MediumTrackingState>(
|
||||||
|
value: MediumTrackingState.ongoing,
|
||||||
|
child: Text(MediumTrackingState.ongoing.toNameString(type)),
|
||||||
|
),
|
||||||
|
PopupMenuItem<MediumTrackingState>(
|
||||||
|
value: MediumTrackingState.completed,
|
||||||
|
child: Text(MediumTrackingState.completed.toNameString(type)),
|
||||||
|
),
|
||||||
|
PopupMenuItem<MediumTrackingState>(
|
||||||
|
value: MediumTrackingState.planned,
|
||||||
|
child: Text(MediumTrackingState.planned.toNameString(type)),
|
||||||
|
),
|
||||||
|
PopupMenuItem<MediumTrackingState>(
|
||||||
|
value: MediumTrackingState.dropped,
|
||||||
|
child: Text(MediumTrackingState.dropped.toNameString(type)),
|
||||||
|
),
|
||||||
|
const PopupMenuItem<MediumTrackingState>(
|
||||||
|
value: MediumTrackingState.all,
|
||||||
|
child: Text('All'),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
Widget _getPopupButton(BuildContext context, AnimeListState state) {
|
Widget _getPopupButton(BuildContext context, AnimeListState state) {
|
||||||
switch (state.trackingType) {
|
switch (state.trackingType) {
|
||||||
case TrackingMediumType.anime:
|
case TrackingMediumType.anime:
|
||||||
return PopupMenuButton(
|
return PopupMenuButton(
|
||||||
icon: Icon(
|
icon: const Icon(
|
||||||
Icons.filter_list,
|
Icons.filter_list,
|
||||||
),
|
),
|
||||||
initialValue: state.animeFilterState,
|
initialValue: state.animeFilterState,
|
||||||
@ -40,32 +67,11 @@ class AnimeListPage extends StatelessWidget {
|
|||||||
AnimeFilterChangedEvent(filterState),
|
AnimeFilterChangedEvent(filterState),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
itemBuilder: (_) => [
|
itemBuilder: (_) => _getPopupButtonItems(TrackingMediumType.anime),
|
||||||
const PopupMenuItem<AnimeTrackingState>(
|
|
||||||
value: AnimeTrackingState.watching,
|
|
||||||
child: Text('Watching'),
|
|
||||||
),
|
|
||||||
const PopupMenuItem<AnimeTrackingState>(
|
|
||||||
value: AnimeTrackingState.completed,
|
|
||||||
child: Text('Completed'),
|
|
||||||
),
|
|
||||||
const PopupMenuItem<AnimeTrackingState>(
|
|
||||||
value: AnimeTrackingState.planToWatch,
|
|
||||||
child: Text('Plan to watch'),
|
|
||||||
),
|
|
||||||
const PopupMenuItem<AnimeTrackingState>(
|
|
||||||
value: AnimeTrackingState.dropped,
|
|
||||||
child: Text('Dropped'),
|
|
||||||
),
|
|
||||||
const PopupMenuItem<AnimeTrackingState>(
|
|
||||||
value: AnimeTrackingState.all,
|
|
||||||
child: Text('All'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
case TrackingMediumType.manga:
|
case TrackingMediumType.manga:
|
||||||
return PopupMenuButton(
|
return PopupMenuButton(
|
||||||
icon: Icon(
|
icon: const Icon(
|
||||||
Icons.filter_list,
|
Icons.filter_list,
|
||||||
),
|
),
|
||||||
initialValue: state.mangaFilterState,
|
initialValue: state.mangaFilterState,
|
||||||
@ -74,28 +80,7 @@ class AnimeListPage extends StatelessWidget {
|
|||||||
MangaFilterChangedEvent(filterState),
|
MangaFilterChangedEvent(filterState),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
itemBuilder: (_) => [
|
itemBuilder: (_) => _getPopupButtonItems(TrackingMediumType.manga),
|
||||||
const PopupMenuItem<MangaTrackingState>(
|
|
||||||
value: MangaTrackingState.reading,
|
|
||||||
child: Text('Reading'),
|
|
||||||
),
|
|
||||||
const PopupMenuItem<MangaTrackingState>(
|
|
||||||
value: MangaTrackingState.completed,
|
|
||||||
child: Text('Completed'),
|
|
||||||
),
|
|
||||||
const PopupMenuItem<MangaTrackingState>(
|
|
||||||
value: MangaTrackingState.planToRead,
|
|
||||||
child: Text('Plan to read'),
|
|
||||||
),
|
|
||||||
const PopupMenuItem<MangaTrackingState>(
|
|
||||||
value: MangaTrackingState.dropped,
|
|
||||||
child: Text('Dropped'),
|
|
||||||
),
|
|
||||||
const PopupMenuItem<MangaTrackingState>(
|
|
||||||
value: MangaTrackingState.all,
|
|
||||||
child: Text('All'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,7 +92,7 @@ class AnimeListPage extends StatelessWidget {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
_getPageTitle(state.trackingType)
|
_getPageTitle(state.trackingType),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
_getPopupButton(context, state),
|
_getPopupButton(context, state),
|
||||||
@ -120,7 +105,7 @@ class AnimeListPage extends StatelessWidget {
|
|||||||
itemCount: state.animes.length,
|
itemCount: state.animes.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final anime = state.animes[index];
|
final anime = state.animes[index];
|
||||||
if (state.animeFilterState != AnimeTrackingState.all) {
|
if (state.animeFilterState != MediumTrackingState.all) {
|
||||||
if (anime.state != state.animeFilterState) return Container();
|
if (anime.state != state.animeFilterState) return Container();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +142,7 @@ class AnimeListPage extends StatelessWidget {
|
|||||||
itemCount: state.mangas.length,
|
itemCount: state.mangas.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final manga = state.mangas[index];
|
final manga = state.mangas[index];
|
||||||
if (state.mangaFilterState != MangaTrackingState.all) {
|
if (state.mangaFilterState != MediumTrackingState.all) {
|
||||||
if (manga.state != state.mangaFilterState) return Container();
|
if (manga.state != state.mangaFilterState) return Container();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +201,7 @@ class AnimeListPage extends StatelessWidget {
|
|||||||
|
|
||||||
_controller.jumpToPage(index);
|
_controller.jumpToPage(index);
|
||||||
},
|
},
|
||||||
items: <BottomBarItem>[
|
items: const [
|
||||||
BottomBarItem(
|
BottomBarItem(
|
||||||
icon: Icon(Icons.tv),
|
icon: Icon(Icons.tv),
|
||||||
title: Text('Anime'),
|
title: Text('Anime'),
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:anitrack/src/data/anime.dart';
|
|
||||||
import 'package:anitrack/src/data/type.dart';
|
import 'package:anitrack/src/data/type.dart';
|
||||||
import 'package:anitrack/src/ui/bloc/anime_search_bloc.dart';
|
import 'package:anitrack/src/ui/bloc/anime_search_bloc.dart';
|
||||||
import 'package:anitrack/src/ui/constants.dart';
|
import 'package:anitrack/src/ui/constants.dart';
|
||||||
import 'package:anitrack/src/ui/widgets/list_item.dart';
|
import 'package:anitrack/src/ui/widgets/list_item.dart';
|
||||||
import 'package:anitrack/src/ui/widgets/image.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class AnimeSearchPage extends StatelessWidget {
|
class AnimeSearchPage extends StatelessWidget {
|
||||||
|
const AnimeSearchPage({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
static MaterialPageRoute<dynamic> get route => MaterialPageRoute<dynamic>(
|
static MaterialPageRoute<dynamic> get route => MaterialPageRoute<dynamic>(
|
||||||
builder: (_) => AnimeSearchPage(),
|
builder: (_) => const AnimeSearchPage(),
|
||||||
settings: const RouteSettings(
|
settings: const RouteSettings(
|
||||||
name: animeSearchRoute,
|
name: animeSearchRoute,
|
||||||
),
|
),
|
||||||
@ -32,7 +34,7 @@ class AnimeSearchPage extends StatelessWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
decoration: InputDecoration(
|
decoration: const InputDecoration(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
labelText: 'Search query',
|
labelText: 'Search query',
|
||||||
),
|
),
|
||||||
@ -50,9 +52,8 @@ class AnimeSearchPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
|
|
||||||
if (state.working)
|
if (state.working)
|
||||||
Expanded(
|
const Expanded(
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.center,
|
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -86,7 +87,7 @@ class AnimeSearchPage extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -3,44 +3,28 @@ import 'package:anitrack/src/data/manga.dart';
|
|||||||
import 'package:anitrack/src/data/type.dart';
|
import 'package:anitrack/src/data/type.dart';
|
||||||
import 'package:anitrack/src/ui/bloc/details_bloc.dart';
|
import 'package:anitrack/src/ui/bloc/details_bloc.dart';
|
||||||
import 'package:anitrack/src/ui/constants.dart';
|
import 'package:anitrack/src/ui/constants.dart';
|
||||||
|
import 'package:anitrack/src/ui/widgets/dropdown.dart';
|
||||||
import 'package:anitrack/src/ui/widgets/image.dart';
|
import 'package:anitrack/src/ui/widgets/image.dart';
|
||||||
import 'package:anitrack/src/ui/widgets/list_item.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
Widget _makeListTile(BuildContext context, dynamic value, String text, bool selected) {
|
|
||||||
return ListTile(
|
|
||||||
title: Text(text),
|
|
||||||
trailing: selected ?
|
|
||||||
Icon(Icons.check) :
|
|
||||||
null,
|
|
||||||
onTap: () {
|
|
||||||
Navigator.of(context).pop(value);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class DetailsPage extends StatelessWidget {
|
class DetailsPage extends StatelessWidget {
|
||||||
|
const DetailsPage({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
static MaterialPageRoute<dynamic> get route => MaterialPageRoute<dynamic>(
|
static MaterialPageRoute<dynamic> get route => MaterialPageRoute<dynamic>(
|
||||||
builder: (_) => DetailsPage(),
|
builder: (_) => const DetailsPage(),
|
||||||
settings: const RouteSettings(
|
settings: const RouteSettings(
|
||||||
name: detailsRoute,
|
name: detailsRoute,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
String _getTrackingStateText(DetailsState state) {
|
|
||||||
if (state.trackingType == TrackingMediumType.anime) {
|
|
||||||
return (state.data as AnimeTrackingData).state.toNameString();
|
|
||||||
} else {
|
|
||||||
return (state.data as MangaTrackingData).state.toNameString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Details'),
|
title: const Text('Details'),
|
||||||
),
|
),
|
||||||
body: BlocBuilder<DetailsBloc, DetailsState>(
|
body: BlocBuilder<DetailsBloc, DetailsState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
@ -69,101 +53,63 @@ class DetailsPage extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
state.trackingType == TrackingMediumType.anime ?
|
state.trackingType == TrackingMediumType.anime ?
|
||||||
(state.data as AnimeTrackingData).title :
|
(state.data as AnimeTrackingData).title :
|
||||||
(state.data as MangaTrackingData).title,
|
(state.data as MangaTrackingData).title,
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
|
||||||
TextButton(
|
|
||||||
child: Text(
|
|
||||||
_getTrackingStateText(state),
|
|
||||||
),
|
|
||||||
onPressed: () async {
|
|
||||||
final result = await showModalBottomSheet<dynamic>(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return ListView(
|
|
||||||
shrinkWrap: true,
|
|
||||||
children: [
|
|
||||||
_makeListTile(
|
|
||||||
context,
|
|
||||||
state.trackingType == TrackingMediumType.anime ?
|
|
||||||
AnimeTrackingState.watching :
|
|
||||||
MangaTrackingState.reading,
|
|
||||||
state.trackingType == TrackingMediumType.anime ?
|
|
||||||
'Watching' :
|
|
||||||
'Reading',
|
|
||||||
state.trackingType == TrackingMediumType.anime ?
|
|
||||||
(state.data as AnimeTrackingData).state == AnimeTrackingState.watching :
|
|
||||||
(state.data as MangaTrackingData).state == MangaTrackingState.reading,
|
|
||||||
),
|
|
||||||
_makeListTile(
|
|
||||||
context,
|
|
||||||
state.trackingType == TrackingMediumType.anime ?
|
|
||||||
AnimeTrackingState.completed :
|
|
||||||
MangaTrackingState.completed,
|
|
||||||
'Completed',
|
|
||||||
state.trackingType == TrackingMediumType.anime ?
|
|
||||||
(state.data as AnimeTrackingData).state == AnimeTrackingState.completed :
|
|
||||||
(state.data as MangaTrackingData).state == MangaTrackingState.completed,
|
|
||||||
),
|
|
||||||
_makeListTile(
|
|
||||||
context,
|
|
||||||
state.trackingType == TrackingMediumType.anime ?
|
|
||||||
AnimeTrackingState.planToWatch :
|
|
||||||
MangaTrackingState.planToRead,
|
|
||||||
state.trackingType == TrackingMediumType.anime ?
|
|
||||||
'Plan to watch' :
|
|
||||||
'Plan to read',
|
|
||||||
state.trackingType == TrackingMediumType.anime ?
|
|
||||||
(state.data as AnimeTrackingData).state == AnimeTrackingState.planToWatch :
|
|
||||||
(state.data as MangaTrackingData).state == MangaTrackingState.planToRead,
|
|
||||||
),
|
|
||||||
_makeListTile(
|
|
||||||
context,
|
|
||||||
state.trackingType == TrackingMediumType.anime ?
|
|
||||||
AnimeTrackingState.dropped :
|
|
||||||
MangaTrackingState.dropped,
|
|
||||||
'Dropped',
|
|
||||||
state.trackingType == TrackingMediumType.anime ?
|
|
||||||
(state.data as AnimeTrackingData).state == AnimeTrackingState.dropped :
|
|
||||||
(state.data as MangaTrackingData).state == MangaTrackingState.dropped,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result == null) return;
|
|
||||||
|
|
||||||
if (state.trackingType == TrackingMediumType.anime) {
|
|
||||||
context.read<DetailsBloc>().add(
|
|
||||||
DetailsUpdatedEvent(
|
|
||||||
(state.data as AnimeTrackingData).copyWith(
|
|
||||||
state: result as AnimeTrackingState,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
context.read<DetailsBloc>().add(
|
|
||||||
DetailsUpdatedEvent(
|
|
||||||
(state.data as MangaTrackingData).copyWith(
|
|
||||||
state: result as MangaTrackingState,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
|
DropdownSelector<MediumTrackingState>(
|
||||||
|
title: 'Watchstate',
|
||||||
|
onChanged: (MediumTrackingState newState) {
|
||||||
|
if (state.trackingType == TrackingMediumType.anime) {
|
||||||
|
context.read<DetailsBloc>().add(
|
||||||
|
DetailsUpdatedEvent(
|
||||||
|
(state.data as AnimeTrackingData).copyWith(
|
||||||
|
state: newState,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (state.trackingType == TrackingMediumType.manga) {
|
||||||
|
context.read<DetailsBloc>().add(
|
||||||
|
DetailsUpdatedEvent(
|
||||||
|
(state.data as MangaTrackingData).copyWith(
|
||||||
|
state: newState,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
values: [
|
||||||
|
SelectorItem(
|
||||||
|
MediumTrackingState.ongoing,
|
||||||
|
MediumTrackingState.ongoing.toNameString(state.trackingType),
|
||||||
|
),
|
||||||
|
SelectorItem(
|
||||||
|
MediumTrackingState.completed,
|
||||||
|
MediumTrackingState.completed.toNameString(state.trackingType),
|
||||||
|
),
|
||||||
|
SelectorItem(
|
||||||
|
MediumTrackingState.planned,
|
||||||
|
MediumTrackingState.planned.toNameString(state.trackingType),
|
||||||
|
),
|
||||||
|
SelectorItem(
|
||||||
|
MediumTrackingState.dropped,
|
||||||
|
MediumTrackingState.dropped.toNameString(state.trackingType),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
initialValue: state.trackingType == TrackingMediumType.anime ?
|
||||||
|
(state.data as AnimeTrackingData).state :
|
||||||
|
(state.data as MangaTrackingData).state,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
129
lib/src/ui/widgets/dropdown.dart
Normal file
129
lib/src/ui/widgets/dropdown.dart
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SelectorItem<T> {
|
||||||
|
const SelectorItem(this.value, this.text);
|
||||||
|
|
||||||
|
final T value;
|
||||||
|
|
||||||
|
final String text;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DropdownSelector<T> extends StatefulWidget {
|
||||||
|
const DropdownSelector({
|
||||||
|
required this.title,
|
||||||
|
required this.onChanged,
|
||||||
|
required this.values,
|
||||||
|
required this.initialValue,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The title of the dropdown
|
||||||
|
final String title;
|
||||||
|
|
||||||
|
final List<SelectorItem<T>> values;
|
||||||
|
|
||||||
|
final T initialValue;
|
||||||
|
|
||||||
|
/// Called when the selection has changed
|
||||||
|
final void Function(T) onChanged;
|
||||||
|
|
||||||
|
@override
|
||||||
|
DropdownSelectorState<T> createState() => DropdownSelectorState<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class DropdownSelectorDialog<T> extends StatelessWidget {
|
||||||
|
const DropdownSelectorDialog({
|
||||||
|
required this.values,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<SelectorItem<T>> values;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SafeArea(
|
||||||
|
child: Material(
|
||||||
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: values.length,
|
||||||
|
itemBuilder: (context, index) => InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop(values[index].value);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Text(
|
||||||
|
values[index].text,
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DropdownSelectorState<T> extends State<DropdownSelector<T>> {
|
||||||
|
late int index;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
index = widget.values.indexWhere((item) => item.value == widget.initialValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Card(
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
final result = await showDialog<T>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => DropdownSelectorDialog<T>(
|
||||||
|
values: widget.values.cast<SelectorItem<T>>(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == null) return;
|
||||||
|
if (result == widget.values[index].value) return;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
index = widget.values.indexWhere((item) => item.value == result);
|
||||||
|
});
|
||||||
|
|
||||||
|
widget.onChanged(result);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
widget.title,
|
||||||
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
widget.values[index].text,
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ class AnimeCoverImage extends StatelessWidget {
|
|||||||
/// The URL to the cover image.
|
/// The URL to the cover image.
|
||||||
final String url;
|
final String url;
|
||||||
|
|
||||||
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ClipRRect(
|
return ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import 'package:anitrack/src/data/anime.dart';
|
|
||||||
import 'package:anitrack/src/ui/widgets/image.dart';
|
import 'package:anitrack/src/ui/widgets/image.dart';
|
||||||
import 'package:anitrack/src/ui/widgets/swipe_icon.dart';
|
import 'package:anitrack/src/ui/widgets/swipe_icon.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -6,7 +5,7 @@ import 'package:swipeable_tile/swipeable_tile.dart';
|
|||||||
|
|
||||||
/// A widget for displaying simple data about an anime in the listview
|
/// A widget for displaying simple data about an anime in the listview
|
||||||
class ListItem extends StatelessWidget {
|
class ListItem extends StatelessWidget {
|
||||||
ListItem({
|
const ListItem({
|
||||||
required this.thumbnailUrl,
|
required this.thumbnailUrl,
|
||||||
required this.title,
|
required this.title,
|
||||||
this.onLeftSwipe,
|
this.onLeftSwipe,
|
||||||
@ -42,14 +41,14 @@ class ListItem extends StatelessWidget {
|
|||||||
key: UniqueKey(),
|
key: UniqueKey(),
|
||||||
backgroundBuilder: (_, direction, __) {
|
backgroundBuilder: (_, direction, __) {
|
||||||
if (direction == SwipeDirection.endToStart) {
|
if (direction == SwipeDirection.endToStart) {
|
||||||
return Align(
|
return const Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: SwipeIcon(
|
child: SwipeIcon(
|
||||||
Icons.add,
|
Icons.add,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (direction == SwipeDirection.startToEnd) {
|
} else if (direction == SwipeDirection.startToEnd) {
|
||||||
return Align(
|
return const Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: SwipeIcon(
|
child: SwipeIcon(
|
||||||
Icons.remove,
|
Icons.remove,
|
||||||
|
@ -576,6 +576,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
very_good_analysis:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: very_good_analysis
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -23,12 +23,13 @@ dependencies:
|
|||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.1.11
|
build_runner: ^2.1.11
|
||||||
flutter_test:
|
|
||||||
sdk: flutter
|
|
||||||
flutter_launcher_icons: ^0.11.0
|
flutter_launcher_icons: ^0.11.0
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
freezed: ^2.1.0+1
|
freezed: ^2.1.0+1
|
||||||
json_serializable: ^6.3.1
|
json_serializable: ^6.3.1
|
||||||
|
very_good_analysis: ^3.0.1
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
Loading…
Reference in New Issue
Block a user