6.5 KiB
6.5 KiB
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
// 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
// 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
// Main theme settings provider (persisted)
themeSettingsProvider
// Convenience providers
currentSeedColorProvider // Color - current seed color
seedColorOptionsProvider // List<SeedColorOption> - all options
Usage in App
// 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
// 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
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:
// 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):
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
// 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(),
),
);
}