update theme selection
This commit is contained in:
256
APP_SETTINGS.md
Normal file
256
APP_SETTINGS.md
Normal file
@@ -0,0 +1,256 @@
|
||||
# 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(),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user