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,7 +70,10 @@ class AnimeListPage extends StatelessWidget { | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
|           body: ListView.builder( | ||||
|           body: PageView( | ||||
|             controller: _controller, | ||||
|             children: [ | ||||
|               ListView.builder( | ||||
|                 itemCount: state.animes.length, | ||||
|                 itemBuilder: (context, index) { | ||||
|                   final anime = state.animes[index]; | ||||
| @ -80,6 +96,9 @@ class AnimeListPage extends StatelessWidget { | ||||
|                   ); | ||||
|                 }, | ||||
|               ), | ||||
|               Placeholder(), | ||||
|             ], | ||||
|           ), | ||||
|           floatingActionButton: FloatingActionButton( | ||||
|             onPressed: () { | ||||
|               context.read<AnimeSearchBloc>().add( | ||||
| @ -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