feat(service): Add working database persistency
This commit is contained in:
parent
624c8bd78a
commit
432796d0c4
@ -4,17 +4,32 @@ import 'package:anitrack/src/ui/bloc/navigation_bloc.dart';
|
||||
import 'package:anitrack/src/ui/constants.dart';
|
||||
import 'package:anitrack/src/ui/pages/anime_list.dart';
|
||||
import 'package:anitrack/src/ui/pages/anime_search.dart';
|
||||
import 'package:anitrack/src/service/database.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
void main() {
|
||||
void main() async {
|
||||
final navKey = GlobalKey<NavigatorState>();
|
||||
|
||||
// Initialize the widgets binding for sqflite
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Initialize the database
|
||||
final database = DatabaseService();
|
||||
await database.initialize();
|
||||
|
||||
// Register singletons
|
||||
GetIt.I.registerSingleton<DatabaseService>(database);
|
||||
GetIt.I.registerSingleton<AnimeListBloc>(AnimeListBloc());
|
||||
GetIt.I.registerSingleton<AnimeSearchBloc>(AnimeSearchBloc());
|
||||
GetIt.I.registerSingleton<NavigationBloc>(NavigationBloc(navKey));
|
||||
|
||||
// Load animes
|
||||
GetIt.I.get<AnimeListBloc>().add(
|
||||
AnimesLoadedEvent(),
|
||||
);
|
||||
|
||||
runApp(
|
||||
MultiBlocProvider(
|
||||
providers: [
|
||||
|
@ -1,50 +1,64 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'anime.freezed.dart';
|
||||
part 'anime.g.dart';
|
||||
|
||||
/// The watch state of an anime
|
||||
enum AnimeTrackingState {
|
||||
watching,
|
||||
completed,
|
||||
planToWatch,
|
||||
dropped,
|
||||
watching, // 0
|
||||
completed, // 1
|
||||
planToWatch, // 2
|
||||
dropped, // 3
|
||||
}
|
||||
|
||||
extension AnimeTrackStateExtension on AnimeTrackingState {
|
||||
int toInteger() {
|
||||
switch (this) {
|
||||
case AnimeTrackingState.watching: return 0;
|
||||
case AnimeTrackingState.completed: return 1;
|
||||
case AnimeTrackingState.planToWatch: return 2;
|
||||
case AnimeTrackingState.dropped: return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
class AnimeTrackingData {
|
||||
const AnimeTrackingData(
|
||||
this.id,
|
||||
this.state,
|
||||
this.title,
|
||||
this.episodesWatched,
|
||||
this.episodesTotal,
|
||||
this.thumbnailUrl,
|
||||
);
|
||||
|
||||
/// The ID of the anime
|
||||
final String id;
|
||||
|
||||
/// The state of the anime
|
||||
final AnimeTrackingState state;
|
||||
|
||||
/// The title of the anime
|
||||
final String title;
|
||||
|
||||
/// Episodes in total.
|
||||
final int? episodesTotal;
|
||||
|
||||
/// Episodes watched.
|
||||
final int episodesWatched;
|
||||
|
||||
/// URL to the thumbnail/cover art for the anime.
|
||||
final String thumbnailUrl;
|
||||
|
||||
AnimeTrackingData copyWith(
|
||||
@freezed
|
||||
class AnimeTrackingData with _$AnimeTrackingData{
|
||||
factory AnimeTrackingData(
|
||||
/// The ID of the anime
|
||||
String id,
|
||||
/// The state of the anime
|
||||
@AnimeTrackingStateConverter() AnimeTrackingState state,
|
||||
/// The title of the anime
|
||||
String title,
|
||||
/// Episodes in total.
|
||||
int episodesWatched,
|
||||
) {
|
||||
return AnimeTrackingData(
|
||||
id,
|
||||
state,
|
||||
title,
|
||||
episodesWatched,
|
||||
episodesTotal,
|
||||
thumbnailUrl,
|
||||
);
|
||||
}
|
||||
/// Episodes watched.
|
||||
int? episodesTotal,
|
||||
/// URL to the thumbnail/cover art for the anime.
|
||||
String thumbnailUrl,
|
||||
) = _AnimeTrackingData;
|
||||
|
||||
/// JSON
|
||||
factory AnimeTrackingData.fromJson(Map<String, dynamic> json) => _$AnimeTrackingDataFromJson(json);
|
||||
}
|
||||
|
295
lib/src/data/anime.freezed.dart
Normal file
295
lib/src/data/anime.freezed.dart
Normal file
@ -0,0 +1,295 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
||||
|
||||
part of 'anime.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||
|
||||
AnimeTrackingData _$AnimeTrackingDataFromJson(Map<String, dynamic> json) {
|
||||
return _AnimeTrackingData.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$AnimeTrackingData {
|
||||
/// The ID of the anime
|
||||
String get id => throw _privateConstructorUsedError;
|
||||
|
||||
/// The state of the anime
|
||||
@AnimeTrackingStateConverter()
|
||||
AnimeTrackingState get state => throw _privateConstructorUsedError;
|
||||
|
||||
/// The title of the anime
|
||||
String get title => throw _privateConstructorUsedError;
|
||||
|
||||
/// Episodes in total.
|
||||
int get episodesWatched => throw _privateConstructorUsedError;
|
||||
|
||||
/// Episodes watched.
|
||||
int? get episodesTotal => throw _privateConstructorUsedError;
|
||||
|
||||
/// URL to the thumbnail/cover art for the anime.
|
||||
String get thumbnailUrl => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$AnimeTrackingDataCopyWith<AnimeTrackingData> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $AnimeTrackingDataCopyWith<$Res> {
|
||||
factory $AnimeTrackingDataCopyWith(
|
||||
AnimeTrackingData value, $Res Function(AnimeTrackingData) then) =
|
||||
_$AnimeTrackingDataCopyWithImpl<$Res>;
|
||||
$Res call(
|
||||
{String id,
|
||||
@AnimeTrackingStateConverter() AnimeTrackingState state,
|
||||
String title,
|
||||
int episodesWatched,
|
||||
int? episodesTotal,
|
||||
String thumbnailUrl});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$AnimeTrackingDataCopyWithImpl<$Res>
|
||||
implements $AnimeTrackingDataCopyWith<$Res> {
|
||||
_$AnimeTrackingDataCopyWithImpl(this._value, this._then);
|
||||
|
||||
final AnimeTrackingData _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function(AnimeTrackingData) _then;
|
||||
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = freezed,
|
||||
Object? state = freezed,
|
||||
Object? title = freezed,
|
||||
Object? episodesWatched = freezed,
|
||||
Object? episodesTotal = freezed,
|
||||
Object? thumbnailUrl = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
id: id == freezed
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
state: state == freezed
|
||||
? _value.state
|
||||
: state // ignore: cast_nullable_to_non_nullable
|
||||
as AnimeTrackingState,
|
||||
title: title == freezed
|
||||
? _value.title
|
||||
: title // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
episodesWatched: episodesWatched == freezed
|
||||
? _value.episodesWatched
|
||||
: episodesWatched // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
episodesTotal: episodesTotal == freezed
|
||||
? _value.episodesTotal
|
||||
: episodesTotal // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
thumbnailUrl: thumbnailUrl == freezed
|
||||
? _value.thumbnailUrl
|
||||
: thumbnailUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$_AnimeTrackingDataCopyWith<$Res>
|
||||
implements $AnimeTrackingDataCopyWith<$Res> {
|
||||
factory _$$_AnimeTrackingDataCopyWith(_$_AnimeTrackingData value,
|
||||
$Res Function(_$_AnimeTrackingData) then) =
|
||||
__$$_AnimeTrackingDataCopyWithImpl<$Res>;
|
||||
@override
|
||||
$Res call(
|
||||
{String id,
|
||||
@AnimeTrackingStateConverter() AnimeTrackingState state,
|
||||
String title,
|
||||
int episodesWatched,
|
||||
int? episodesTotal,
|
||||
String thumbnailUrl});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$_AnimeTrackingDataCopyWithImpl<$Res>
|
||||
extends _$AnimeTrackingDataCopyWithImpl<$Res>
|
||||
implements _$$_AnimeTrackingDataCopyWith<$Res> {
|
||||
__$$_AnimeTrackingDataCopyWithImpl(
|
||||
_$_AnimeTrackingData _value, $Res Function(_$_AnimeTrackingData) _then)
|
||||
: super(_value, (v) => _then(v as _$_AnimeTrackingData));
|
||||
|
||||
@override
|
||||
_$_AnimeTrackingData get _value => super._value as _$_AnimeTrackingData;
|
||||
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = freezed,
|
||||
Object? state = freezed,
|
||||
Object? title = freezed,
|
||||
Object? episodesWatched = freezed,
|
||||
Object? episodesTotal = freezed,
|
||||
Object? thumbnailUrl = freezed,
|
||||
}) {
|
||||
return _then(_$_AnimeTrackingData(
|
||||
id == freezed
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
state == freezed
|
||||
? _value.state
|
||||
: state // ignore: cast_nullable_to_non_nullable
|
||||
as AnimeTrackingState,
|
||||
title == freezed
|
||||
? _value.title
|
||||
: title // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
episodesWatched == freezed
|
||||
? _value.episodesWatched
|
||||
: episodesWatched // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
episodesTotal == freezed
|
||||
? _value.episodesTotal
|
||||
: episodesTotal // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
thumbnailUrl == freezed
|
||||
? _value.thumbnailUrl
|
||||
: thumbnailUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$_AnimeTrackingData implements _AnimeTrackingData {
|
||||
_$_AnimeTrackingData(this.id, @AnimeTrackingStateConverter() this.state,
|
||||
this.title, this.episodesWatched, this.episodesTotal, this.thumbnailUrl);
|
||||
|
||||
factory _$_AnimeTrackingData.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_AnimeTrackingDataFromJson(json);
|
||||
|
||||
/// The ID of the anime
|
||||
@override
|
||||
final String id;
|
||||
|
||||
/// The state of the anime
|
||||
@override
|
||||
@AnimeTrackingStateConverter()
|
||||
final AnimeTrackingState state;
|
||||
|
||||
/// The title of the anime
|
||||
@override
|
||||
final String title;
|
||||
|
||||
/// Episodes in total.
|
||||
@override
|
||||
final int episodesWatched;
|
||||
|
||||
/// Episodes watched.
|
||||
@override
|
||||
final int? episodesTotal;
|
||||
|
||||
/// URL to the thumbnail/cover art for the anime.
|
||||
@override
|
||||
final String thumbnailUrl;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AnimeTrackingData(id: $id, state: $state, title: $title, episodesWatched: $episodesWatched, episodesTotal: $episodesTotal, thumbnailUrl: $thumbnailUrl)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$_AnimeTrackingData &&
|
||||
const DeepCollectionEquality().equals(other.id, id) &&
|
||||
const DeepCollectionEquality().equals(other.state, state) &&
|
||||
const DeepCollectionEquality().equals(other.title, title) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.episodesWatched, episodesWatched) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.episodesTotal, episodesTotal) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.thumbnailUrl, thumbnailUrl));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(id),
|
||||
const DeepCollectionEquality().hash(state),
|
||||
const DeepCollectionEquality().hash(title),
|
||||
const DeepCollectionEquality().hash(episodesWatched),
|
||||
const DeepCollectionEquality().hash(episodesTotal),
|
||||
const DeepCollectionEquality().hash(thumbnailUrl));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
_$$_AnimeTrackingDataCopyWith<_$_AnimeTrackingData> get copyWith =>
|
||||
__$$_AnimeTrackingDataCopyWithImpl<_$_AnimeTrackingData>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$_AnimeTrackingDataToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _AnimeTrackingData implements AnimeTrackingData {
|
||||
factory _AnimeTrackingData(
|
||||
final String id,
|
||||
@AnimeTrackingStateConverter() final AnimeTrackingState state,
|
||||
final String title,
|
||||
final int episodesWatched,
|
||||
final int? episodesTotal,
|
||||
final String thumbnailUrl) = _$_AnimeTrackingData;
|
||||
|
||||
factory _AnimeTrackingData.fromJson(Map<String, dynamic> json) =
|
||||
_$_AnimeTrackingData.fromJson;
|
||||
|
||||
@override
|
||||
|
||||
/// The ID of the anime
|
||||
String get id;
|
||||
@override
|
||||
|
||||
/// The state of the anime
|
||||
@AnimeTrackingStateConverter()
|
||||
AnimeTrackingState get state;
|
||||
@override
|
||||
|
||||
/// The title of the anime
|
||||
String get title;
|
||||
@override
|
||||
|
||||
/// Episodes in total.
|
||||
int get episodesWatched;
|
||||
@override
|
||||
|
||||
/// Episodes watched.
|
||||
int? get episodesTotal;
|
||||
@override
|
||||
|
||||
/// URL to the thumbnail/cover art for the anime.
|
||||
String get thumbnailUrl;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_AnimeTrackingDataCopyWith<_$_AnimeTrackingData> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
28
lib/src/data/anime.g.dart
Normal file
28
lib/src/data/anime.g.dart
Normal file
@ -0,0 +1,28 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'anime.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$_AnimeTrackingData _$$_AnimeTrackingDataFromJson(Map<String, dynamic> json) =>
|
||||
_$_AnimeTrackingData(
|
||||
json['id'] as String,
|
||||
const AnimeTrackingStateConverter().fromJson(json['state'] as int),
|
||||
json['title'] as String,
|
||||
json['episodesWatched'] as int,
|
||||
json['episodesTotal'] as int?,
|
||||
json['thumbnailUrl'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$_AnimeTrackingDataToJson(
|
||||
_$_AnimeTrackingData instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'state': const AnimeTrackingStateConverter().toJson(instance.state),
|
||||
'title': instance.title,
|
||||
'episodesWatched': instance.episodesWatched,
|
||||
'episodesTotal': instance.episodesTotal,
|
||||
'thumbnailUrl': instance.thumbnailUrl,
|
||||
};
|
55
lib/src/service/database.dart
Normal file
55
lib/src/service/database.dart
Normal file
@ -0,0 +1,55 @@
|
||||
import 'package:anitrack/src/data/anime.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
|
||||
const animeTable = 'Anime';
|
||||
|
||||
Future<void> _createDatabase(Database db, int version) async {
|
||||
await db.execute(
|
||||
'''
|
||||
CREATE TABLE $animeTable(
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
state INTEGER NOT NULL,
|
||||
episodesTotal INTEGER,
|
||||
episodesWatched INTEGER NOT NULL,
|
||||
thumbnailUrl TEXT NOT NULL,
|
||||
title TEXT NOT NULL
|
||||
)''',
|
||||
);
|
||||
}
|
||||
|
||||
class DatabaseService {
|
||||
late final Database _db;
|
||||
|
||||
Future<void> initialize() async {
|
||||
_db = await openDatabase(
|
||||
'anitrack.db',
|
||||
version: 1,
|
||||
onCreate: _createDatabase,
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<AnimeTrackingData>> loadAnimes() async {
|
||||
final animes = await _db.query(animeTable);
|
||||
|
||||
return animes
|
||||
.cast<Map<String, dynamic>>()
|
||||
.map((Map<String, dynamic> anime) => AnimeTrackingData.fromJson(anime))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<void> addAnime(AnimeTrackingData data) async {
|
||||
await _db.insert(
|
||||
animeTable,
|
||||
data.toJson(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updateAnime(AnimeTrackingData data) async {
|
||||
await _db.update(
|
||||
animeTable,
|
||||
data.toJson(),
|
||||
where: 'id = ?',
|
||||
whereArgs: [data.id],
|
||||
);
|
||||
}
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
import 'dart:math';
|
||||
import 'package:anitrack/src/data/anime.dart';
|
||||
import 'package:anitrack/src/service/database.dart';
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
part 'anime_list_state.dart';
|
||||
part 'anime_list_event.dart';
|
||||
@ -12,10 +14,13 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
||||
on<AnimeAddedEvent>(_onAnimeAdded);
|
||||
on<AnimeEpisodeIncrementedEvent>(_onIncremented);
|
||||
on<AnimeEpisodeDecrementedEvent>(_onDecremented);
|
||||
on<AnimesLoadedEvent>(_onAnimesLoaded);
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
Future<void> _onAnimeAdded(AnimeAddedEvent event, Emitter<AnimeListState> emit) async {
|
||||
// Add the anime to the database
|
||||
await GetIt.I.get<DatabaseService>().addAnime(event.data);
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
animes: List.from([
|
||||
@ -34,16 +39,18 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
||||
if (anime.episodesTotal != null && anime.episodesWatched + 1 > anime.episodesTotal!) return;
|
||||
|
||||
final newList = List<AnimeTrackingData>.from(state.animes);
|
||||
newList[index] = anime.copyWith(
|
||||
anime.episodesWatched + 1,
|
||||
final newAnime = anime.copyWith(
|
||||
episodesWatched: anime.episodesWatched + 1,
|
||||
);
|
||||
newList[index] = newAnime;
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
animes: newList,
|
||||
),
|
||||
);
|
||||
print('${event.id} incremented');
|
||||
|
||||
await GetIt.I.get<DatabaseService>().updateAnime(newAnime);
|
||||
}
|
||||
|
||||
Future<void> _onDecremented(AnimeEpisodeDecrementedEvent event, Emitter<AnimeListState> emit) async {
|
||||
@ -54,15 +61,25 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
||||
if (anime.episodesWatched - 1 < 0) return;
|
||||
|
||||
final newList = List<AnimeTrackingData>.from(state.animes);
|
||||
newList[index] = anime.copyWith(
|
||||
anime.episodesWatched - 1,
|
||||
final newAnime = anime.copyWith(
|
||||
episodesWatched: anime.episodesWatched - 1,
|
||||
);
|
||||
newList[index] = newAnime;
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
animes: newList,
|
||||
),
|
||||
);
|
||||
print('${event.id} decremented');
|
||||
|
||||
await GetIt.I.get<DatabaseService>().updateAnime(newAnime);
|
||||
}
|
||||
|
||||
Future<void> _onAnimesLoaded(AnimesLoadedEvent event, Emitter<AnimeListState> emit) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
animes: await GetIt.I.get<DatabaseService>().loadAnimes(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -22,3 +22,6 @@ class AnimeAddedEvent extends AnimeListEvent {
|
||||
/// The anime to add.
|
||||
final AnimeTrackingData data;
|
||||
}
|
||||
|
||||
/// Triggered when animes are to be loaded from the database
|
||||
class AnimesLoadedEvent extends AnimeListEvent {}
|
||||
|
@ -79,20 +79,27 @@ class AnimeSearchPage extends StatelessWidget {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
item.title,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
maxLines: 2,
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
item.title,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
maxLines: 2,
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
|
||||
Text(
|
||||
item.description,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 4,
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
item.description,
|
||||
textAlign: TextAlign.justify,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 4,
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
41
pubspec.lock
41
pubspec.lock
@ -292,12 +292,19 @@ packages:
|
||||
source: hosted
|
||||
version: "0.6.5"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: json_annotation
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.8.0"
|
||||
version: "4.6.0"
|
||||
json_serializable:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: json_serializable
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.3.2"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -415,6 +422,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.7"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -422,6 +436,20 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
sqflite:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sqflite
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.4+1"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.4.2+2"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -457,6 +485,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0+3"
|
||||
synchronized:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -515,4 +550,4 @@ packages:
|
||||
version: "3.1.1"
|
||||
sdks:
|
||||
dart: ">=2.18.4 <3.0.0"
|
||||
flutter: ">=1.16.0"
|
||||
flutter: ">=3.3.0"
|
||||
|
@ -16,7 +16,8 @@ dependencies:
|
||||
freezed_annotation: 2.1.0
|
||||
get_it: ^7.2.0
|
||||
jikan_api: ^2.0.0
|
||||
|
||||
json_annotation: 4.6.0
|
||||
sqflite: ^2.2.4+1
|
||||
swipeable_tile: ^2.0.0+3
|
||||
|
||||
dev_dependencies:
|
||||
@ -25,6 +26,7 @@ dev_dependencies:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.0
|
||||
freezed: ^2.1.0+1
|
||||
json_serializable: ^6.3.1
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
Loading…
Reference in New Issue
Block a user