Overview
Invenicum uses the Provider package for state management, implementing the ChangeNotifier pattern. This approach provides:- Reactive UI updates
- Clear separation of business logic and presentation
- Dependency injection
- Testability
Provider Hierarchy
The complete provider tree is established inlib/main.dart:64-261:
Two-Tier Structure
Tier 1: Services (Provider)
Services are stateless singletons that handle API communication:- No reactive state (don’t call
notifyListeners()) - Injected via constructor dependency
- Single instance shared across the app
Tier 2: State Providers (ChangeNotifier)
Providers manage reactive state:- Extend
ChangeNotifier - Call
notifyListeners()to trigger UI rebuilds - Can depend on other providers (via ProxyProvider)
Provider Types
1. ChangeNotifierProvider
Used for independent state that doesn’t depend on other providers:AuthProvider (lib/providers/auth_provider.dart)
2. ChangeNotifierProxyProvider
Used when a provider depends on another provider’s state:create: Instantiates the provider onceupdate: Called wheneverAuthProviderchangesauth: Current state of the dependency (AuthProvider)prev: The existing instance of InventoryItemProvider- Returns
prev!to maintain the same instance (preserves cache)
Common Provider Patterns
Pattern 1: Loading State
All providers follow this pattern for async operations:lib/providers/inventory_item_provider.dart:292-346
Pattern 2: Data Caching
Providers cache data to minimize API calls:Pattern 3: Computed Getters
Providers expose processed data through getters:Pattern 4: Auth-Dependent Initialization
Many providers auto-load data when the user logs in:- Checks
!auth.isLoadingto ensure auth is ready - Checks
auth.isAuthenticatedto ensure user is logged in - Uses
Future.microtask()to avoid calling async code during build - Prevents redundant loads with
previous.stats == null
Example Providers
AuthProvider
File:lib/providers/auth_provider.dart
Responsibilities:
- Manage authentication state (user, token, loading)
- Login/logout operations
- Profile updates
- GitHub OAuth integration
- Password management
login()- Authenticate userlogout()- Clear sessionupdateProfile()- Update user datacheckAuthStatus()- Restore session on app start
InventoryItemProvider
File:lib/providers/inventory_item_provider.dart
Responsibilities:
- Load and cache inventory items
- Filter, sort, paginate items (client-side)
- CRUD operations (create, update, delete, clone)
- Manage current container/asset type context
- Track price history and market value
loadInventoryItems()- Fetch items for a specific asset typeloadAllItemsGlobal()- Load all items across containerscreateInventoryItem()- Add new itemupdateAssetWithFiles()- Update item with file uploadsdeleteInventoryItem()- Remove itemsetFilter()- Apply search/filtersortInventoryItems()- Sort by columngoToPage()- Navigate pagination
ContainerProvider
File:lib/providers/container_provider.dart
Responsibilities:
- Load container hierarchy
- Manage asset types within containers
- Load data lists (dropdowns)
- CRUD for containers, asset types, locations
PluginProvider
File:lib/providers/plugin_provider.dart
Responsibilities:
- Manage installed plugins
- Browse community plugins (GitHub + database)
- Install/uninstall plugins
- Activate/deactivate plugins
- Process plugin UI (STAC) with user context
refresh()- Reload plugin listsinstall()- Install from marketplaceuninstall()- Remove plugintogglePluginStatus()- Enable/disablegetProcessedUi()- Inject user data into plugin UI (e.g.,{{userName}})
ThemeProvider
File:lib/providers/theme_provider.dart
Responsibilities:
- Manage theme customization (colors, brightness)
- Sync with user profile theme config
- Persist theme changes to backend
PreferencesProvider
File:lib/providers/preferences_provider.dart
Responsibilities:
- Manage user preferences (language, currency, notifications)
- Load preferences on login
- Persist preference changes
Accessing Providers in UI
watch vs read vs select
Consumer Widget
For granular rebuilds:Selector Widget
For optimal performance (rebuild only on specific changes):State Update Flow
Typical flow for updating state:lib/providers/inventory_item_provider.dart:403-432)
Best Practices
1. Single Responsibility
Each provider manages one domain:- ✅
AuthProviderhandles auth,InventoryItemProviderhandles items - ❌ Don’t mix concerns (e.g., auth logic in InventoryItemProvider)
2. Dispose Properly
Always dispose of resources:3. Avoid Unnecessary Rebuilds
Usecontext.select() or Selector for expensive widgets:
4. Error Handling
Rethrow errors for UI to display:5. Use Future.microtask for Initialization
Avoid calling async methods during build:6. Optimize with Getters
Compute data in getters, not in methods:Testing Providers
Providers are easy to test in isolation:Next Steps
- Architecture Overview - Understand the bigger picture
- Project Structure - Navigate the codebase
- Contributing Guide - Extend the app with new functionality
