summaryrefslogtreecommitdiff
path: root/player-android/lib/providers/audio_handler_provider.dart
blob: d3ea3075a05d096fc52ba5c96bcd640187f8e451 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../services/audio_handler.dart';

// ---------------------------------------------------------------------------
// audioHandlerProvider
// ---------------------------------------------------------------------------

/// Provides the [PlayerAudioHandler] singleton that [AudioService.init]
/// registered at app startup.
///
/// This provider has **no default implementation** — it must be overridden at
/// the composition root ([ProviderScope] in [main]) with the instance returned
/// by [AudioService.init].  Attempting to read it without an override throws a
/// [StateError] so misconfiguration is caught immediately.
///
///   - Using a [ProviderScope] override (rather than reading a global mutable
///     variable from `main.dart`) follows the Dependency Inversion Principle:
///     the provider file does not depend on `main.dart`, and the wiring is
///     explicit and visible in the composition root.
///   - Tests can supply a fake handler by overriding this provider in the
///     test's [ProviderScope], without touching the production entry point.
///
/// Usage in widgets:
/// ```dart
/// final handler = ref.read(audioHandlerProvider);
/// await handler.play();
/// ```
///
/// Composition root wiring (see `main.dart`):
/// ```dart
/// final handler = await AudioService.init<PlayerAudioHandler>(...);
/// runApp(ProviderScope(
///   overrides: [audioHandlerProvider.overrideWithValue(handler)],
///   child: const PlayerAndroidApp(),
/// ));
/// ```
final audioHandlerProvider = Provider<PlayerAudioHandler>(
  (_) => throw StateError(
    'audioHandlerProvider has no value: override it with the '
    'PlayerAudioHandler instance from AudioService.init() in ProviderScope.',
  ),
);