feat(ui): Stub out a manga tracking page
This commit is contained in:
parent
d273a6deb2
commit
a60e9e8e81
5
lib/src/data/type.dart
Normal file
5
lib/src/data/type.dart
Normal file
@ -0,0 +1,5 @@
|
||||
/// The type of medium we are tracking. Useful for UI stuff.
|
||||
enum TrackingMediumType {
|
||||
anime,
|
||||
manga,
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import 'dart:math';
|
||||
import 'package:anitrack/src/data/anime.dart';
|
||||
import 'package:anitrack/src/data/type.dart';
|
||||
import 'package:anitrack/src/service/database.dart';
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
@ -16,6 +17,7 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
||||
on<AnimeEpisodeDecrementedEvent>(_onDecremented);
|
||||
on<AnimesLoadedEvent>(_onAnimesLoaded);
|
||||
on<AnimeFilterChangedEvent>(_onAnimesFiltered);
|
||||
on<AnimeTrackingTypeChanged>(_onTrackingTypeChanged);
|
||||
}
|
||||
|
||||
Future<void> _onAnimeAdded(AnimeAddedEvent event, Emitter<AnimeListState> emit) async {
|
||||
@ -91,4 +93,12 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onTrackingTypeChanged(AnimeTrackingTypeChanged event, Emitter<AnimeListState> emit) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
trackingType: event.type,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ final _privateConstructorUsedError = UnsupportedError(
|
||||
mixin _$AnimeListState {
|
||||
List<AnimeTrackingData> get animes => throw _privateConstructorUsedError;
|
||||
AnimeTrackingState get filterState => throw _privateConstructorUsedError;
|
||||
TrackingMediumType get trackingType => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$AnimeListStateCopyWith<AnimeListState> get copyWith =>
|
||||
@ -29,7 +30,10 @@ abstract class $AnimeListStateCopyWith<$Res> {
|
||||
factory $AnimeListStateCopyWith(
|
||||
AnimeListState value, $Res Function(AnimeListState) then) =
|
||||
_$AnimeListStateCopyWithImpl<$Res>;
|
||||
$Res call({List<AnimeTrackingData> animes, AnimeTrackingState filterState});
|
||||
$Res call(
|
||||
{List<AnimeTrackingData> animes,
|
||||
AnimeTrackingState filterState,
|
||||
TrackingMediumType trackingType});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -45,6 +49,7 @@ class _$AnimeListStateCopyWithImpl<$Res>
|
||||
$Res call({
|
||||
Object? animes = freezed,
|
||||
Object? filterState = freezed,
|
||||
Object? trackingType = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
animes: animes == freezed
|
||||
@ -55,6 +60,10 @@ class _$AnimeListStateCopyWithImpl<$Res>
|
||||
? _value.filterState
|
||||
: filterState // ignore: cast_nullable_to_non_nullable
|
||||
as AnimeTrackingState,
|
||||
trackingType: trackingType == freezed
|
||||
? _value.trackingType
|
||||
: trackingType // ignore: cast_nullable_to_non_nullable
|
||||
as TrackingMediumType,
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -66,7 +75,10 @@ abstract class _$$_AnimeListStateCopyWith<$Res>
|
||||
_$_AnimeListState value, $Res Function(_$_AnimeListState) then) =
|
||||
__$$_AnimeListStateCopyWithImpl<$Res>;
|
||||
@override
|
||||
$Res call({List<AnimeTrackingData> animes, AnimeTrackingState filterState});
|
||||
$Res call(
|
||||
{List<AnimeTrackingData> animes,
|
||||
AnimeTrackingState filterState,
|
||||
TrackingMediumType trackingType});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -84,6 +96,7 @@ class __$$_AnimeListStateCopyWithImpl<$Res>
|
||||
$Res call({
|
||||
Object? animes = freezed,
|
||||
Object? filterState = freezed,
|
||||
Object? trackingType = freezed,
|
||||
}) {
|
||||
return _then(_$_AnimeListState(
|
||||
animes: animes == freezed
|
||||
@ -94,6 +107,10 @@ class __$$_AnimeListStateCopyWithImpl<$Res>
|
||||
? _value.filterState
|
||||
: filterState // ignore: cast_nullable_to_non_nullable
|
||||
as AnimeTrackingState,
|
||||
trackingType: trackingType == freezed
|
||||
? _value.trackingType
|
||||
: trackingType // ignore: cast_nullable_to_non_nullable
|
||||
as TrackingMediumType,
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -103,7 +120,8 @@ class __$$_AnimeListStateCopyWithImpl<$Res>
|
||||
class _$_AnimeListState implements _AnimeListState {
|
||||
_$_AnimeListState(
|
||||
{final List<AnimeTrackingData> animes = const [],
|
||||
this.filterState = AnimeTrackingState.watching})
|
||||
this.filterState = AnimeTrackingState.watching,
|
||||
this.trackingType = TrackingMediumType.anime})
|
||||
: _animes = animes;
|
||||
|
||||
final List<AnimeTrackingData> _animes;
|
||||
@ -117,10 +135,13 @@ class _$_AnimeListState implements _AnimeListState {
|
||||
@override
|
||||
@JsonKey()
|
||||
final AnimeTrackingState filterState;
|
||||
@override
|
||||
@JsonKey()
|
||||
final TrackingMediumType trackingType;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AnimeListState(animes: $animes, filterState: $filterState)';
|
||||
return 'AnimeListState(animes: $animes, filterState: $filterState, trackingType: $trackingType)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -130,14 +151,17 @@ class _$_AnimeListState implements _AnimeListState {
|
||||
other is _$_AnimeListState &&
|
||||
const DeepCollectionEquality().equals(other._animes, _animes) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.filterState, filterState));
|
||||
.equals(other.filterState, filterState) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.trackingType, trackingType));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(_animes),
|
||||
const DeepCollectionEquality().hash(filterState));
|
||||
const DeepCollectionEquality().hash(filterState),
|
||||
const DeepCollectionEquality().hash(trackingType));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -148,13 +172,16 @@ class _$_AnimeListState implements _AnimeListState {
|
||||
abstract class _AnimeListState implements AnimeListState {
|
||||
factory _AnimeListState(
|
||||
{final List<AnimeTrackingData> animes,
|
||||
final AnimeTrackingState filterState}) = _$_AnimeListState;
|
||||
final AnimeTrackingState filterState,
|
||||
final TrackingMediumType trackingType}) = _$_AnimeListState;
|
||||
|
||||
@override
|
||||
List<AnimeTrackingData> get animes;
|
||||
@override
|
||||
AnimeTrackingState get filterState;
|
||||
@override
|
||||
TrackingMediumType get trackingType;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_AnimeListStateCopyWith<_$_AnimeListState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
@ -33,3 +33,11 @@ class AnimeFilterChangedEvent extends AnimeListEvent {
|
||||
/// The state to filter
|
||||
final AnimeTrackingState filterState;
|
||||
}
|
||||
|
||||
/// Triggered when the view is changed from the anime or the manga view
|
||||
class AnimeTrackingTypeChanged extends AnimeListEvent {
|
||||
AnimeTrackingTypeChanged(this.type);
|
||||
|
||||
/// The type we switched to
|
||||
final TrackingMediumType type;
|
||||
}
|
||||
|
@ -5,5 +5,6 @@ class AnimeListState with _$AnimeListState {
|
||||
factory AnimeListState({
|
||||
@Default([]) List<AnimeTrackingData> animes,
|
||||
@Default(AnimeTrackingState.watching) AnimeTrackingState filterState,
|
||||
@Default(TrackingMediumType.anime) TrackingMediumType trackingType,
|
||||
}) = _AnimeListState;
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
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/ui/bloc/anime_list_bloc.dart';
|
||||
import 'package:anitrack/src/ui/bloc/anime_search_bloc.dart';
|
||||
import 'package:anitrack/src/ui/constants.dart';
|
||||
import 'package:anitrack/src/ui/widgets/anime.dart';
|
||||
import 'package:bottom_bar/bottom_bar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class AnimeListPage extends StatelessWidget {
|
||||
static MaterialPageRoute<dynamic> get route => MaterialPageRoute<dynamic>(
|
||||
@ -14,13 +16,24 @@ class AnimeListPage extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
|
||||
final PageController _controller = PageController();
|
||||
|
||||
String _getPageTitle(TrackingMediumType type) {
|
||||
switch (type) {
|
||||
case TrackingMediumType.anime: return 'Anime';
|
||||
case TrackingMediumType.manga: return 'Manga';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<AnimeListBloc, AnimeListState>(
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Animes'),
|
||||
title: Text(
|
||||
_getPageTitle(state.trackingType)
|
||||
),
|
||||
actions: [
|
||||
PopupMenuButton(
|
||||
icon: Icon(
|
||||
@ -57,28 +70,34 @@ class AnimeListPage extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ListView.builder(
|
||||
itemCount: state.animes.length,
|
||||
itemBuilder: (context, index) {
|
||||
final anime = state.animes[index];
|
||||
if (state.filterState != AnimeTrackingState.all) {
|
||||
if (anime.state != state.filterState) return Container();
|
||||
}
|
||||
body: PageView(
|
||||
controller: _controller,
|
||||
children: [
|
||||
ListView.builder(
|
||||
itemCount: state.animes.length,
|
||||
itemBuilder: (context, index) {
|
||||
final anime = state.animes[index];
|
||||
if (state.filterState != AnimeTrackingState.all) {
|
||||
if (anime.state != state.filterState) return Container();
|
||||
}
|
||||
|
||||
return AnimeListWidget(
|
||||
data: anime,
|
||||
onLeftSwipe: () {
|
||||
context.read<AnimeListBloc>().add(
|
||||
AnimeEpisodeDecrementedEvent(state.animes[index].id),
|
||||
return AnimeListWidget(
|
||||
data: anime,
|
||||
onLeftSwipe: () {
|
||||
context.read<AnimeListBloc>().add(
|
||||
AnimeEpisodeDecrementedEvent(state.animes[index].id),
|
||||
);
|
||||
},
|
||||
onRightSwipe: () {
|
||||
context.read<AnimeListBloc>().add(
|
||||
AnimeEpisodeIncrementedEvent(state.animes[index].id),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
onRightSwipe: () {
|
||||
context.read<AnimeListBloc>().add(
|
||||
AnimeEpisodeIncrementedEvent(state.animes[index].id),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
Placeholder(),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
@ -89,6 +108,33 @@ class AnimeListPage extends StatelessWidget {
|
||||
tooltip: 'Increment',
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
bottomNavigationBar: BottomBar(
|
||||
selectedIndex: state.trackingType == TrackingMediumType.anime ?
|
||||
0 :
|
||||
1,
|
||||
onTap: (int index) {
|
||||
_controller.jumpToPage(index);
|
||||
context.read<AnimeListBloc>().add(
|
||||
AnimeTrackingTypeChanged(
|
||||
index == 0 ?
|
||||
TrackingMediumType.anime :
|
||||
TrackingMediumType.manga,
|
||||
),
|
||||
);
|
||||
},
|
||||
items: <BottomBarItem>[
|
||||
BottomBarItem(
|
||||
icon: Icon(Icons.tv),
|
||||
title: Text('Anime'),
|
||||
activeColor: Colors.blue,
|
||||
),
|
||||
BottomBarItem(
|
||||
icon: Icon(Icons.auto_stories),
|
||||
title: Text('Manga'),
|
||||
activeColor: Colors.red,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -43,6 +43,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
bottom_bar:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: bottom_bar
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
build:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -9,6 +9,7 @@ environment:
|
||||
|
||||
dependencies:
|
||||
bloc: ^8.1.0
|
||||
bottom_bar: ^2.0.3
|
||||
cupertino_icons: ^1.0.2
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
Loading…
Reference in New Issue
Block a user