147d6e280e
Major version bumps (riverpod 2->3, go_router 14->17, just_audio 0.9->0.10, flutter_secure_storage 9->10, google_fonts 6->8, flutter_lints 4->6). package_info_plus held at 8.x due to win32 conflict with secure_storage 10.x. Riverpod 3 breaks: AsyncValue.valueOrNull -> .value, StateProvider replaced with Notifier subclass. just_audio 0.10: ConcatenatingAudioSource -> setAudioSources. Search slice: - models/page.dart (generic Page<T> envelope) - models/search_response.dart (3-facet wrapper) - api/endpoints/search.dart - search/search_provider.dart (debounced 250ms) - search/search_screen.dart (TextField + 3 horizontal/vertical sections) - Wired /search route + search button on home AppBar
38 lines
1.5 KiB
Dart
38 lines
1.5 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../api/endpoints/search.dart';
|
|
import '../library/library_providers.dart' show dioProvider;
|
|
import '../models/search_response.dart';
|
|
|
|
final searchApiProvider = FutureProvider<SearchApi>((ref) async {
|
|
return SearchApi(await ref.watch(dioProvider.future));
|
|
});
|
|
|
|
/// Current search query text. The screen's TextField writes here on
|
|
/// every keystroke; the resultsProvider below debounces.
|
|
class SearchQueryNotifier extends Notifier<String> {
|
|
@override
|
|
String build() => '';
|
|
void set(String q) => state = q;
|
|
}
|
|
|
|
final searchQueryProvider =
|
|
NotifierProvider<SearchQueryNotifier, String>(SearchQueryNotifier.new);
|
|
|
|
/// Debounced search results. Returns null for empty queries so the
|
|
/// screen can show a neutral empty state instead of "0 results."
|
|
///
|
|
/// Debounce: 250ms. After the wait, re-reads the live query — if the
|
|
/// user has typed more in that window, this attempt is silently
|
|
/// abandoned (returns null) and a newer attempt fires on the next
|
|
/// rebuild. No request hits the server while the user is mid-typing.
|
|
final searchResultsProvider = FutureProvider<SearchResponse?>((ref) async {
|
|
final q = ref.watch(searchQueryProvider).trim();
|
|
if (q.isEmpty) return null;
|
|
await Future<void>.delayed(const Duration(milliseconds: 250));
|
|
// Re-read the (possibly newer) query; if the user typed more, bail.
|
|
if (ref.read(searchQueryProvider).trim() != q) return null;
|
|
final api = await ref.watch(searchApiProvider.future);
|
|
return api.search(q);
|
|
});
|