State Management with Providers
State Management with Provider
Freak-Flix utilizes the Provider package for state management. This reactive architecture ensures that UI components automatically update when underlying data changes, such as when a library scan completes or a user logs in.
State is divided into functional domains to maintain a clean separation of concerns and optimize rebuild performance.
UserProvider
The UserProvider manages authentication states, user profile information, and secure session tokens. It interfaces with the backend auth services (Node.js/Hono or Netlify Functions).
Public Interface:
| Method / Property | Type | Description |
| :--- | :--- | :--- |
| user | User? | The current authenticated user object. |
| isAuthenticated | bool | Returns true if a valid JWT is present. |
| login(email, password) | Future<void> | Authenticates a user and persists the session. |
| register(email, password)| Future<void> | Creates a new account and logs the user in. |
| logout() | Future<void> | Clears local session data and resets the state. |
Usage Example:
final userProvider = Provider.of<UserProvider>(context, listen: false);
// Trigger a login
await userProvider.login('user@example.com', 'securePassword123');
// Access user data in UI
Text('Welcome, ${userProvider.user?.email}');
LibraryProvider
The LibraryProvider is the core of the media experience. It manages the collection of Movies, TV Shows, Anime, and Adult content, including metadata fetched from TMDB, AniList, and StashDB.
Key Features:
- Recursive Scanning: Tracks the status of local and cloud (OneDrive) folder scans.
- Categorization: Automatically filters media into distinct types.
- Metadata Sync: Coordinates the enrichment of raw file data with rich internet metadata.
Public Interface:
| Method / Property | Type | Description |
| :--- | :--- | :--- |
| movies | List<Media> | List of all movie items in the library. |
| isScanning | bool | Whether a library update is currently in progress. |
| triggerScan(folderPath) | Future<void> | Initiates a scan of the specified directory. |
| refreshMetadata(itemId) | Future<void> | Forces a re-fetch of metadata for a specific item. |
Usage Example:
Consumer<LibraryProvider>(
builder: (context, library, child) {
if (library.isScanning) return CircularProgressIndicator();
return GridView.builder(
itemCount: library.movies.length,
itemBuilder: (context, index) => MediaCard(library.movies[index]),
);
},
);
SettingsProvider
This provider handles app configuration, API keys, and UI preferences. It ensures that sensitive keys (like TMDB or StashDB API keys) are available globally for service requests.
Configuration State:
- API Credentials: Storage for TMDB, StashDB, and Azure App IDs.
- Privacy Settings: Toggles for "Adult Content" visibility.
- Theme Preferences: Management of dark/light mode and immersive UI settings.
Usage Example:
final settings = context.read<SettingsProvider>();
// Check if adult content should be displayed
if (settings.showAdultContent) {
renderAdultCategory();
}
// Update TMDB Key
settings.updateTmdbKey('your_api_key_here');
PlaybackProvider
The PlaybackProvider tracks active playback sessions. It acts as a bridge between the media_kit (or mpv) controller and the UI, managing play/pause states, scrubbing, and "Continue Watching" progress.
Public Interface:
| Property | Type | Description |
| :--- | :--- | :--- |
| currentMedia | Media? | The item currently being played. |
| playbackPosition | Duration | The current timestamp in the active stream. |
| isPlaying | bool | The current status of the video engine. |
Usage Example:
// Resume playback from saved state
final playback = context.read<PlaybackProvider>();
playback.play(mediaItem, startPosition: mediaItem.lastWatchedPosition);
Best Practices for Developers
- Selective Listening: Use
context.select<T, R>when you only need a specific property from a provider to prevent unnecessary widget rebuilds. - Logic Separation: Business logic (e.g., how to parse a file name for metadata) should remain in the Provider; UI should only call public methods.
- Initialization: Most providers are initialized at the root of the app in
main.dartusing aMultiProviderblock to ensure availability across all routes.