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 'dart:math';
|
||||||
import 'package:anitrack/src/data/anime.dart';
|
import 'package:anitrack/src/data/anime.dart';
|
||||||
|
import 'package:anitrack/src/data/type.dart';
|
||||||
import 'package:anitrack/src/service/database.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';
|
||||||
@ -16,6 +17,7 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
|||||||
on<AnimeEpisodeDecrementedEvent>(_onDecremented);
|
on<AnimeEpisodeDecrementedEvent>(_onDecremented);
|
||||||
on<AnimesLoadedEvent>(_onAnimesLoaded);
|
on<AnimesLoadedEvent>(_onAnimesLoaded);
|
||||||
on<AnimeFilterChangedEvent>(_onAnimesFiltered);
|
on<AnimeFilterChangedEvent>(_onAnimesFiltered);
|
||||||
|
on<AnimeTrackingTypeChanged>(_onTrackingTypeChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onAnimeAdded(AnimeAddedEvent event, Emitter<AnimeListState> emit) async {
|
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 {
|
mixin _$AnimeListState {
|
||||||
List<AnimeTrackingData> get animes => throw _privateConstructorUsedError;
|
List<AnimeTrackingData> get animes => throw _privateConstructorUsedError;
|
||||||
AnimeTrackingState get filterState => throw _privateConstructorUsedError;
|
AnimeTrackingState get filterState => throw _privateConstructorUsedError;
|
||||||
|
TrackingMediumType get trackingType => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
$AnimeListStateCopyWith<AnimeListState> get copyWith =>
|
$AnimeListStateCopyWith<AnimeListState> get copyWith =>
|
||||||
@ -29,7 +30,10 @@ abstract class $AnimeListStateCopyWith<$Res> {
|
|||||||
factory $AnimeListStateCopyWith(
|
factory $AnimeListStateCopyWith(
|
||||||
AnimeListState value, $Res Function(AnimeListState) then) =
|
AnimeListState value, $Res Function(AnimeListState) then) =
|
||||||
_$AnimeListStateCopyWithImpl<$Res>;
|
_$AnimeListStateCopyWithImpl<$Res>;
|
||||||
$Res call({List<AnimeTrackingData> animes, AnimeTrackingState filterState});
|
$Res call(
|
||||||
|
{List<AnimeTrackingData> animes,
|
||||||
|
AnimeTrackingState filterState,
|
||||||
|
TrackingMediumType trackingType});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@ -45,6 +49,7 @@ class _$AnimeListStateCopyWithImpl<$Res>
|
|||||||
$Res call({
|
$Res call({
|
||||||
Object? animes = freezed,
|
Object? animes = freezed,
|
||||||
Object? filterState = freezed,
|
Object? filterState = freezed,
|
||||||
|
Object? trackingType = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(_value.copyWith(
|
return _then(_value.copyWith(
|
||||||
animes: animes == freezed
|
animes: animes == freezed
|
||||||
@ -55,6 +60,10 @@ class _$AnimeListStateCopyWithImpl<$Res>
|
|||||||
? _value.filterState
|
? _value.filterState
|
||||||
: filterState // ignore: cast_nullable_to_non_nullable
|
: filterState // ignore: cast_nullable_to_non_nullable
|
||||||
as AnimeTrackingState,
|
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) =
|
_$_AnimeListState value, $Res Function(_$_AnimeListState) then) =
|
||||||
__$$_AnimeListStateCopyWithImpl<$Res>;
|
__$$_AnimeListStateCopyWithImpl<$Res>;
|
||||||
@override
|
@override
|
||||||
$Res call({List<AnimeTrackingData> animes, AnimeTrackingState filterState});
|
$Res call(
|
||||||
|
{List<AnimeTrackingData> animes,
|
||||||
|
AnimeTrackingState filterState,
|
||||||
|
TrackingMediumType trackingType});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@ -84,6 +96,7 @@ class __$$_AnimeListStateCopyWithImpl<$Res>
|
|||||||
$Res call({
|
$Res call({
|
||||||
Object? animes = freezed,
|
Object? animes = freezed,
|
||||||
Object? filterState = freezed,
|
Object? filterState = freezed,
|
||||||
|
Object? trackingType = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$_AnimeListState(
|
return _then(_$_AnimeListState(
|
||||||
animes: animes == freezed
|
animes: animes == freezed
|
||||||
@ -94,6 +107,10 @@ class __$$_AnimeListStateCopyWithImpl<$Res>
|
|||||||
? _value.filterState
|
? _value.filterState
|
||||||
: filterState // ignore: cast_nullable_to_non_nullable
|
: filterState // ignore: cast_nullable_to_non_nullable
|
||||||
as AnimeTrackingState,
|
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 {
|
class _$_AnimeListState implements _AnimeListState {
|
||||||
_$_AnimeListState(
|
_$_AnimeListState(
|
||||||
{final List<AnimeTrackingData> animes = const [],
|
{final List<AnimeTrackingData> animes = const [],
|
||||||
this.filterState = AnimeTrackingState.watching})
|
this.filterState = AnimeTrackingState.watching,
|
||||||
|
this.trackingType = TrackingMediumType.anime})
|
||||||
: _animes = animes;
|
: _animes = animes;
|
||||||
|
|
||||||
final List<AnimeTrackingData> _animes;
|
final List<AnimeTrackingData> _animes;
|
||||||
@ -117,10 +135,13 @@ class _$_AnimeListState implements _AnimeListState {
|
|||||||
@override
|
@override
|
||||||
@JsonKey()
|
@JsonKey()
|
||||||
final AnimeTrackingState filterState;
|
final AnimeTrackingState filterState;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final TrackingMediumType trackingType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'AnimeListState(animes: $animes, filterState: $filterState)';
|
return 'AnimeListState(animes: $animes, filterState: $filterState, trackingType: $trackingType)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -130,14 +151,17 @@ class _$_AnimeListState implements _AnimeListState {
|
|||||||
other is _$_AnimeListState &&
|
other is _$_AnimeListState &&
|
||||||
const DeepCollectionEquality().equals(other._animes, _animes) &&
|
const DeepCollectionEquality().equals(other._animes, _animes) &&
|
||||||
const DeepCollectionEquality()
|
const DeepCollectionEquality()
|
||||||
.equals(other.filterState, filterState));
|
.equals(other.filterState, filterState) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other.trackingType, trackingType));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(
|
int get hashCode => Object.hash(
|
||||||
runtimeType,
|
runtimeType,
|
||||||
const DeepCollectionEquality().hash(_animes),
|
const DeepCollectionEquality().hash(_animes),
|
||||||
const DeepCollectionEquality().hash(filterState));
|
const DeepCollectionEquality().hash(filterState),
|
||||||
|
const DeepCollectionEquality().hash(trackingType));
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
@override
|
@override
|
||||||
@ -148,13 +172,16 @@ class _$_AnimeListState implements _AnimeListState {
|
|||||||
abstract class _AnimeListState implements AnimeListState {
|
abstract class _AnimeListState implements AnimeListState {
|
||||||
factory _AnimeListState(
|
factory _AnimeListState(
|
||||||
{final List<AnimeTrackingData> animes,
|
{final List<AnimeTrackingData> animes,
|
||||||
final AnimeTrackingState filterState}) = _$_AnimeListState;
|
final AnimeTrackingState filterState,
|
||||||
|
final TrackingMediumType trackingType}) = _$_AnimeListState;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<AnimeTrackingData> get animes;
|
List<AnimeTrackingData> get animes;
|
||||||
@override
|
@override
|
||||||
AnimeTrackingState get filterState;
|
AnimeTrackingState get filterState;
|
||||||
@override
|
@override
|
||||||
|
TrackingMediumType get trackingType;
|
||||||
|
@override
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
_$$_AnimeListStateCopyWith<_$_AnimeListState> get copyWith =>
|
_$$_AnimeListStateCopyWith<_$_AnimeListState> get copyWith =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
|
@ -33,3 +33,11 @@ class AnimeFilterChangedEvent extends AnimeListEvent {
|
|||||||
/// The state to filter
|
/// The state to filter
|
||||||
final AnimeTrackingState filterState;
|
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({
|
factory AnimeListState({
|
||||||
@Default([]) List<AnimeTrackingData> animes,
|
@Default([]) List<AnimeTrackingData> animes,
|
||||||
@Default(AnimeTrackingState.watching) AnimeTrackingState filterState,
|
@Default(AnimeTrackingState.watching) AnimeTrackingState filterState,
|
||||||
|
@Default(TrackingMediumType.anime) TrackingMediumType trackingType,
|
||||||
}) = _AnimeListState;
|
}) = _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/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_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/constants.dart';
|
import 'package:anitrack/src/ui/constants.dart';
|
||||||
import 'package:anitrack/src/ui/widgets/anime.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 {
|
class AnimeListPage extends StatelessWidget {
|
||||||
static MaterialPageRoute<dynamic> get route => MaterialPageRoute<dynamic>(
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<AnimeListBloc, AnimeListState>(
|
return BlocBuilder<AnimeListBloc, AnimeListState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Animes'),
|
title: Text(
|
||||||
|
_getPageTitle(state.trackingType)
|
||||||
|
),
|
||||||
actions: [
|
actions: [
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
@ -57,28 +70,34 @@ class AnimeListPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ListView.builder(
|
body: PageView(
|
||||||
itemCount: state.animes.length,
|
controller: _controller,
|
||||||
itemBuilder: (context, index) {
|
children: [
|
||||||
final anime = state.animes[index];
|
ListView.builder(
|
||||||
if (state.filterState != AnimeTrackingState.all) {
|
itemCount: state.animes.length,
|
||||||
if (anime.state != state.filterState) return Container();
|
itemBuilder: (context, index) {
|
||||||
}
|
final anime = state.animes[index];
|
||||||
|
if (state.filterState != AnimeTrackingState.all) {
|
||||||
|
if (anime.state != state.filterState) return Container();
|
||||||
|
}
|
||||||
|
|
||||||
return AnimeListWidget(
|
return AnimeListWidget(
|
||||||
data: anime,
|
data: anime,
|
||||||
onLeftSwipe: () {
|
onLeftSwipe: () {
|
||||||
context.read<AnimeListBloc>().add(
|
context.read<AnimeListBloc>().add(
|
||||||
AnimeEpisodeDecrementedEvent(state.animes[index].id),
|
AnimeEpisodeDecrementedEvent(state.animes[index].id),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onRightSwipe: () {
|
||||||
|
context.read<AnimeListBloc>().add(
|
||||||
|
AnimeEpisodeIncrementedEvent(state.animes[index].id),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onRightSwipe: () {
|
),
|
||||||
context.read<AnimeListBloc>().add(
|
Placeholder(),
|
||||||
AnimeEpisodeIncrementedEvent(state.animes[index].id),
|
],
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -89,6 +108,33 @@ class AnimeListPage extends StatelessWidget {
|
|||||||
tooltip: 'Increment',
|
tooltip: 'Increment',
|
||||||
child: const Icon(Icons.add),
|
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"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
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:
|
build:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -9,6 +9,7 @@ environment:
|
|||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
bloc: ^8.1.0
|
bloc: ^8.1.0
|
||||||
|
bottom_bar: ^2.0.3
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
Loading…
Reference in New Issue
Block a user