257 lines
6.5 KiB
Markdown
257 lines
6.5 KiB
Markdown
# App Settings & Theme System
|
|
|
|
## Overview
|
|
|
|
The app uses a centralized `AppSettingsBox` (Hive) for storing all app-level settings. This includes theme preferences, language settings, notification preferences, and other user configurations.
|
|
|
|
---
|
|
|
|
## AppSettingsBox
|
|
|
|
**Location**: `lib/core/database/app_settings_box.dart`
|
|
|
|
### Initialization
|
|
|
|
```dart
|
|
// In main.dart - call before runApp()
|
|
await AppSettingsBox.init();
|
|
```
|
|
|
|
### Storage Keys
|
|
|
|
| Key | Type | Default | Description |
|
|
|-----|------|---------|-------------|
|
|
| **Theme** |
|
|
| `seed_color_id` | String | `'blue'` | Selected theme color ID |
|
|
| `theme_mode` | int | `0` | 0=system, 1=light, 2=dark |
|
|
| **Language** |
|
|
| `language_code` | String | `'vi'` | Language code (vi, en) |
|
|
| **Notifications** |
|
|
| `notifications_enabled` | bool | `true` | Master notification toggle |
|
|
| `order_notifications` | bool | `true` | Order status notifications |
|
|
| `promotion_notifications` | bool | `true` | Promotion notifications |
|
|
| `chat_notifications` | bool | `true` | Chat message notifications |
|
|
| **User Preferences** |
|
|
| `onboarding_completed` | bool | `false` | Onboarding flow completed |
|
|
| `biometric_enabled` | bool | `false` | Biometric login enabled |
|
|
| `remember_login` | bool | `false` | Remember login credentials |
|
|
| **App State** |
|
|
| `last_sync_time` | String | - | Last data sync timestamp |
|
|
| `app_version` | String | - | Last launched app version |
|
|
| `first_launch_date` | String | - | First app launch date |
|
|
|
|
### Usage
|
|
|
|
```dart
|
|
// Generic get/set
|
|
AppSettingsBox.get<String>('key', defaultValue: 'default');
|
|
await AppSettingsBox.set('key', value);
|
|
|
|
// Helper methods
|
|
AppSettingsBox.getSeedColorId(); // Returns 'blue', 'teal', etc.
|
|
await AppSettingsBox.setSeedColorId('teal');
|
|
|
|
AppSettingsBox.getThemeModeIndex(); // Returns 0, 1, or 2
|
|
await AppSettingsBox.setThemeModeIndex(1);
|
|
|
|
AppSettingsBox.getLanguageCode(); // Returns 'vi' or 'en'
|
|
await AppSettingsBox.setLanguageCode('en');
|
|
|
|
AppSettingsBox.areNotificationsEnabled(); // Returns true/false
|
|
AppSettingsBox.isOnboardingCompleted();
|
|
AppSettingsBox.isBiometricEnabled();
|
|
```
|
|
|
|
---
|
|
|
|
## Theme System
|
|
|
|
### Architecture
|
|
|
|
```
|
|
colors.dart → Seed color options & status colors
|
|
app_theme.dart → ThemeData generation from seed color
|
|
theme_provider.dart → Riverpod state management
|
|
```
|
|
|
|
### Available Seed Colors
|
|
|
|
| ID | Name | Color |
|
|
|----|------|-------|
|
|
| `blue` | Xanh dương | `#005B9A` (default) |
|
|
| `teal` | Xanh ngọc | `#009688` |
|
|
| `green` | Xanh lá | `#4CAF50` |
|
|
| `purple` | Tím | `#673AB7` |
|
|
| `indigo` | Chàm | `#3F51B5` |
|
|
| `orange` | Cam | `#FF5722` |
|
|
| `red` | Đỏ | `#E53935` |
|
|
| `pink` | Hồng | `#E91E63` |
|
|
|
|
### Providers
|
|
|
|
```dart
|
|
// Main theme settings provider (persisted)
|
|
themeSettingsProvider
|
|
|
|
// Convenience providers
|
|
currentSeedColorProvider // Color - current seed color
|
|
seedColorOptionsProvider // List<SeedColorOption> - all options
|
|
```
|
|
|
|
### Usage in App
|
|
|
|
```dart
|
|
// app.dart - Dynamic theme
|
|
class MyApp extends ConsumerWidget {
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final settings = ref.watch(themeSettingsProvider);
|
|
|
|
return MaterialApp(
|
|
theme: AppTheme.lightTheme(settings.seedColor),
|
|
darkTheme: AppTheme.darkTheme(settings.seedColor),
|
|
themeMode: settings.themeMode,
|
|
// ...
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Changing Theme
|
|
|
|
```dart
|
|
// Change seed color
|
|
ref.read(themeSettingsProvider.notifier).setSeedColor('teal');
|
|
|
|
// Change theme mode
|
|
ref.read(themeSettingsProvider.notifier).setThemeMode(ThemeMode.dark);
|
|
|
|
// Toggle light/dark
|
|
ref.read(themeSettingsProvider.notifier).toggleThemeMode();
|
|
```
|
|
|
|
### Color Picker Widget Example
|
|
|
|
```dart
|
|
class ColorPickerWidget extends ConsumerWidget {
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final options = ref.watch(seedColorOptionsProvider);
|
|
final current = ref.watch(themeSettingsProvider);
|
|
|
|
return Wrap(
|
|
spacing: 8,
|
|
children: options.map((option) {
|
|
final isSelected = option.id == current.seedColorId;
|
|
return GestureDetector(
|
|
onTap: () => ref
|
|
.read(themeSettingsProvider.notifier)
|
|
.setSeedColor(option.id),
|
|
child: Container(
|
|
width: 48,
|
|
height: 48,
|
|
decoration: BoxDecoration(
|
|
color: option.color,
|
|
shape: BoxShape.circle,
|
|
border: isSelected
|
|
? Border.all(color: Colors.white, width: 3)
|
|
: null,
|
|
),
|
|
child: isSelected
|
|
? const Icon(Icons.check, color: Colors.white)
|
|
: null,
|
|
),
|
|
);
|
|
}).toList(),
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Using ColorScheme
|
|
|
|
With the `fromSeed()` approach, always use `Theme.of(context).colorScheme` for colors:
|
|
|
|
```dart
|
|
// Via context extension (recommended)
|
|
final cs = context.colorScheme;
|
|
|
|
// Common color usage
|
|
cs.primary // Main brand color (buttons, links)
|
|
cs.onPrimary // Text/icons on primary color
|
|
cs.primaryContainer // Softer brand background
|
|
cs.onPrimaryContainer // Text on primaryContainer
|
|
|
|
cs.secondary // Secondary accent
|
|
cs.tertiary // Third accent color
|
|
|
|
cs.surface // Card/container backgrounds
|
|
cs.onSurface // Primary text color
|
|
cs.onSurfaceVariant // Secondary text color
|
|
|
|
cs.outline // Borders, dividers
|
|
cs.outlineVariant // Lighter borders
|
|
|
|
cs.error // Error states
|
|
cs.onError // Text on error
|
|
|
|
// Example widget
|
|
Container(
|
|
color: cs.primaryContainer,
|
|
child: Text(
|
|
'Hello',
|
|
style: TextStyle(color: cs.onPrimaryContainer),
|
|
),
|
|
)
|
|
```
|
|
|
|
### Status Colors (Fixed)
|
|
|
|
These colors don't change with theme (from backend):
|
|
|
|
```dart
|
|
AppColors.success // #28a745 - Green
|
|
AppColors.warning // #ffc107 - Yellow
|
|
AppColors.danger // #dc3545 - Red
|
|
AppColors.info // #17a2b8 - Blue
|
|
```
|
|
|
|
---
|
|
|
|
## Files Reference
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `lib/core/database/app_settings_box.dart` | Hive storage for all app settings |
|
|
| `lib/core/theme/colors.dart` | Seed colors, status colors, gradients |
|
|
| `lib/core/theme/app_theme.dart` | ThemeData generation |
|
|
| `lib/core/theme/theme_provider.dart` | Riverpod providers for theme |
|
|
| `lib/core/theme/typography.dart` | Text styles |
|
|
|
|
---
|
|
|
|
## Initialization Order
|
|
|
|
```dart
|
|
// main.dart
|
|
Future<void> main() async {
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
|
|
// 1. Initialize Hive
|
|
await Hive.initFlutter();
|
|
|
|
// 2. Initialize AppSettingsBox
|
|
await AppSettingsBox.init();
|
|
|
|
// 3. Initialize other boxes...
|
|
|
|
runApp(
|
|
ProviderScope(
|
|
child: MyApp(),
|
|
),
|
|
);
|
|
}
|
|
```
|