Persist theme

Persist theme in sqlite database.
A new table was created for this.

Add migration to add table for persisted theme
This commit is contained in:
Marco 2024-04-04 18:29:09 +02:00
parent 66d7d27a81
commit a52a50d4d1
10 changed files with 411 additions and 158 deletions

View File

@ -8,17 +8,23 @@ import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart';
part 'database.g.dart'; part 'database.g.dart';
class NoteTable extends Table { var database = AppDatabase(); //global, since we should only use one instance
class PersistentNote extends Table {
TextColumn get id => text()(); TextColumn get id => text()();
TextColumn get content => text()(); TextColumn get content => text()();
} }
@DriftDatabase(tables: [NoteTable]) class PersistentTheme extends Table {
TextColumn get brightness => text().withDefault(const Constant("dark"))();
}
@DriftDatabase(tables: [PersistentNote, PersistentTheme])
class AppDatabase extends _$AppDatabase { class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection()); AppDatabase() : super(_openConnection());
@override @override
int get schemaVersion => 1; int get schemaVersion => 2;
@override @override
MigrationStrategy get migration { MigrationStrategy get migration {
@ -27,30 +33,26 @@ class AppDatabase extends _$AppDatabase {
await m.createAll(); await m.createAll();
}, },
onUpgrade: (Migrator m, int from, int to) async { onUpgrade: (Migrator m, int from, int to) async {
if (from < 2) {} if (from < 2) {
await m.renameTable(persistentNote, "note_table");
await m.createTable(persistentTheme);
}
}, },
); );
} }
} }
LazyDatabase _openConnection() { LazyDatabase _openConnection() {
// the LazyDatabase util lets us find the right location for the file async.
return LazyDatabase(() async { return LazyDatabase(() async {
// put the database file, called db.sqlite here, into the documents folder
// for your app.
final dbFolder = await getApplicationDocumentsDirectory(); final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'db.sqlite')); final file = File(p.join(dbFolder.path, 'db.sqlite'));
// Also work around limitations on old Android versions
if (Platform.isAndroid) { if (Platform.isAndroid) {
await applyWorkaroundToOpenSqlite3OnOldAndroidVersions(); await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();
} }
// Make sqlite3 pick a more suitable location for temporary files - the
// one from the system may be inaccessible due to sandboxing.
final cachebase = (await getTemporaryDirectory()).path; final cachebase = (await getTemporaryDirectory()).path;
// We can't access /tmp on Android, which sqlite3 would try by default.
// Explicitly tell it about the correct temporary directory.
sqlite3.tempDirectory = cachebase; sqlite3.tempDirectory = cachebase;
return NativeDatabase.createInBackground(file); return NativeDatabase.createInBackground(file);

View File

@ -3,12 +3,12 @@
part of 'database.dart'; part of 'database.dart';
// ignore_for_file: type=lint // ignore_for_file: type=lint
class $NoteTableTable extends NoteTable class $PersistentNoteTable extends PersistentNote
with TableInfo<$NoteTableTable, NoteTableData> { with TableInfo<$PersistentNoteTable, PersistentNoteData> {
@override @override
final GeneratedDatabase attachedDatabase; final GeneratedDatabase attachedDatabase;
final String? _alias; final String? _alias;
$NoteTableTable(this.attachedDatabase, [this._alias]); $PersistentNoteTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id'); static const VerificationMeta _idMeta = const VerificationMeta('id');
@override @override
late final GeneratedColumn<String> id = GeneratedColumn<String>( late final GeneratedColumn<String> id = GeneratedColumn<String>(
@ -26,9 +26,9 @@ class $NoteTableTable extends NoteTable
String get aliasedName => _alias ?? actualTableName; String get aliasedName => _alias ?? actualTableName;
@override @override
String get actualTableName => $name; String get actualTableName => $name;
static const String $name = 'note_table'; static const String $name = 'persistent_note';
@override @override
VerificationContext validateIntegrity(Insertable<NoteTableData> instance, VerificationContext validateIntegrity(Insertable<PersistentNoteData> instance,
{bool isInserting = false}) { {bool isInserting = false}) {
final context = VerificationContext(); final context = VerificationContext();
final data = instance.toColumns(true); final data = instance.toColumns(true);
@ -49,9 +49,9 @@ class $NoteTableTable extends NoteTable
@override @override
Set<GeneratedColumn> get $primaryKey => const {}; Set<GeneratedColumn> get $primaryKey => const {};
@override @override
NoteTableData map(Map<String, dynamic> data, {String? tablePrefix}) { PersistentNoteData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return NoteTableData( return PersistentNoteData(
id: attachedDatabase.typeMapping id: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}id'])!, .read(DriftSqlType.string, data['${effectivePrefix}id'])!,
content: attachedDatabase.typeMapping content: attachedDatabase.typeMapping
@ -60,15 +60,16 @@ class $NoteTableTable extends NoteTable
} }
@override @override
$NoteTableTable createAlias(String alias) { $PersistentNoteTable createAlias(String alias) {
return $NoteTableTable(attachedDatabase, alias); return $PersistentNoteTable(attachedDatabase, alias);
} }
} }
class NoteTableData extends DataClass implements Insertable<NoteTableData> { class PersistentNoteData extends DataClass
implements Insertable<PersistentNoteData> {
final String id; final String id;
final String content; final String content;
const NoteTableData({required this.id, required this.content}); const PersistentNoteData({required this.id, required this.content});
@override @override
Map<String, Expression> toColumns(bool nullToAbsent) { Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{}; final map = <String, Expression>{};
@ -77,17 +78,17 @@ class NoteTableData extends DataClass implements Insertable<NoteTableData> {
return map; return map;
} }
NoteTableCompanion toCompanion(bool nullToAbsent) { PersistentNoteCompanion toCompanion(bool nullToAbsent) {
return NoteTableCompanion( return PersistentNoteCompanion(
id: Value(id), id: Value(id),
content: Value(content), content: Value(content),
); );
} }
factory NoteTableData.fromJson(Map<String, dynamic> json, factory PersistentNoteData.fromJson(Map<String, dynamic> json,
{ValueSerializer? serializer}) { {ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer; serializer ??= driftRuntimeOptions.defaultSerializer;
return NoteTableData( return PersistentNoteData(
id: serializer.fromJson<String>(json['id']), id: serializer.fromJson<String>(json['id']),
content: serializer.fromJson<String>(json['content']), content: serializer.fromJson<String>(json['content']),
); );
@ -101,13 +102,14 @@ class NoteTableData extends DataClass implements Insertable<NoteTableData> {
}; };
} }
NoteTableData copyWith({String? id, String? content}) => NoteTableData( PersistentNoteData copyWith({String? id, String? content}) =>
PersistentNoteData(
id: id ?? this.id, id: id ?? this.id,
content: content ?? this.content, content: content ?? this.content,
); );
@override @override
String toString() { String toString() {
return (StringBuffer('NoteTableData(') return (StringBuffer('PersistentNoteData(')
..write('id: $id, ') ..write('id: $id, ')
..write('content: $content') ..write('content: $content')
..write(')')) ..write(')'))
@ -119,27 +121,27 @@ class NoteTableData extends DataClass implements Insertable<NoteTableData> {
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
(other is NoteTableData && (other is PersistentNoteData &&
other.id == this.id && other.id == this.id &&
other.content == this.content); other.content == this.content);
} }
class NoteTableCompanion extends UpdateCompanion<NoteTableData> { class PersistentNoteCompanion extends UpdateCompanion<PersistentNoteData> {
final Value<String> id; final Value<String> id;
final Value<String> content; final Value<String> content;
final Value<int> rowid; final Value<int> rowid;
const NoteTableCompanion({ const PersistentNoteCompanion({
this.id = const Value.absent(), this.id = const Value.absent(),
this.content = const Value.absent(), this.content = const Value.absent(),
this.rowid = const Value.absent(), this.rowid = const Value.absent(),
}); });
NoteTableCompanion.insert({ PersistentNoteCompanion.insert({
required String id, required String id,
required String content, required String content,
this.rowid = const Value.absent(), this.rowid = const Value.absent(),
}) : id = Value(id), }) : id = Value(id),
content = Value(content); content = Value(content);
static Insertable<NoteTableData> custom({ static Insertable<PersistentNoteData> custom({
Expression<String>? id, Expression<String>? id,
Expression<String>? content, Expression<String>? content,
Expression<int>? rowid, Expression<int>? rowid,
@ -151,9 +153,9 @@ class NoteTableCompanion extends UpdateCompanion<NoteTableData> {
}); });
} }
NoteTableCompanion copyWith( PersistentNoteCompanion copyWith(
{Value<String>? id, Value<String>? content, Value<int>? rowid}) { {Value<String>? id, Value<String>? content, Value<int>? rowid}) {
return NoteTableCompanion( return PersistentNoteCompanion(
id: id ?? this.id, id: id ?? this.id,
content: content ?? this.content, content: content ?? this.content,
rowid: rowid ?? this.rowid, rowid: rowid ?? this.rowid,
@ -177,7 +179,7 @@ class NoteTableCompanion extends UpdateCompanion<NoteTableData> {
@override @override
String toString() { String toString() {
return (StringBuffer('NoteTableCompanion(') return (StringBuffer('PersistentNoteCompanion(')
..write('id: $id, ') ..write('id: $id, ')
..write('content: $content, ') ..write('content: $content, ')
..write('rowid: $rowid') ..write('rowid: $rowid')
@ -186,12 +188,170 @@ class NoteTableCompanion extends UpdateCompanion<NoteTableData> {
} }
} }
class $PersistentThemeTable extends PersistentTheme
with TableInfo<$PersistentThemeTable, PersistentThemeData> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$PersistentThemeTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _brightnessMeta =
const VerificationMeta('brightness');
@override
late final GeneratedColumn<String> brightness = GeneratedColumn<String>(
'brightness', aliasedName, false,
type: DriftSqlType.string,
requiredDuringInsert: false,
defaultValue: const Constant("dark"));
@override
List<GeneratedColumn> get $columns => [brightness];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'persistent_theme';
@override
VerificationContext validateIntegrity(
Insertable<PersistentThemeData> instance,
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('brightness')) {
context.handle(
_brightnessMeta,
brightness.isAcceptableOrUnknown(
data['brightness']!, _brightnessMeta));
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => const {};
@override
PersistentThemeData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return PersistentThemeData(
brightness: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}brightness'])!,
);
}
@override
$PersistentThemeTable createAlias(String alias) {
return $PersistentThemeTable(attachedDatabase, alias);
}
}
class PersistentThemeData extends DataClass
implements Insertable<PersistentThemeData> {
final String brightness;
const PersistentThemeData({required this.brightness});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['brightness'] = Variable<String>(brightness);
return map;
}
PersistentThemeCompanion toCompanion(bool nullToAbsent) {
return PersistentThemeCompanion(
brightness: Value(brightness),
);
}
factory PersistentThemeData.fromJson(Map<String, dynamic> json,
{ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return PersistentThemeData(
brightness: serializer.fromJson<String>(json['brightness']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'brightness': serializer.toJson<String>(brightness),
};
}
PersistentThemeData copyWith({String? brightness}) => PersistentThemeData(
brightness: brightness ?? this.brightness,
);
@override
String toString() {
return (StringBuffer('PersistentThemeData(')
..write('brightness: $brightness')
..write(')'))
.toString();
}
@override
int get hashCode => brightness.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is PersistentThemeData && other.brightness == this.brightness);
}
class PersistentThemeCompanion extends UpdateCompanion<PersistentThemeData> {
final Value<String> brightness;
final Value<int> rowid;
const PersistentThemeCompanion({
this.brightness = const Value.absent(),
this.rowid = const Value.absent(),
});
PersistentThemeCompanion.insert({
this.brightness = const Value.absent(),
this.rowid = const Value.absent(),
});
static Insertable<PersistentThemeData> custom({
Expression<String>? brightness,
Expression<int>? rowid,
}) {
return RawValuesInsertable({
if (brightness != null) 'brightness': brightness,
if (rowid != null) 'rowid': rowid,
});
}
PersistentThemeCompanion copyWith(
{Value<String>? brightness, Value<int>? rowid}) {
return PersistentThemeCompanion(
brightness: brightness ?? this.brightness,
rowid: rowid ?? this.rowid,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (brightness.present) {
map['brightness'] = Variable<String>(brightness.value);
}
if (rowid.present) {
map['rowid'] = Variable<int>(rowid.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('PersistentThemeCompanion(')
..write('brightness: $brightness, ')
..write('rowid: $rowid')
..write(')'))
.toString();
}
}
abstract class _$AppDatabase extends GeneratedDatabase { abstract class _$AppDatabase extends GeneratedDatabase {
_$AppDatabase(QueryExecutor e) : super(e); _$AppDatabase(QueryExecutor e) : super(e);
late final $NoteTableTable noteTable = $NoteTableTable(this); late final $PersistentNoteTable persistentNote = $PersistentNoteTable(this);
late final $PersistentThemeTable persistentTheme =
$PersistentThemeTable(this);
@override @override
Iterable<TableInfo<Table, Object?>> get allTables => Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>(); allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@override @override
List<DatabaseSchemaEntity> get allSchemaEntities => [noteTable]; List<DatabaseSchemaEntity> get allSchemaEntities =>
[persistentNote, persistentTheme];
} }

View File

@ -1,25 +1,31 @@
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:fnotes/notes_app.dart'; import 'package:fnotes/notes_app.dart';
import 'package:fnotes/persistence_bloc.dart'; import 'package:fnotes/persisted_brightness.dart';
import 'package:fnotes/persistent_notes_bloc.dart';
import 'package:fnotes/theme_bloc.dart'; import 'package:fnotes/theme_bloc.dart';
void main() { void main() async {
WidgetsFlutterBinding.ensureInitialized(); //for drift WidgetsFlutterBinding.ensureInitialized(); //for drift
runApp(const MainApp());
PersistedBrightness persistedBrightness =
await PersistedThemeBloc.getPersistedBrightness();
runApp(MainApp(brightness: persistedBrightness.toFlutterBrightness()));
} }
class MainApp extends StatelessWidget { class MainApp extends StatelessWidget {
const MainApp({super.key}); final Brightness brightness;
const MainApp({super.key, required this.brightness});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MultiBlocProvider( return MultiBlocProvider(
providers: [ providers: [
BlocProvider(create: (context) => ThemeBloc()), BlocProvider(create: (context) => PersistedThemeBloc(brightness)),
BlocProvider(create: (context) => PersistenceBloc()) BlocProvider(create: (context) => PersistentNotesBloc()),
], ],
child: BlocBuilder<ThemeBloc, ThemeState>( child: BlocBuilder<PersistedThemeBloc, ThemeState>(
builder: (context, state) { builder: (context, state) {
return MaterialApp( return MaterialApp(
theme: state.theme, theme: state.theme,

View File

@ -1,7 +1,7 @@
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:fnotes/note.dart'; import 'package:fnotes/note.dart';
import 'package:fnotes/persistence_bloc.dart'; import 'package:fnotes/persistent_notes_bloc.dart';
import 'package:fnotes/theme_bloc.dart'; import 'package:fnotes/theme_bloc.dart';
class NotesApp extends StatefulWidget { class NotesApp extends StatefulWidget {
@ -18,7 +18,7 @@ class _NotesAppState extends State<NotesApp> {
@override @override
void initState() { void initState() {
context.read<PersistenceBloc>().add(LoadNotesEvent()); context.read<PersistentNotesBloc>().add(LoadNotesEvent());
super.initState(); super.initState();
} }
@ -31,29 +31,29 @@ class _NotesAppState extends State<NotesApp> {
IconButton( IconButton(
icon: const Icon(Icons.light_mode), icon: const Icon(Icons.light_mode),
onPressed: () { onPressed: () {
context.read<ThemeBloc>().add(ThemeEvent()); context.read<PersistedThemeBloc>().add(ThemeChangedEvent());
}, },
) )
], ],
), ),
body: BlocBuilder<ThemeBloc, ThemeState>( body: BlocBuilder<PersistedThemeBloc, ThemeState>(
builder: (context, state) { builder: (context, themeState) {
return BlocBuilder<PersistenceBloc, PersistenceState>( return BlocBuilder<PersistentNotesBloc, PersistentNotesState>(
builder: (context, blocState) { builder: (context, notesState) {
return ListView.builder( return ListView.builder(
itemCount: blocState.notes.length, itemCount: notesState.notes.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return Dismissible( return Dismissible(
onDismissed: (direction) { onDismissed: (direction) {
context.read<PersistenceBloc>().add(NoteDismissed( context.read<PersistentNotesBloc>().add(NoteDismissed(
Note.withId( Note.withId(
id: blocState.notes[index].id, id: notesState.notes[index].id,
content: blocState.notes[index].content))); content: notesState.notes[index].content)));
}, },
key: ValueKey(blocState.notes[index]), key: ValueKey(notesState.notes[index]),
child: Card( child: Card(
elevation: 5, elevation: 5,
color: state.theme.colorScheme.primaryContainer, color: themeState.theme.colorScheme.primaryContainer,
child: SizedBox( child: SizedBox(
height: 50, height: 50,
child: Padding( child: Padding(
@ -61,10 +61,10 @@ class _NotesAppState extends State<NotesApp> {
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
blocState.notes[index].content, notesState.notes[index].content,
style: TextStyle( style: TextStyle(
color: color: themeState
state.theme.colorScheme.onPrimaryContainer), .theme.colorScheme.onPrimaryContainer),
), ),
), ),
), ),
@ -83,7 +83,7 @@ class _NotesAppState extends State<NotesApp> {
(value) { (value) {
if (value != null && value.isNotEmpty) { if (value != null && value.isNotEmpty) {
context context
.read<PersistenceBloc>() .read<PersistentNotesBloc>()
.add(NoteEntered(Note(content: value))); .add(NoteEntered(Note(content: value)));
} }
}, },

View File

@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
class PersistedBrightness {
final String brightness;
PersistedBrightness(this.brightness);
factory PersistedBrightness.fromFlutterBrightness(Brightness brightness) {
var persistedBrightness = "light";
if (brightness == Brightness.dark) {
persistedBrightness = "dark";
}
return PersistedBrightness(persistedBrightness);
}
Brightness toFlutterBrightness() {
Brightness flutterBrightness = Brightness.light;
if (brightness == "dark") {
flutterBrightness = Brightness.dark;
}
return flutterBrightness;
}
@override
String toString() => brightness;
PersistedBrightness toOpposite() {
var newBrightness = "light";
if (brightness == "light") {
newBrightness = "dark";
}
return PersistedBrightness(newBrightness);
}
}

View File

@ -1,79 +0,0 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fnotes/database.dart';
import 'package:fnotes/note.dart';
class PersistenceBloc extends Bloc<PersistenceEvent, PersistenceState> {
static final database = AppDatabase();
PersistenceBloc() : super(PersistenceState.init()) {
on<StoreNotesEvent>(storeAllNotes);
on<LoadNotesEvent>(loadAllNotes);
on<NoteEntered>(storeNote);
on<NoteDismissed>(deleteNote);
}
void storeAllNotes(PersistenceEvent event, Emitter<PersistenceState> emit) {}
void loadAllNotes(
PersistenceEvent event, Emitter<PersistenceState> emit) async {
List<Note> list = [];
await database.select(database.noteTable).get().then(
(value) {
list = value.map((row) {
return Note.withId(id: row.id, content: row.content);
}).toList();
},
);
emit(PersistenceState(notes: list));
}
void storeNote(NoteEntered event, Emitter<PersistenceState> emit) async {
await database.into(database.noteTable).insert(NoteTableCompanion.insert(
id: event.note.id,
content: event.note.content,
));
var newNotes = state.notes;
newNotes.add(Note.withId(id: event.note.id, content: event.note.content));
emit(PersistenceState(notes: newNotes));
}
void deleteNote(NoteDismissed event, Emitter<PersistenceState> emit) {
(database.delete(database.noteTable)
..where((tbl) => tbl.id.equals(event.note.id)))
.go();
var newNotes =
state.notes.where((note) => note.id != event.note.id).toList();
emit(PersistenceState(notes: newNotes));
}
}
class PersistenceEvent {}
class LoadNotesEvent extends PersistenceEvent {}
class StoreNotesEvent extends PersistenceEvent {}
class NoteEntered extends PersistenceEvent {
final Note note;
NoteEntered(this.note);
}
class NoteDismissed extends PersistenceEvent {
final Note note;
NoteDismissed(this.note);
}
class PersistenceState {
List<Note> notes = List.empty(growable: true);
PersistenceState({required this.notes});
factory PersistenceState.init() {
return PersistenceState(notes: []);
}
}

View File

@ -0,0 +1,78 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fnotes/database.dart';
import 'package:fnotes/note.dart';
class PersistentNotesBloc
extends Bloc<PersistentNotesEvent, PersistentNotesState> {
PersistentNotesBloc() : super(PersistentNotesState.init()) {
on<LoadNotesEvent>(loadAllNotes);
on<NoteEntered>(storeNote);
on<NoteDismissed>(deleteNote);
}
void storeAllNotes(
PersistentNotesEvent event, Emitter<PersistentNotesState> emit) {}
void loadAllNotes(
PersistentNotesEvent event, Emitter<PersistentNotesState> emit) async {
List<Note> list = [];
await database.select(database.persistentNote).get().then(
(value) {
list = value.map((row) {
return Note.withId(id: row.id, content: row.content);
}).toList();
},
);
emit(PersistentNotesState(notes: list));
}
void storeNote(NoteEntered event, Emitter<PersistentNotesState> emit) async {
await database
.into(database.persistentNote)
.insert(PersistentNoteCompanion.insert(
id: event.note.id,
content: event.note.content,
));
var newNotes = state.notes;
newNotes.add(Note.withId(id: event.note.id, content: event.note.content));
emit(PersistentNotesState(notes: newNotes));
}
void deleteNote(NoteDismissed event, Emitter<PersistentNotesState> emit) {
(database.delete(database.persistentNote)
..where((tbl) => tbl.id.equals(event.note.id)))
.go();
var newNotes =
state.notes.where((note) => note.id != event.note.id).toList();
emit(PersistentNotesState(notes: newNotes));
}
}
class PersistentNotesEvent {}
class LoadNotesEvent extends PersistentNotesEvent {}
class NoteEntered extends PersistentNotesEvent {
final Note note;
NoteEntered(this.note);
}
class NoteDismissed extends PersistentNotesEvent {
final Note note;
NoteDismissed(this.note);
}
class PersistentNotesState {
List<Note> notes = List.empty(growable: true);
PersistentNotesState({required this.notes});
factory PersistentNotesState.init() {
return PersistentNotesState(notes: []);
}
}

View File

@ -1,33 +1,78 @@
import 'package:drift/drift.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:fnotes/database.dart';
import 'package:fnotes/persisted_brightness.dart';
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> { class PersistedThemeBloc extends Bloc<ThemeEvent, ThemeState> {
ThemeBloc() : super(ThemeState.init()) { PersistedThemeBloc(Brightness brightness)
on<ThemeEvent>(switchTheme); : super(ThemeState.withBrightness(brightness)) {
on<ThemeChangedEvent>(switchTheme);
on<LoadThemeEvent>(loadTheme);
} }
void switchTheme(ThemeEvent event, Emitter<ThemeState> emit) { void switchTheme(ThemeChangedEvent event, Emitter<ThemeState> emit) async {
if (state.theme.brightness == Brightness.light) { await database.delete(database.persistentTheme).go();
emit(ThemeState.withBrightness(Brightness.dark));
} else { var newBrightness =
emit(ThemeState.withBrightness(Brightness.light)); PersistedBrightness.fromFlutterBrightness(state.theme.brightness)
.toOpposite();
await database
.into(database.persistentTheme)
.insert(PersistentThemeCompanion.insert(
brightness: Value(newBrightness.toString()),
));
emit(ThemeState.withBrightness(newBrightness.toFlutterBrightness()));
} }
void loadTheme(LoadThemeEvent event, Emitter<ThemeState> emit) async {
PersistedBrightness persistedBrightness = await getPersistedBrightness();
emit(ThemeState.withBrightness(persistedBrightness.toFlutterBrightness()));
}
static Future<PersistedBrightness> getPersistedBrightness() async {
PersistedBrightness persistedBrightness = PersistedBrightness("light");
await database.select(database.persistentTheme).get().then(
(value) {
var brightnessIter = value.map((row) {
return row.brightness;
});
if (brightnessIter.isNotEmpty) {
persistedBrightness = PersistedBrightness(brightnessIter.first);
}
},
);
return persistedBrightness;
} }
} }
class ThemeEvent {} class ThemeEvent {}
class LoadThemeEvent extends ThemeEvent {}
class ThemeChangedEvent extends ThemeEvent {}
class ThemeState { class ThemeState {
final ThemeData theme; final ThemeData theme;
static final ThemeData initTheme = ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.lightBlue, brightness: Brightness.light),
);
ThemeState({required this.theme}); ThemeState({required this.theme});
factory ThemeState.init() { static Future<ThemeState> fetchAndConstructInitState() async {
return ThemeState(theme: initTheme); PersistedBrightness persistedBrightness = PersistedBrightness('light');
await database.select(database.persistentTheme).get().then(
(value) {
var brightnessIter = value.map((row) {
return row.brightness;
});
persistedBrightness = PersistedBrightness(brightnessIter.first);
},
);
return ThemeState.withBrightness(persistedBrightness.toFlutterBrightness());
} }
factory ThemeState.withBrightness(Brightness brightness) { factory ThemeState.withBrightness(Brightness brightness) {

View File

@ -513,7 +513,7 @@ packages:
source: hosted source: hosted
version: "1.5.1" version: "1.5.1"
provider: provider:
dependency: transitive dependency: "direct main"
description: description:
name: provider name: provider
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c

View File

@ -17,6 +17,7 @@ dependencies:
sqlite3_flutter_libs: ^0.5.20 sqlite3_flutter_libs: ^0.5.20
uuid: ^4.3.3 uuid: ^4.3.3
sqlite3: ^2.4.0 sqlite3: ^2.4.0
provider: ^6.1.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: