新增夜间模式相关代码
This commit is contained in:
+53
-43
@@ -1,11 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'pages/battery_health_page.dart';
|
||||
import 'services/saf_storage_service.dart';
|
||||
import 'theme/app_colors.dart';
|
||||
import 'theme/theme_service.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await SafStorageService().init();
|
||||
await ThemeService.init(); // 🆕
|
||||
|
||||
runApp(const MyApp());
|
||||
}
|
||||
@@ -15,53 +18,60 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<ThemeMode>(
|
||||
valueListenable: ThemeService.notifier,
|
||||
builder: (context, mode, _) {
|
||||
return MaterialApp(
|
||||
title: '电池健康度',
|
||||
theme: ThemeData(
|
||||
// 🟢 中文用 HarmonyOS Sans SC
|
||||
fontFamily: 'HarmonyOS_Sans_SC',
|
||||
// 🟢 数字回退到 FlymeFont
|
||||
fontFamilyFallback: const ['Flyme'],
|
||||
// 🟢 统一文字颜色为黑色
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: const Color(0xFFFFFFFF),
|
||||
).copyWith(onSurface: Colors.black),
|
||||
textTheme: const TextTheme(
|
||||
// 大标题/数字(健康度、容量等)
|
||||
displayMedium: TextStyle(
|
||||
fontFamily: 'HarmonyOS_Sans_SC',
|
||||
fontFamilyFallback: ['Flyme'],
|
||||
color: Colors.black,
|
||||
),
|
||||
// 标题(如“容量百分比变动趋势”)
|
||||
headlineMedium: TextStyle(
|
||||
fontFamily: 'HarmonyOS_Sans_SC',
|
||||
fontFamilyFallback: ['Flyme'],
|
||||
color: Colors.black,
|
||||
),
|
||||
// 正文(电压、温度、循环次数等)
|
||||
bodyMedium: TextStyle(
|
||||
fontFamily: 'HarmonyOS_Sans_SC',
|
||||
fontFamilyFallback: ['Flyme'],
|
||||
color: Colors.black,
|
||||
),
|
||||
// 小字(日期、辅助信息)
|
||||
bodySmall: TextStyle(
|
||||
fontFamily: 'HarmonyOS_Sans_SC',
|
||||
fontFamilyFallback: ['Flyme'],
|
||||
color: Colors.black87,
|
||||
),
|
||||
// 数字专用(大号数字,如健康度百分比)
|
||||
displayLarge: TextStyle(
|
||||
fontFamily: 'HarmonyOS_Sans_SC',
|
||||
fontFamilyFallback: ['Flyme'],
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
useMaterial3: true,
|
||||
),
|
||||
themeMode: mode,
|
||||
theme: _buildTheme(Brightness.light, kLightColors),
|
||||
darkTheme: _buildTheme(Brightness.dark, kDarkColors),
|
||||
home: const BatteryHealthPage(),
|
||||
debugShowCheckedModeBanner: false,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
ThemeData _buildTheme(Brightness brightness, AppColors colors) {
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: brightness,
|
||||
scaffoldBackgroundColor: colors.background,
|
||||
fontFamily: 'HarmonyOS_Sans_SC',
|
||||
fontFamilyFallback: const ['Flyme'],
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: colors.background,
|
||||
brightness: brightness,
|
||||
),
|
||||
extensions: [colors], // 🆕 注册 ThemeExtension
|
||||
textTheme: TextTheme(
|
||||
displayMedium: TextStyle(
|
||||
fontFamily: 'HarmonyOS_Sans_SC',
|
||||
fontFamilyFallback: const ['Flyme'],
|
||||
color: colors.textPrimary,
|
||||
),
|
||||
headlineMedium: TextStyle(
|
||||
fontFamily: 'HarmonyOS_Sans_SC',
|
||||
fontFamilyFallback: const ['Flyme'],
|
||||
color: colors.textPrimary,
|
||||
),
|
||||
bodyMedium: TextStyle(
|
||||
fontFamily: 'HarmonyOS_Sans_SC',
|
||||
fontFamilyFallback: const ['Flyme'],
|
||||
color: colors.textPrimary,
|
||||
),
|
||||
bodySmall: TextStyle(
|
||||
fontFamily: 'HarmonyOS_Sans_SC',
|
||||
fontFamilyFallback: const ['Flyme'],
|
||||
color: colors.textSecondary,
|
||||
),
|
||||
displayLarge: TextStyle(
|
||||
fontFamily: 'HarmonyOS_Sans_SC',
|
||||
fontFamilyFallback: const ['Flyme'],
|
||||
color: colors.textPrimary,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:developer' as dev;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import '../theme/theme_service.dart';
|
||||
|
||||
// --- 本地导入 ---
|
||||
import '../models/device_matching.dart';
|
||||
@@ -626,6 +627,56 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
||||
return ChargeUtils.getChargeTypeLabel(_isWireless, _chargeType);
|
||||
}
|
||||
|
||||
// 🆕 夜间模式切换弹窗 ← 加在这里
|
||||
void _showThemePicker() {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (ctx) {
|
||||
final current = ThemeService.notifier.value;
|
||||
return SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.brightness_auto),
|
||||
title: const Text('跟随系统'),
|
||||
trailing: current == ThemeMode.system
|
||||
? const Icon(Icons.check)
|
||||
: null,
|
||||
onTap: () {
|
||||
ThemeService.setMode(ThemeMode.system);
|
||||
Navigator.pop(ctx);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.light_mode),
|
||||
title: const Text('浅色'),
|
||||
trailing: current == ThemeMode.light
|
||||
? const Icon(Icons.check)
|
||||
: null,
|
||||
onTap: () {
|
||||
ThemeService.setMode(ThemeMode.light);
|
||||
Navigator.pop(ctx);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.dark_mode),
|
||||
title: const Text('深色'),
|
||||
trailing: current == ThemeMode.dark
|
||||
? const Icon(Icons.check)
|
||||
: null,
|
||||
onTap: () {
|
||||
ThemeService.setMode(ThemeMode.dark);
|
||||
Navigator.pop(ctx);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// ---------- Build ----------
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -695,7 +746,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
||||
);
|
||||
break;
|
||||
case 'dark_mode':
|
||||
// 夜间模式切换逻辑(1.7.0 再实现)
|
||||
_showThemePicker();
|
||||
break;
|
||||
}
|
||||
},
|
||||
@@ -705,11 +756,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
||||
value: 'about', // 🟢 新增
|
||||
child: Text('关于'),
|
||||
),
|
||||
const PopupMenuItem(
|
||||
value: 'dark_mode',
|
||||
enabled: false, // 暂时灰显
|
||||
child: Text('夜间模式(开发中)'),
|
||||
),
|
||||
const PopupMenuItem(value: 'dark_mode', child: Text('夜间模式')),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@immutable
|
||||
class AppColors extends ThemeExtension<AppColors> {
|
||||
final Color background; // 页面背景
|
||||
final Color surface; // 卡片/面板背景
|
||||
final Color textPrimary; // 主文字
|
||||
final Color textSecondary; // 次要文字
|
||||
final Color textTertiary; // 弱化文字
|
||||
final Color divider; // 分割线
|
||||
final Color icon; // 图标色
|
||||
|
||||
final Color healthGreen; // 健康度 ≥80%
|
||||
final Color healthYellow; // 70~80%
|
||||
final Color healthOrange;
|
||||
final Color healthRed; // <70% 或异常
|
||||
final Color barEmpty; // 无数据柱
|
||||
|
||||
final Color voltUp; // 电压↑
|
||||
final Color voltDown; // 电压↓
|
||||
final Color tempUp;
|
||||
final Color tempDown;
|
||||
|
||||
final Color progressBar; // 更新日志进度条
|
||||
final Color statusBarTint; // 状态栏背景
|
||||
|
||||
const AppColors({
|
||||
required this.background,
|
||||
required this.surface,
|
||||
required this.textPrimary,
|
||||
required this.textSecondary,
|
||||
required this.textTertiary,
|
||||
required this.divider,
|
||||
required this.icon,
|
||||
required this.healthGreen,
|
||||
required this.healthYellow,
|
||||
required this.healthOrange,
|
||||
required this.healthRed,
|
||||
required this.barEmpty,
|
||||
required this.voltUp,
|
||||
required this.voltDown,
|
||||
required this.tempUp,
|
||||
required this.tempDown,
|
||||
required this.progressBar,
|
||||
required this.statusBarTint,
|
||||
});
|
||||
|
||||
@override
|
||||
AppColors copyWith({
|
||||
Color? background,
|
||||
Color? surface,
|
||||
Color? textPrimary,
|
||||
Color? textSecondary,
|
||||
Color? textTertiary,
|
||||
Color? divider,
|
||||
Color? icon,
|
||||
Color? healthGreen,
|
||||
Color? healthYellow,
|
||||
Color? healthOrange,
|
||||
Color? healthRed,
|
||||
Color? barEmpty,
|
||||
Color? voltUp,
|
||||
Color? voltDown,
|
||||
Color? tempUp,
|
||||
Color? tempDown,
|
||||
Color? progressBar,
|
||||
Color? statusBarTint,
|
||||
}) {
|
||||
return AppColors(
|
||||
background: background ?? this.background,
|
||||
surface: surface ?? this.surface,
|
||||
textPrimary: textPrimary ?? this.textPrimary,
|
||||
textSecondary: textSecondary ?? this.textSecondary,
|
||||
textTertiary: textTertiary ?? this.textTertiary,
|
||||
divider: divider ?? this.divider,
|
||||
icon: icon ?? this.icon,
|
||||
healthGreen: healthGreen ?? this.healthGreen,
|
||||
healthYellow: healthYellow ?? this.healthYellow,
|
||||
healthOrange: healthOrange ?? this.healthOrange,
|
||||
healthRed: healthRed ?? this.healthRed,
|
||||
barEmpty: barEmpty ?? this.barEmpty,
|
||||
voltUp: voltUp ?? this.voltUp,
|
||||
voltDown: voltDown ?? this.voltDown,
|
||||
tempUp: tempUp ?? this.tempUp,
|
||||
tempDown: tempDown ?? this.tempDown,
|
||||
progressBar: progressBar ?? this.progressBar,
|
||||
statusBarTint: statusBarTint ?? this.statusBarTint,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AppColors lerp(ThemeExtension<AppColors>? other, double t) {
|
||||
if (other is! AppColors) return this;
|
||||
return AppColors(
|
||||
background: Color.lerp(background, other.background, t)!,
|
||||
surface: Color.lerp(surface, other.surface, t)!,
|
||||
textPrimary: Color.lerp(textPrimary, other.textPrimary, t)!,
|
||||
textSecondary: Color.lerp(textSecondary, other.textSecondary, t)!,
|
||||
textTertiary: Color.lerp(textTertiary, other.textTertiary, t)!,
|
||||
divider: Color.lerp(divider, other.divider, t)!,
|
||||
icon: Color.lerp(icon, other.icon, t)!,
|
||||
healthGreen: Color.lerp(healthGreen, other.healthGreen, t)!,
|
||||
healthYellow: Color.lerp(healthYellow, other.healthYellow, t)!,
|
||||
healthOrange: Color.lerp(healthOrange, other.healthOrange, t)!,
|
||||
healthRed: Color.lerp(healthRed, other.healthRed, t)!,
|
||||
barEmpty: Color.lerp(barEmpty, other.barEmpty, t)!,
|
||||
voltUp: Color.lerp(voltUp, other.voltUp, t)!,
|
||||
voltDown: Color.lerp(voltDown, other.voltDown, t)!,
|
||||
tempUp: Color.lerp(tempUp, other.tempUp, t)!,
|
||||
tempDown: Color.lerp(tempDown, other.tempDown, t)!,
|
||||
progressBar: Color.lerp(progressBar, other.progressBar, t)!,
|
||||
statusBarTint: Color.lerp(statusBarTint, other.statusBarTint, t)!,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 浅色主题(保持现有视觉)
|
||||
const AppColors kLightColors = AppColors(
|
||||
background: Colors.white,
|
||||
surface: Color(0xFFF0F4F8),
|
||||
textPrimary: Colors.black,
|
||||
textSecondary: Color(0xFF5F5F5F),
|
||||
textTertiary: Colors.grey,
|
||||
divider: Color(0xFFE0E0E0),
|
||||
icon: Colors.black,
|
||||
healthGreen: Color(0xFF00B140),
|
||||
healthYellow: Color(0xFFEFDF00),
|
||||
healthOrange: Color(0xFFD75A16),
|
||||
healthRed: Colors.red,
|
||||
barEmpty: Color(0xFFE0E0E0),
|
||||
voltUp: Colors.red,
|
||||
voltDown: Colors.blue,
|
||||
tempUp: Colors.red,
|
||||
tempDown: Colors.blue,
|
||||
progressBar: Color.fromRGBO(157, 212, 237, 1.0),
|
||||
statusBarTint: Color.fromRGBO(157, 212, 237, 0.1),
|
||||
);
|
||||
|
||||
/// 深色主题(色相保留,明度调整)
|
||||
const AppColors kDarkColors = AppColors(
|
||||
background: Color(0xFF121212),
|
||||
surface: Color(0xFF1E1E1E),
|
||||
textPrimary: Color(0xFFE8E8E8),
|
||||
textSecondary: Color(0xFFA0A0A0),
|
||||
textTertiary: Color(0xFF707070),
|
||||
divider: Color(0xFF2A2A2A),
|
||||
icon: Color(0xFFE8E8E8),
|
||||
healthGreen: Color(0xFF00D14D),
|
||||
healthYellow: Color(0xFFD4C520),
|
||||
healthOrange: Color(0xFFFF7838),
|
||||
healthRed: Color(0xFFFF5252),
|
||||
barEmpty: Color(0xFF2A2A2A),
|
||||
voltUp: Color(0xFFFF5252),
|
||||
voltDown: Color(0xFF5AA9FF),
|
||||
tempUp: Color(0xFFFF5252),
|
||||
tempDown: Color(0xFF5AA9FF),
|
||||
progressBar: Color(0xFF5A9BBD),
|
||||
statusBarTint: Color(0xFF121212),
|
||||
);
|
||||
|
||||
/// 便捷扩展:context.colors.textPrimary
|
||||
extension AppColorsX on BuildContext {
|
||||
AppColors get colors => Theme.of(this).extension<AppColors>()!;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class ThemeService {
|
||||
static const _prefKey = 'theme_mode';
|
||||
static final ValueNotifier<ThemeMode> notifier = ValueNotifier(
|
||||
ThemeMode.system,
|
||||
);
|
||||
|
||||
/// 启动时调用(main.dart)
|
||||
static Future<void> init() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final saved = prefs.getString(_prefKey);
|
||||
notifier.value = _parse(saved);
|
||||
}
|
||||
|
||||
/// 菜单里调用
|
||||
static Future<void> setMode(ThemeMode mode) async {
|
||||
notifier.value = mode;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_prefKey, _name(mode));
|
||||
}
|
||||
|
||||
static ThemeMode _parse(String? s) {
|
||||
switch (s) {
|
||||
case 'light':
|
||||
return ThemeMode.light;
|
||||
case 'dark':
|
||||
return ThemeMode.dark;
|
||||
default:
|
||||
return ThemeMode.system;
|
||||
}
|
||||
}
|
||||
|
||||
static String _name(ThemeMode m) {
|
||||
switch (m) {
|
||||
case ThemeMode.light:
|
||||
return 'light';
|
||||
case ThemeMode.dark:
|
||||
return 'dark';
|
||||
case ThemeMode.system:
|
||||
return 'system';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user