From aae7e6da6e97c4ecc94576b696f7b36bff6bf6a4 Mon Sep 17 00:00:00 2001 From: Fodor Benedek Date: Mon, 28 Oct 2024 23:54:01 +0100 Subject: [PATCH] key filter ui and service --- .gitignore | 3 +- lib/data/database.dart | 29 +++- lib/data/song/song.dart | 18 ++- lib/services/key/filter.dart | 73 ++++++++- lib/services/key/select_distinct.drift | 26 +++ lib/services/songs/filter.dart | 18 ++- lib/services/songs/filter.drift | 2 +- lib/ui/base/songs/filter/general/state.dart | 3 +- .../songs/filter/general/widgets/filters.dart | 75 +++++---- lib/ui/base/songs/filter/key/state.dart | 54 ++++++- lib/ui/base/songs/filter/key/widget.dart | 148 +++++++++++++++++- lib/ui/base/songs/page.dart | 44 ++++-- pubspec.lock | 2 +- pubspec.yaml | 1 + 14 files changed, 425 insertions(+), 71 deletions(-) create mode 100644 lib/services/key/select_distinct.drift diff --git a/.gitignore b/.gitignore index cf2ed6f..6d5224f 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,5 @@ app.*.map.json # Ignore generated files (good practice to keep them out of version control) *.freezed.dart -*.g.dart \ No newline at end of file +*.g.dart +CONTINUE.txt diff --git a/lib/data/database.dart b/lib/data/database.dart index c82cd51..c3ec2d8 100644 --- a/lib/data/database.dart +++ b/lib/data/database.dart @@ -1,7 +1,11 @@ import 'dart:io'; import 'package:drift/drift.dart'; -import 'package:drift_flutter/drift_flutter.dart'; +import 'package:drift/native.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:sqlite3/sqlite3.dart'; +import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart'; +import 'package:path/path.dart' as p; import 'bank/bank.dart'; import 'song/song.dart'; @@ -19,6 +23,7 @@ late final Directory dataDir; include: { 'song/song.drift', '../services/songs/filter.drift', + '../services/key/select_distinct.drift', }, ) class LyricDatabase extends _$LyricDatabase { @@ -27,10 +32,6 @@ class LyricDatabase extends _$LyricDatabase { @override int get schemaVersion => 1; - static QueryExecutor _openConnection() { - return driftDatabase(name: 'lyric'); - } - @override MigrationStrategy get migration { return MigrationStrategy(onCreate: (Migrator m) async { @@ -42,6 +43,24 @@ class LyricDatabase extends _$LyricDatabase { } } +// see https://drift.simonbinder.eu/setup/#database-class +// implemented for logStatemets capability +LazyDatabase _openConnection() { + return LazyDatabase(() async { + final dbFolder = await getApplicationDocumentsDirectory(); + final file = File(p.join(dbFolder.path, 'lyric.sqlite')); + + if (Platform.isAndroid) { + await applyWorkaroundToOpenSqlite3OnOldAndroidVersions(); + } + + final cachebase = (await getTemporaryDirectory()).path; + sqlite3.tempDirectory = cachebase; + + return NativeDatabase.createInBackground(file, logStatements: true); + }); +} + class UriConverter extends TypeConverter { const UriConverter(); diff --git a/lib/data/song/song.dart b/lib/data/song/song.dart index f2800d6..f1eb2bf 100644 --- a/lib/data/song/song.dart +++ b/lib/data/song/song.dart @@ -118,10 +118,10 @@ class KeyFieldConverter extends TypeConverter { } class KeyField { - final String key; - final String scale; + final String pitch; + final String mode; - KeyField(this.key, this.scale); + KeyField(this.pitch, this.mode); static KeyField? fromString(String? value) { if (value == null || value.isEmpty) return null; @@ -135,6 +135,16 @@ class KeyField { @override String toString() { - return '$key-$scale'; + return '$pitch-$mode'; } + + @override + bool operator ==(Object other) { + if (other is! KeyField) return false; + if (pitch == other.pitch && mode == other.mode) return true; + return false; + } + + @override + int get hashCode => Object.hash(pitch, mode); } diff --git a/lib/services/key/filter.dart b/lib/services/key/filter.dart index c09a527..672f147 100644 --- a/lib/services/key/filter.dart +++ b/lib/services/key/filter.dart @@ -1,12 +1,77 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:lyric/data/database.dart'; +import 'package:lyric/data/song/song.dart'; +import 'package:lyric/ui/base/songs/filter/key/state.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +part 'filter.g.dart'; + +typedef KeyFilterSelectable = ({String label, Function(bool) onSelected, bool selected, bool addingKey}); + @Riverpod(keepAlive: true) -Future> selectableKeyRootsFor(Ref ref, {String? mode}) { - throw UnimplementedError(); +Stream> selectablePitches(Ref ref) { + final state = ref.watch(keyFilterStateProvider); + if (state.modes.length == 1 && state.pitches.isEmpty) { + return db.selectDistinctPitches(state.modes.first).watch().map((pitches) { + List selectables = []; + for (var e in pitches) { + final key = KeyField(e, state.modes.first); + if (state.keys.contains(key)) continue; + selectables.add(( + label: key.toString(), + onSelected: (v) => ref.read(keyFilterStateProvider.notifier).setKeyTo(key, v), + selected: state.keys.contains(key), + addingKey: true, + )); + } + return selectables; + }); + } else { + return db.selectDistinctPitches('%').watch().map((pitches) { + return pitches + .map( + (e) => ( + label: e, + onSelected: (v) => ref.read(keyFilterStateProvider.notifier).setPitchTo(e, v), + selected: state.pitches.contains(e), + addingKey: false, + ), + ) + .toList(); + }); + } } @Riverpod(keepAlive: true) -Future> selectableKeyModesFor(Ref ref, {String? root}) { - throw UnimplementedError(); +Stream> selectableModes(Ref ref) { + final state = ref.watch(keyFilterStateProvider); + if (state.pitches.length == 1 && state.modes.isEmpty) { + return db.selectDistinctModes(state.pitches.first).watch().map((modes) { + List selectables = []; + for (var e in modes) { + final key = KeyField(state.pitches.first, e); + if (state.keys.contains(key)) continue; + selectables.add(( + label: key.toString(), + onSelected: (v) => ref.read(keyFilterStateProvider.notifier).setKeyTo(key, v), + selected: state.keys.contains(key), + addingKey: true, + )); + } + return selectables; + }); + } else { + return db.selectDistinctModes('%').watch().map((modes) { + return modes + .map( + (e) => ( + label: e, + onSelected: (v) => ref.read(keyFilterStateProvider.notifier).setModeTo(e, v), + selected: state.modes.contains(e), + addingKey: false, + ), + ) + .toList(); + }); + } } diff --git a/lib/services/key/select_distinct.drift b/lib/services/key/select_distinct.drift new file mode 100644 index 0000000..a7129f3 --- /dev/null +++ b/lib/services/key/select_distinct.drift @@ -0,0 +1,26 @@ +import '../../data/song/song.drift'; +import '../../data/song/song.dart'; + +selectDistinctKeys: + SELECT DISTINCT + key_field + FROM songs; + +selectDistinctPitches(:for_mode AS TEXT): + SELECT DISTINCT + substr( + key_field + ,1 + ,instr(key_field, '-') - 1 + ) + FROM songs + WHERE key_field != '' AND key_field LIKE concat('%-', :for_mode); + +selectDistinctModes(:for_pitch AS TEXT): + SELECT DISTINCT + substr( + key_field + ,instr(key_field, '-') + 1 + ) + FROM songs + WHERE key_field != '' AND key_field LIKE concat(:for_pitch, '-%'); \ No newline at end of file diff --git a/lib/services/songs/filter.dart b/lib/services/songs/filter.dart index c9c5693..059b43f 100644 --- a/lib/services/songs/filter.dart +++ b/lib/services/songs/filter.dart @@ -1,6 +1,7 @@ import 'package:drift/drift.dart'; import 'package:drift/extensions/json1.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:lyric/ui/base/songs/filter/key/state.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../data/database.dart'; @@ -72,6 +73,7 @@ Stream> filteredSongs(Ref ref) { final String searchString = sanitize(ref.watch(searchStringStateProvider)); final List searchFields = ref.watch(searchFieldsStateProvider); final Map> filters = ref.watch(filterStateProvider); + final KeyFilters keyFilters = ref.watch(keyFilterStateProvider); String ftsMatchString = '{${searchFields.join(' ')}} : $searchString'; print(ftsMatchString); @@ -84,7 +86,19 @@ Stream> filteredSongs(Ref ref) { entry.value.map((value) => fieldData.like('%$value%')), ); }, - )); + ).followedBy([ + Expression.or([ + if (keyFilters.pitches.isNotEmpty || keyFilters.modes.isNotEmpty) + Expression.and([ + if (keyFilters.pitches.isNotEmpty) + Expression.or(keyFilters.pitches.map((e) => songsFts.keyField.like('$e-%'))), + if (keyFilters.modes.isNotEmpty) + Expression.or(keyFilters.modes.map((e) => songsFts.keyField.like('%-$e'))), + ]), + if (keyFilters.keys.isNotEmpty) + Expression.or(keyFilters.keys.map((e) => songsFts.keyField.equals(e.toString()))), + ], ifEmpty: Constant(true)) + ])); } if (searchString.isEmpty) { @@ -92,7 +106,7 @@ Stream> filteredSongs(Ref ref) { (songsFtList) => songsFtList.map((songsFt) => SongResult(songsFt: songsFt)).toList(), ); } else { - return (db.song_fulltext_search(ftsMatchString, (songsFts) => filterExpression(songsFts)).watch()).map( + return (db.songFulltextSearch(ftsMatchString, (songsFts) => filterExpression(songsFts)).watch()).map( (matchList) => matchList.map((match) => SongResult(match: match)).toList(), ); } diff --git a/lib/services/songs/filter.drift b/lib/services/songs/filter.drift index 8ebd5b5..3edf0eb 100644 --- a/lib/services/songs/filter.drift +++ b/lib/services/songs/filter.drift @@ -1,7 +1,7 @@ import '../../data/song/song.drift'; import '../../data/song/song.dart'; -song_fulltext_search(:match_string AS TEXT): +songFulltextSearch(:match_string AS TEXT): SELECT bm25(songs_fts, 0.0, 0.0, 10.0, 0.5, 5.0, 5.0, 2.0, 0.0, 0.0) AS rank ,uuid diff --git a/lib/ui/base/songs/filter/general/state.dart b/lib/ui/base/songs/filter/general/state.dart index ff73b12..22fcfb9 100644 --- a/lib/ui/base/songs/filter/general/state.dart +++ b/lib/ui/base/songs/filter/general/state.dart @@ -67,7 +67,8 @@ const Map> songFieldsMap = { 'type': 'filterable_multiselect', 'icon': Icons.height, }, - 'key': { + 'pitch': { + // todo change back to key 'title_hu': 'Hangnem', 'type': 'filterable_key', 'icon': Icons.music_note, diff --git a/lib/ui/base/songs/filter/general/widgets/filters.dart b/lib/ui/base/songs/filter/general/widgets/filters.dart index 1f6b920..71a78ce 100644 --- a/lib/ui/base/songs/filter/general/widgets/filters.dart +++ b/lib/ui/base/songs/filter/general/widgets/filters.dart @@ -33,7 +33,9 @@ class FiltersColumn extends ConsumerWidget { fieldType: e.value.type, fieldPopulatedCount: e.value.count, ), - FieldType.key => KeyFilterCard(), + FieldType.key => KeyFilterCard( + fieldPopulatedCount: e.value.count, + ), _ => LErrorCard( type: LErrorType.warning, title: 'Nem támogatott szűrőtípus!', @@ -75,11 +77,13 @@ class LFilterChipsState extends ConsumerState { @override Widget build(BuildContext context) { + // todo final var selectableValues = ref.watch(selectableValuesForFilterableFieldProvider(widget.field, widget.fieldType)); var filterState = ref.watch(filterStateProvider); var filterStateNotifier = ref.read(filterStateProvider.notifier); + // todo final bool active() => filterState.containsKey(widget.field); return Card( @@ -116,36 +120,30 @@ class LFilterChipsState extends ConsumerState { subtitle: switch (selectableValues) { AsyncLoading() => LinearProgressIndicator(), AsyncError(:final error) => Text('Hiba a szűrőértékek lekérdezése közben: $error'), - AsyncValue(:final value) => Row( - mainAxisSize: MainAxisSize.min, - children: [ - Expanded( - child: SizedBox( - height: 38, - child: FadingEdgeScrollView.fromScrollView( - child: ListView.builder( - shrinkWrap: true, - controller: _filterChipsRowController, - scrollDirection: Axis.horizontal, - itemCount: value!.length, - itemBuilder: (context, i) { - String item = value[i]; - bool selected = filterState[widget.field]?.contains(item) ?? false; - onSelected(bool newValue) { - if (newValue) { - filterStateNotifier.addFilter(widget.field, item); - } else { - filterStateNotifier.removeFilter(widget.field, item); - } - } - - return LFilterChip(label: item, onSelected: onSelected, selected: selected); - }, - ), - ), - ), - ) - ], + AsyncValue(:final value) => SizedBox( + height: 38, + child: FadingEdgeScrollView.fromScrollView( + child: ListView.builder( + shrinkWrap: true, + controller: _filterChipsRowController, + scrollDirection: Axis.horizontal, + itemCount: value!.length, + itemBuilder: (context, i) { + // todo move all logic to service (like in filter/key/widget.dart) + String item = value[i]; + bool selected = filterState[widget.field]?.contains(item) ?? false; + onSelected(bool newValue) { + if (newValue) { + filterStateNotifier.addFilter(widget.field, item); + } else { + filterStateNotifier.removeFilter(widget.field, item); + } + } + + return LFilterChip(label: item, onSelected: onSelected, selected: selected); + }, + ), + ), ) }, trailing: active() @@ -162,12 +160,16 @@ class LFilterChip extends StatelessWidget { required this.label, required this.onSelected, required this.selected, + this.leading, + this.special = false, super.key, }); final String label; final Function(bool) onSelected; final bool selected; + final bool special; + final Widget? leading; @override Widget build(BuildContext context) { @@ -176,12 +178,19 @@ class LFilterChip extends StatelessWidget { child: FilterChip.elevated( color: WidgetStateProperty.resolveWith((states) { if (!states.contains(WidgetState.selected)) { + if (special) return Theme.of(context).colorScheme.surfaceContainer; return Theme.of(context).cardColor; } else { - return Theme.of(context).focusColor; + return Theme.of(context).colorScheme.surfaceContainerHighest; } }), - label: Text(label), + labelPadding: EdgeInsets.only(left: leading != null ? 0 : 5, right: 5), + label: Row( + children: [ + if (leading != null) Padding(padding: EdgeInsets.only(right: 5), child: leading!), + Text(label), + ], + ), selected: selected, onSelected: onSelected, materialTapTargetSize: MaterialTapTargetSize.padded, diff --git a/lib/ui/base/songs/filter/key/state.dart b/lib/ui/base/songs/filter/key/state.dart index 9026127..f5c494f 100644 --- a/lib/ui/base/songs/filter/key/state.dart +++ b/lib/ui/base/songs/filter/key/state.dart @@ -1,4 +1,50 @@ -enum KeyFilterMode { - completeKeys, - -} \ No newline at end of file +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../../../../../data/song/song.dart'; + +part 'state.g.dart'; + +typedef KeyFilters = ({Set pitches, Set modes, Set keys}); + +@Riverpod(keepAlive: true) +class KeyFilterState extends _$KeyFilterState { + @override + KeyFilters build() { + return (pitches: {}, modes: {}, keys: {}); + } + + void reset() { + state = build(); + } + + bool get isEmpty { + return state.pitches.isEmpty && state.modes.isEmpty && state.keys.isEmpty; + } + + void setPitchTo(String pitch, bool value) { + if (value) { + if (state.pitches.add(pitch)) ref.notifyListeners(); + } else { + if (state.pitches.remove(pitch)) ref.notifyListeners(); + } + } + + void setModeTo(String mode, bool value) { + if (value) { + if (state.modes.add(mode)) ref.notifyListeners(); + } else { + if (state.modes.remove(mode)) ref.notifyListeners(); + } + } + + void setKeyTo(KeyField keyField, bool value) { + if (value) { + state.pitches.remove(keyField.pitch); + state.modes.remove(keyField.mode); + state.keys.add(keyField); + ref.notifyListeners(); + } else { + if (state.keys.remove(keyField)) ref.notifyListeners(); + } + } +} diff --git a/lib/ui/base/songs/filter/key/widget.dart b/lib/ui/base/songs/filter/key/widget.dart index db1559d..03af61d 100644 --- a/lib/ui/base/songs/filter/key/widget.dart +++ b/lib/ui/base/songs/filter/key/widget.dart @@ -1,10 +1,150 @@ +import 'package:fading_edge_scrollview/fading_edge_scrollview.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:lyric/services/key/filter.dart'; +import 'package:lyric/ui/base/songs/filter/general/widgets/filters.dart'; +import 'package:lyric/ui/base/songs/filter/key/state.dart'; +import 'package:lyric/ui/common/error.dart'; -class KeyFilterCard extends StatelessWidget { - const KeyFilterCard({super.key}); +class KeyFilterCard extends ConsumerStatefulWidget { + const KeyFilterCard({ + required this.fieldPopulatedCount, + super.key, + }); + + final int fieldPopulatedCount; + + @override + ConsumerState createState() => _KeyFilterCardState(); +} + +class _KeyFilterCardState extends ConsumerState { + @override + void initState() { + keysScrollController = ScrollController(); + pitchesScrollController = ScrollController(); + modesScrollController = ScrollController(); + super.initState(); + } + + late final ScrollController keysScrollController; + late final ScrollController pitchesScrollController; + late final ScrollController modesScrollController; @override Widget build(BuildContext context) { - return Placeholder(child: Text('Key Filter')); + final state = ref.watch(keyFilterStateProvider); + final isActive = !(state.pitches.isEmpty && state.modes.isEmpty && state.keys.isEmpty); + + final selectablePitches = ref.watch(selectablePitchesProvider); + final selectableModes = ref.watch(selectableModesProvider); + + return Card( + elevation: isActive ? 7 : 0, + color: isActive ? Theme.of(context).colorScheme.secondaryContainer : null, + child: ListTile( + contentPadding: const EdgeInsets.only(left: 15), + leading: Icon(Icons.piano), + trailing: isActive + ? IconButton( + icon: Icon(Icons.clear), + onPressed: () => ref.read(keyFilterStateProvider.notifier).reset(), + ) + : null, + title: AnimatedSize( + duration: Durations.medium1, + child: Stack( + fit: StackFit.passthrough, + children: [ + AnimatedOpacity( + duration: Durations.medium1, + opacity: state.keys.isEmpty ? 1 : 0, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text('Hangnem'), + Expanded( + child: Text( + " ${widget.fieldPopulatedCount} kitöltve", + maxLines: 1, + softWrap: false, + textAlign: TextAlign.end, + overflow: TextOverflow.fade, + style: TextStyle( + fontStyle: FontStyle.italic, + color: Theme.of(context).colorScheme.onSecondaryContainer, + fontSize: Theme.of(context).textTheme.bodySmall!.fontSize, + ), + ), + ), + ], + ), + ), + if (state.keys.isNotEmpty) + SizedBox( + height: 38, + child: FadingEdgeScrollView.fromScrollView( + child: ListView( + controller: keysScrollController, + scrollDirection: Axis.horizontal, + children: state.keys + .map( + (e) => LFilterChip( + label: e.toString(), + onSelected: (v) => ref.read(keyFilterStateProvider.notifier).setKeyTo(e, v), + selected: state.keys.contains(e), + ), + ) + .toList(), + ), + ), + ), + ], + ), + ), + subtitle: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + chipsRow(selectablePitches), + chipsRow(selectableModes, modes: true), + ], + ), + ), + ); + } + + Widget chipsRow(AsyncValue> selectables, {bool modes = false}) { + return switch (selectables) { + AsyncError(:final Error error) => LErrorCard( + type: LErrorType.warning, + title: 'Hiba a hangsoradatok lekérdezésekor', + message: error.toString(), + stack: error.stackTrace.toString(), + icon: Icons.error), + AsyncValue(:final value) => SizedBox( + height: 38, + child: value == null + ? LinearProgressIndicator() + : FadingEdgeScrollView.fromScrollView( + child: ListView.builder( + // hack, but still better than code repetition... + controller: modes ? modesScrollController : pitchesScrollController, + shrinkWrap: true, + scrollDirection: Axis.horizontal, + itemBuilder: (context, i) => LFilterChip( + label: value[i].label, + selected: value[i].selected, + onSelected: value[i].onSelected, + special: value[i].addingKey, + leading: value[i].addingKey ? Icon(Icons.add, size: 20) : null, + ), + itemCount: value.length, + ), + ), + ), + }; } -} \ No newline at end of file +} diff --git a/lib/ui/base/songs/page.dart b/lib/ui/base/songs/page.dart index 51d8e97..a1967f9 100644 --- a/lib/ui/base/songs/page.dart +++ b/lib/ui/base/songs/page.dart @@ -5,6 +5,7 @@ import 'package:lyric/services/songs/filter.dart'; import 'package:lyric/ui/base/songs/filter/general/state.dart'; import 'package:lyric/ui/base/songs/filter/general/widgets/filters.dart'; import 'package:lyric/ui/base/songs/filter/general/widgets/search_field_selector.dart'; +import 'package:lyric/ui/base/songs/filter/key/state.dart'; import 'package:lyric/ui/base/songs/song_tile.dart'; import 'package:lyric/ui/common/error.dart'; @@ -56,6 +57,7 @@ class _SongsPageState extends ConsumerState { Widget build(BuildContext context) { final songResults = ref.watch(filteredSongsProvider); final filterState = ref.watch(filterStateProvider); + final keyFilterState = ref.watch(keyFilterStateProvider); return Stack( children: [ @@ -122,23 +124,43 @@ class _SongsPageState extends ConsumerState { child: Theme( data: Theme.of(context).copyWith(dividerColor: Colors.transparent), child: ExpansionTile( - collapsedBackgroundColor: filterState.isEmpty - ? null - : Theme.of(context).colorScheme.secondaryContainer, - collapsedIconColor: filterState.isEmpty - ? null - : Theme.of(context).colorScheme.onSecondaryContainer, + collapsedBackgroundColor: + (filterState.isEmpty && ref.read(keyFilterStateProvider.notifier).isEmpty) + ? null + : Theme.of(context).colorScheme.secondaryContainer, + collapsedIconColor: + (filterState.isEmpty && ref.read(keyFilterStateProvider.notifier).isEmpty) + ? null + : Theme.of(context).colorScheme.onSecondaryContainer, controller: _filterExpansionTileController, leading: const Icon(Icons.filter_list), title: Text( - filterState.isEmpty + (filterState.isEmpty && ref.read(keyFilterStateProvider.notifier).isEmpty) ? 'Szűrők' - : filterState.values.map((e) => e.join(', ')).join('; '), + : ([ + if (!ref.read(keyFilterStateProvider.notifier).isEmpty) + [ + if (keyFilterState.keys.isNotEmpty) + keyFilterState.keys.map((e) => e.toString()).join(' vagy '), + if (keyFilterState.pitches.isNotEmpty || + keyFilterState.modes.isNotEmpty) + [ + if (keyFilterState.pitches.isNotEmpty) + 'alaphangja ${keyFilterState.pitches.join(' vagy ')}', + if (keyFilterState.modes.isNotEmpty) + 'hangsora ${keyFilterState.modes.join(' vagy ')}' + ].join(' és ') + ].join(', vagy '), + if (filterState.isNotEmpty) + filterState.values.map((e) => e.join(' vagy ')).join(', és '), + ].join(', valamint ')), style: TextStyle( - color: filterState.isEmpty + color: (filterState.isEmpty && + ref.read(keyFilterStateProvider.notifier).isEmpty) ? null : Theme.of(context).colorScheme.onSecondaryContainer, - fontSize: filterState.isEmpty + fontSize: (filterState.isEmpty && + ref.read(keyFilterStateProvider.notifier).isEmpty) ? null : Theme.of(context).textTheme.bodyMedium!.fontSize, ), @@ -177,7 +199,7 @@ class _SongsPageState extends ConsumerState { stack: stackTrace.toString(), ), ), - AsyncLoading(:final value) || AsyncValue(:final value) => value == null + AsyncValue(:final value) => value == null ? const Center( child: CircularProgressIndicator(), ) diff --git a/pubspec.lock b/pubspec.lock index b6cb88f..3162fb6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -734,7 +734,7 @@ packages: source: hosted version: "2.4.7" sqlite3_flutter_libs: - dependency: transitive + dependency: "direct main" description: name: sqlite3_flutter_libs sha256: "7ae52b23366e5295005022e62fa093f64bfe190810223ea0ebf733a4cd140bce" diff --git a/pubspec.yaml b/pubspec.yaml index 08fb70d..bfba4b3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,6 +27,7 @@ dependencies: queue: ^3.1.0+2 drift: ^2.20.3 drift_flutter: ^0.2.1 + sqlite3_flutter_libs: ^0.5.26 dev_dependencies: flutter_lints: ^5.0.0