深色模式代码第二轮补充

This commit is contained in:
2026-10-11 16:03:06 +08:00
parent 1d033670f7
commit 7989495521
10 changed files with 198 additions and 209 deletions
+11
View File
@@ -45,6 +45,17 @@ class MyApp extends StatelessWidget {
brightness: brightness, brightness: brightness,
), ),
extensions: [colors], // 🆕 注册 ThemeExtension extensions: [colors], // 🆕 注册 ThemeExtension
appBarTheme: AppBarTheme(
backgroundColor: colors.background, // ← 关键:和 Scaffold 一致
foregroundColor: colors.textPrimary,
surfaceTintColor: Colors.transparent, // ← 关掉 M3 的滑动变色
elevation: 0,
scrolledUnderElevation: 0,
shadowColor: Colors.transparent,
iconTheme: IconThemeData(color: colors.textPrimary),
),
textTheme: TextTheme( textTheme: TextTheme(
displayMedium: TextStyle( displayMedium: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
+21 -37
View File
@@ -1,16 +1,16 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../theme/app_colors.dart';
class AboutPage extends StatelessWidget { class AboutPage extends StatelessWidget {
const AboutPage({super.key}); const AboutPage({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark; final c = context.colors;
return Scaffold( return Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: AppBar( appBar: AppBar(
title: const Text( title: const Text(
'关于', '关于',
@@ -19,10 +19,10 @@ class AboutPage extends StatelessWidget {
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
), ),
backgroundColor: Colors.transparent, backgroundColor: c.background,
elevation: 0, elevation: 0,
foregroundColor: Theme.of(context).iconTheme.color, foregroundColor: c.textPrimary,
iconTheme: IconThemeData(color: Theme.of(context).iconTheme.color), iconTheme: IconThemeData(color: c.textPrimary),
), ),
body: Center( body: Center(
child: Padding( child: Padding(
@@ -30,15 +30,12 @@ class AboutPage extends StatelessWidget {
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
// Logo
Container( Container(
width: 120, width: 120,
height: 120, height: 120,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
color: isDark color: c.surface,
? Colors.grey.shade800
: const Color(0xFFF0F4F8),
), ),
child: ClipOval( child: ClipOval(
child: Image.asset( child: Image.asset(
@@ -50,38 +47,39 @@ class AboutPage extends StatelessWidget {
), ),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
const Text( Text(
'MEIZU Battery Healthy', 'MEIZU Battery Healthy',
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: c.textPrimary,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
'版本 1.7.0', '版本 1.8.0',
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 14, fontSize: 14,
color: Colors.grey.shade500, color: c.textTertiary,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Divider( Divider(
color: Colors.grey.shade300, color: c.divider,
thickness: 0.5, thickness: 0.5,
indent: 40, indent: 40,
endIndent: 40, endIndent: 40,
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
const Text( Text(
'专机专配 · 仅针对魅族独家开发', '专机专配 · 仅针对魅族独家开发',
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 14, fontSize: 14,
color: Colors.grey, color: c.textSecondary,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
@@ -90,13 +88,12 @@ class AboutPage extends StatelessWidget {
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 13, fontSize: 13,
color: Colors.grey.shade400, color: c.textTertiary,
), ),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
// 源码链接按钮
InkWell( InkWell(
onTap: () => _launchUrl(context), // 注意这里传入了 context onTap: () => _launchUrl(context),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
child: Container( child: Container(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@@ -104,34 +101,24 @@ class AboutPage extends StatelessWidget {
vertical: 12, vertical: 12,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDark color: c.surface,
? Colors.grey.shade800
: const Color(0xFFF0F4F8),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Icon( Icon(Icons.code, size: 18, color: c.textPrimary),
Icons.code,
size: 18,
color: Theme.of(context).iconTheme.color,
),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
'查看源码 / 反馈', '查看源码 / 反馈',
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 14, fontSize: 14,
color: Theme.of(context).iconTheme.color, color: c.textPrimary,
), ),
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
Icon( Icon(Icons.open_in_new, size: 14, color: c.textTertiary),
Icons.open_in_new,
size: 14,
color: Colors.grey.shade500,
),
], ],
), ),
), ),
@@ -142,7 +129,7 @@ class AboutPage extends StatelessWidget {
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 12, fontSize: 12,
color: Colors.grey.shade400, color: c.textTertiary,
), ),
), ),
], ],
@@ -155,14 +142,11 @@ class AboutPage extends StatelessWidget {
Future<void> _launchUrl(BuildContext context) async { Future<void> _launchUrl(BuildContext context) async {
const url = 'https://git.whitetop.xyz/lxh2875931338/MEIZU-Battery-Healthy'; const url = 'https://git.whitetop.xyz/lxh2875931338/MEIZU-Battery-Healthy';
final uri = Uri.parse(url); final uri = Uri.parse(url);
try { try {
// ✅ 直接尝试打开,不再检查 canLaunchUrl
await launchUrl(uri, mode: LaunchMode.externalApplication); await launchUrl(uri, mode: LaunchMode.externalApplication);
} catch (e) { } catch (e) {
// 如果打开失败,复制链接并提示
debugPrint('打开链接失败: $e'); debugPrint('打开链接失败: $e');
await Clipboard.setData(ClipboardData(text: url)); await Clipboard.setData(const ClipboardData(text: url));
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
+11 -10
View File
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import '../theme/theme_service.dart'; import '../theme/theme_service.dart';
import '../theme/app_colors.dart';
// --- 本地导入 --- // --- 本地导入 ---
import '../models/device_matching.dart'; import '../models/device_matching.dart';
@@ -696,25 +697,25 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
); );
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: const SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarColor: Color.fromRGBO(157, 212, 237, 0.1), statusBarColor: context.colors.statusBarTint,
statusBarIconBrightness: Brightness.dark, statusBarIconBrightness: Brightness.dark,
statusBarBrightness: Brightness.dark, statusBarBrightness: Brightness.dark,
), ),
child: Scaffold( child: Scaffold(
backgroundColor: Colors.white, // backgroundColor: Colors.white,
appBar: AppBar( appBar: AppBar(
backgroundColor: Colors.white, // backgroundColor: Colors.white,
elevation: 0, elevation: 0,
scrolledUnderElevation: 0, scrolledUnderElevation: 0,
shadowColor: Colors.transparent, shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent, surfaceTintColor: Colors.transparent,
title: const Text( title: Text(
'电池健康度', '电池健康度',
style: TextStyle( style: TextStyle(
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Colors.black, color: context.colors.textPrimary,
), ),
), ),
actions: [ actions: [
@@ -722,11 +723,11 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
icon: const Icon(Icons.refresh), icon: const Icon(Icons.refresh),
onPressed: readBatteryCapacity, onPressed: readBatteryCapacity,
tooltip: '刷新数据', tooltip: '刷新数据',
color: Colors.black, color: context.colors.icon,
), ),
PopupMenuButton<String>( PopupMenuButton<String>(
icon: const Icon(Icons.more_vert), icon: const Icon(Icons.more_vert),
color: Colors.white, color: context.colors.surface,
onSelected: (value) { onSelected: (value) {
switch (value) { switch (value) {
case 'changelog': case 'changelog':
@@ -772,9 +773,9 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
children: [ children: [
Text( Text(
_deviceModel, _deviceModel,
style: const TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
color: Colors.grey, color: context.colors.textTertiary,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
), ),
), ),
+28 -40
View File
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; // 🟢 添加这一行 import 'package:flutter/services.dart';
import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_markdown/flutter_markdown.dart';
import '../theme/app_colors.dart';
class ChangelogPage extends StatefulWidget { class ChangelogPage extends StatefulWidget {
const ChangelogPage({super.key}); const ChangelogPage({super.key});
@@ -15,33 +16,27 @@ class _ChangelogPageState extends State<ChangelogPage>
String _markdownContent = ''; String _markdownContent = '';
bool _isLoading = true; bool _isLoading = true;
String? _error; String? _error;
Widget? _cachedMarkdown;
// ---------- 滚动 ---------- // ---------- 滚动 ----------
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
double _scrollProgress = 0.0; double _scrollProgress = 0.0;
bool _isScrolling = false; bool _isScrolling = false;
// ---------- 呼吸动画(维持高刷感知) ---------- // ---------- 呼吸动画 ----------
late AnimationController _breathController; late AnimationController _breathController;
late Animation<double> _breathAnimation; late Animation<double> _breathAnimation;
// ---------- 原生刷新率控制 ---------- // ---------- 原生刷新率控制 ----------
// 🟢 改用 static final(不是 const)
static final _frameRateChannel = MethodChannel( static final _frameRateChannel = MethodChannel(
'com.lxh.battery_health/frame_rate', 'com.lxh.battery_health/frame_rate',
); );
// ---------- 生命周期 ----------
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_loadChangelog(); _loadChangelog();
// 进入页面时申请高刷(原生层)
_frameRateChannel.invokeMethod('setHighRefreshRate'); _frameRateChannel.invokeMethod('setHighRefreshRate');
// 呼吸动画:让进度条在滚动时轻微抖动,维持高刷感知
_breathController = AnimationController( _breathController = AnimationController(
vsync: this, vsync: this,
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 500),
@@ -70,16 +65,13 @@ class _ChangelogPageState extends State<ChangelogPage>
@override @override
void dispose() { void dispose() {
// 离开页面时恢复默认刷新率
_frameRateChannel.invokeMethod('clearRefreshRate'); _frameRateChannel.invokeMethod('clearRefreshRate');
_scrollController.removeListener(_updateProgress); _scrollController.removeListener(_updateProgress);
_scrollController.dispose(); _scrollController.dispose();
_breathController.dispose(); _breathController.dispose();
super.dispose(); super.dispose();
} }
// ---------- 方法 ----------
void _updateProgress() { void _updateProgress() {
if (!_scrollController.hasClients) return; if (!_scrollController.hasClients) return;
final maxScroll = _scrollController.position.maxScrollExtent; final maxScroll = _scrollController.position.maxScrollExtent;
@@ -96,7 +88,6 @@ class _ChangelogPageState extends State<ChangelogPage>
setState(() { setState(() {
_markdownContent = content; _markdownContent = content;
_isLoading = false; _isLoading = false;
_cachedMarkdown = _buildMarkdown(content);
}); });
} catch (e) { } catch (e) {
setState(() { setState(() {
@@ -106,78 +97,75 @@ class _ChangelogPageState extends State<ChangelogPage>
} }
} }
Widget _buildMarkdown(String content) { Widget _buildMarkdown(BuildContext context, String content) {
final c = context.colors;
return Markdown( return Markdown(
data: content, data: content,
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
styleSheet: MarkdownStyleSheet( styleSheet: MarkdownStyleSheet(
h1: const TextStyle( h1: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
color: Colors.black, color: c.textPrimary,
), ),
h2: const TextStyle( h2: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Colors.black87, color: c.textPrimary,
), ),
h3: const TextStyle( h3: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Colors.black87, color: c.textSecondary,
), ),
p: const TextStyle( p: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16, fontSize: 16,
color: Colors.black87, color: c.textSecondary,
), ),
listBullet: const TextStyle( listBullet: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16, fontSize: 16,
color: Colors.black87, color: c.textSecondary,
), ),
code: const TextStyle( code: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 14, fontSize: 14,
color: Colors.black87, color: c.textSecondary,
backgroundColor: Color(0xFFF5F5F5), backgroundColor: c.surface,
), ),
), ),
); );
} }
// ---------- Build ----------
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final c = context.colors;
final breathOffset = _isScrolling ? _breathAnimation.value : 0.0; final breathOffset = _isScrolling ? _breathAnimation.value : 0.0;
final displayProgress = (_scrollProgress + breathOffset).clamp(0.0, 1.0); final displayProgress = (_scrollProgress + breathOffset).clamp(0.0, 1.0);
return Scaffold( return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar( appBar: AppBar(
title: const Text( title: Text(
'更新日志', '更新日志',
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.black, color: c.textPrimary,
), ),
), ),
backgroundColor: Colors.white,
elevation: 0, elevation: 0,
foregroundColor: Colors.black, foregroundColor: c.textPrimary,
iconTheme: const IconThemeData(color: Colors.black), iconTheme: IconThemeData(color: c.textPrimary),
bottom: PreferredSize( bottom: PreferredSize(
preferredSize: const Size.fromHeight(2.0), preferredSize: const Size.fromHeight(2.0),
child: LinearProgressIndicator( child: LinearProgressIndicator(
value: displayProgress, value: displayProgress,
backgroundColor: Colors.grey.shade200, backgroundColor: c.divider,
valueColor: const AlwaysStoppedAnimation<Color>( valueColor: AlwaysStoppedAnimation<Color>(c.progressBar),
Color.fromRGBO(157, 212, 237, 1.0),
),
minHeight: 2.0, minHeight: 2.0,
), ),
), ),
@@ -188,16 +176,16 @@ class _ChangelogPageState extends State<ChangelogPage>
? Center( ? Center(
child: Text( child: Text(
_error!, _error!,
style: const TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.grey, color: c.textTertiary,
), ),
), ),
) )
: SingleChildScrollView( : SingleChildScrollView(
controller: _scrollController, controller: _scrollController,
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: _cachedMarkdown ?? const SizedBox.shrink(), child: _buildMarkdown(context, _markdownContent),
), ),
); );
} }
+43 -45
View File
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import '../models/data_abnormal_type.dart'; import '../models/data_abnormal_type.dart';
import '../theme/app_colors.dart';
class BatteryInfoPanel extends StatelessWidget { class BatteryInfoPanel extends StatelessWidget {
final double batteryVolt; final double batteryVolt;
@@ -9,9 +10,8 @@ class BatteryInfoPanel extends StatelessWidget {
final double minVolt; final double minVolt;
final double maxTemp; final double maxTemp;
final double minTemp; final double minTemp;
final DataAbnormalType abnormalType;
final double tempAvg; final double tempAvg;
final DataAbnormalType abnormalType;
const BatteryInfoPanel({ const BatteryInfoPanel({
super.key, super.key,
@@ -27,19 +27,22 @@ class BatteryInfoPanel extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final c = context.colors;
final bool isVoltageAbnormal = final bool isVoltageAbnormal =
abnormalType == DataAbnormalType.voltageAbnormal; abnormalType == DataAbnormalType.voltageAbnormal;
final Color voltColor = isVoltageAbnormal ? Colors.red : Colors.black; final Color voltColor = isVoltageAbnormal ? c.healthRed : c.textPrimary;
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// ---------- 电压列 ----------
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildInfoItem( _buildInfoItem(
_buildIcon('voltage'), context,
_buildIcon(context, 'voltage'),
'电压', '电压',
'${batteryVolt.toStringAsFixed(3)}V', '${batteryVolt.toStringAsFixed(3)}V',
textColor: voltColor, textColor: voltColor,
@@ -53,22 +56,17 @@ class BatteryInfoPanel extends StatelessWidget {
fontFamily: 'Flyme', fontFamily: 'Flyme',
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Colors.red, color: c.voltUp,
), ),
children: [ children: [
const TextSpan( TextSpan(
text: '↑ ', text: '↑ ',
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.red, color: c.voltUp,
),
),
TextSpan(
text: '${maxVolt.toStringAsFixed(3)}V',
style: TextStyle(
color: isVoltageAbnormal ? Colors.red : Colors.red,
), ),
), ),
TextSpan(text: '${maxVolt.toStringAsFixed(3)}V'),
], ],
), ),
), ),
@@ -79,22 +77,17 @@ class BatteryInfoPanel extends StatelessWidget {
fontFamily: 'Flyme', fontFamily: 'Flyme',
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Colors.blue, color: c.voltDown,
), ),
children: [ children: [
const TextSpan( TextSpan(
text: '↓ ', text: '↓ ',
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.blue, color: c.voltDown,
),
),
TextSpan(
text: '${minVolt.toStringAsFixed(3)}V',
style: TextStyle(
color: isVoltageAbnormal ? Colors.red : Colors.blue,
), ),
), ),
TextSpan(text: '${minVolt.toStringAsFixed(3)}V'),
], ],
), ),
), ),
@@ -103,33 +96,34 @@ class BatteryInfoPanel extends StatelessWidget {
], ],
), ),
), ),
// ---------- 温度列 ----------
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildInfoItem( _buildInfoItem(
_buildIcon('temperature'), context,
_buildIcon(context, 'temperature'),
'温度', '温度',
'${batteryTemp.toStringAsFixed(0)}℃', '${batteryTemp.toStringAsFixed(0)}℃',
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
// 🆕 一行显示:↑ max ↓ min [avg] avg
Row( Row(
children: [ children: [
RichText( RichText(
text: TextSpan( text: TextSpan(
style: const TextStyle( style: TextStyle(
fontFamily: 'Flyme', fontFamily: 'Flyme',
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Colors.red, color: c.tempUp,
), ),
children: [ children: [
const TextSpan( TextSpan(
text: '↑ ', text: '↑ ',
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.red, color: c.tempUp,
), ),
), ),
TextSpan(text: '${maxTemp.toStringAsFixed(1)}℃'), TextSpan(text: '${maxTemp.toStringAsFixed(1)}℃'),
@@ -139,18 +133,18 @@ class BatteryInfoPanel extends StatelessWidget {
const SizedBox(width: 6), const SizedBox(width: 6),
RichText( RichText(
text: TextSpan( text: TextSpan(
style: const TextStyle( style: TextStyle(
fontFamily: 'Flyme', fontFamily: 'Flyme',
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Colors.blue, color: c.tempDown,
), ),
children: [ children: [
const TextSpan( TextSpan(
text: '↓ ', text: '↓ ',
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.blue, color: c.tempDown,
), ),
), ),
TextSpan(text: '${minTemp.toStringAsFixed(1)}℃'), TextSpan(text: '${minTemp.toStringAsFixed(1)}℃'),
@@ -158,15 +152,15 @@ class BatteryInfoPanel extends StatelessWidget {
), ),
), ),
const SizedBox(width: 6), const SizedBox(width: 6),
_buildIcon('avg', size: 14), _buildIcon(context, 'avg', size: 14),
const SizedBox(width: 2), const SizedBox(width: 2),
Text( Text(
'${tempAvg.toStringAsFixed(1)}℃', '${tempAvg.toStringAsFixed(1)}℃',
style: const TextStyle( style: TextStyle(
fontFamily: 'Flyme', fontFamily: 'Flyme',
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Colors.grey, color: c.textTertiary,
), ),
), ),
], ],
@@ -178,50 +172,54 @@ class BatteryInfoPanel extends StatelessWidget {
); );
} }
Widget _buildIcon(String type, {double size = 18}) { Widget _buildIcon(BuildContext context, String type, {double size = 18}) {
final c = context.colors;
final filter = ColorFilter.mode(c.textTertiary, BlendMode.srcIn);
switch (type) { switch (type) {
case 'voltage': case 'voltage':
return SvgPicture.asset( return SvgPicture.asset(
'assets/icons/si--lightning-fill.svg', 'assets/icons/si--lightning-fill.svg',
width: size, width: size,
height: size, height: size,
colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn), colorFilter: filter,
); );
case 'temperature': case 'temperature':
return SvgPicture.asset( return SvgPicture.asset(
'assets/icons/mdi--temperature.svg', 'assets/icons/mdi--temperature.svg',
width: size, width: size,
height: size, height: size,
colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn), colorFilter: filter,
); );
case 'avg': // 🆕 case 'avg':
return SvgPicture.asset( return SvgPicture.asset(
'assets/icons/avg.svg', 'assets/icons/avg.svg',
width: size, width: size,
height: size, height: size,
colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn), colorFilter: filter,
); );
default: default:
return Icon(Icons.help_outline, size: size, color: Colors.grey); return Icon(Icons.help_outline, size: size, color: c.textTertiary);
} }
} }
Widget _buildInfoItem( Widget _buildInfoItem(
BuildContext context,
Widget icon, Widget icon,
String label, String label,
String value, { String value, {
Color textColor = Colors.black, Color? textColor,
}) { }) {
final c = context.colors;
return Row( return Row(
children: [ children: [
icon, icon,
const SizedBox(width: 6), const SizedBox(width: 6),
Text( Text(
label, label,
style: const TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 14, fontSize: 14,
color: Colors.grey, color: c.textTertiary,
), ),
), ),
const SizedBox(width: 6), const SizedBox(width: 6),
@@ -231,7 +229,7 @@ class BatteryInfoPanel extends StatelessWidget {
fontFamily: 'Flyme', fontFamily: 'Flyme',
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: textColor, color: textColor ?? c.textPrimary,
), ),
), ),
], ],
+13 -15
View File
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../models/data_abnormal_type.dart'; import '../models/data_abnormal_type.dart';
import '../theme/app_colors.dart';
class BatterySummary extends StatelessWidget { class BatterySummary extends StatelessWidget {
final double healthPercent; final double healthPercent;
@@ -19,25 +20,23 @@ class BatterySummary extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final c = context.colors;
final bool isAbnormal = abnormalType != DataAbnormalType.none; final bool isAbnormal = abnormalType != DataAbnormalType.none;
final bool isCapacityAbnormal = final bool isCapacityAbnormal =
abnormalType == DataAbnormalType.capacityTooLow || abnormalType == DataAbnormalType.capacityTooLow ||
abnormalType == DataAbnormalType.capacityTooHigh; abnormalType == DataAbnormalType.capacityTooHigh;
// 健康度颜色:异常时红色,否则按阈值
final Color healthColor = isAbnormal final Color healthColor = isAbnormal
? Colors.red ? c.healthRed
: (healthPercent < 70 ? Colors.red : Colors.black); : (healthPercent < 70 ? c.healthRed : c.textPrimary);
// 容量颜色:异常时红色,否则灰色
final Color capacityColor = isCapacityAbnormal final Color capacityColor = isCapacityAbnormal
? Colors.red ? c.healthRed
: const Color(0xFF5F5F5F); : c.textSecondary;
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
// 健康度大数字
Row( Row(
crossAxisAlignment: CrossAxisAlignment.baseline, crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic, textBaseline: TextBaseline.alphabetic,
@@ -60,34 +59,33 @@ class BatterySummary extends StatelessWidget {
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.w300, fontWeight: FontWeight.w300,
height: 1.0, height: 1.0,
color: healthColor == Colors.red color: healthColor == c.healthRed
? Colors.red ? c.healthRed
: Colors.grey.shade600, : c.textSecondary,
), ),
), ),
], ],
), ),
const Spacer(), const Spacer(),
// cycles + mAh
Column( Column(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
RichText( RichText(
text: TextSpan( text: TextSpan(
style: const TextStyle( style: TextStyle(
fontFamily: 'Flyme', fontFamily: 'Flyme',
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
height: 1.2, height: 1.2,
color: Colors.black, color: c.textPrimary,
), ),
children: [ children: [
TextSpan(text: '$cycleCount'), TextSpan(text: '$cycleCount'),
const TextSpan( TextSpan(
text: ' cycles', text: ' cycles',
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.black, color: c.textPrimary,
), ),
), ),
], ],
+44 -35
View File
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import '../utils/charge_utils.dart'; import '../utils/charge_utils.dart';
import '../theme/app_colors.dart';
class ChargeDetailPage extends StatelessWidget { class ChargeDetailPage extends StatelessWidget {
final String chargeType; final String chargeType;
@@ -41,50 +42,53 @@ class ChargeDetailPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final c = context.colors;
final isDark = Theme.of(context).brightness == Brightness.dark;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: const SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarColor: Color.fromRGBO(157, 212, 237, 0.1), statusBarColor: c.statusBarTint,
statusBarIconBrightness: Brightness.dark, statusBarIconBrightness: isDark ? Brightness.light : Brightness.dark,
statusBarBrightness: Brightness.dark, statusBarBrightness: Theme.of(context).brightness,
), ),
child: Scaffold( child: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar( appBar: AppBar(
title: const Text('充电详情'), title: const Text('充电详情'),
backgroundColor: Colors.white, backgroundColor: c.background,
elevation: 0, elevation: 0,
scrolledUnderElevation: 0, scrolledUnderElevation: 0,
shadowColor: Colors.transparent, shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent, surfaceTintColor: Colors.transparent,
foregroundColor: Colors.black, foregroundColor: c.textPrimary,
iconTheme: const IconThemeData(color: Colors.black), iconTheme: IconThemeData(color: c.textPrimary),
), ),
body: Padding( body: Padding(
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildTextRow('协议', _getProtocolDisplay()), _buildTextRow(context, '协议', _getProtocolDisplay()),
const Divider(), const Divider(),
if (!isWireless) ...[ if (!isWireless) ...[
_buildTextRow('标定档位', _getRequestedDisplay()), _buildTextRow(context, '标定档位', _getRequestedDisplay()),
const Divider(), const Divider(),
_buildTextRow('实际功率', _getActualDisplay()), _buildTextRow(context, '实际功率', _getActualDisplay()),
const Divider(), const Divider(),
] else ...[ ] else ...[
_buildTextRow('功率', ChargeUtils.getDisplayPowerWireless(power)), _buildTextRow(
context,
'功率',
ChargeUtils.getDisplayPowerWireless(power),
),
const Divider(), const Divider(),
], ],
_buildDetailRow(context, '电压', volt, 'V'),
_buildDetailRow('电压', volt, 'V'),
const Divider(), const Divider(),
_buildDetailRow('电流', curr, 'A'), _buildDetailRow(context, '电流', curr, 'A'),
const Divider(), const Divider(),
if (isWireless) _buildTextRow(context, '类型', '无线'),
if (isWireless) _buildTextRow('类型', '无线'), if (!isWireless && wiredLevel == null)
if (!isWireless && wiredLevel == null) _buildTextRow('档位', '自动'), _buildTextRow(context, '档位', '自动'),
const Spacer(), const Spacer(),
Center( Center(
child: Text( child: Text(
@@ -92,7 +96,7 @@ class ChargeDetailPage extends StatelessWidget {
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 12, fontSize: 12,
color: Colors.grey.shade500, color: c.textTertiary,
), ),
), ),
), ),
@@ -103,8 +107,8 @@ class ChargeDetailPage extends StatelessWidget {
); );
} }
/// 纯文本行(协议、功率、档位等) Widget _buildTextRow(BuildContext context, String label, String value) {
Widget _buildTextRow(String label, String value) { final c = context.colors;
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 6.0), padding: const EdgeInsets.symmetric(vertical: 6.0),
child: Row( child: Row(
@@ -112,20 +116,20 @@ class ChargeDetailPage extends StatelessWidget {
children: [ children: [
Text( Text(
label, label,
style: const TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: Colors.grey, color: c.textTertiary,
), ),
), ),
Text( Text(
value, value,
style: const TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', // 复杂字符串保持 Harmony fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Colors.black87, color: c.textPrimary,
), ),
), ),
], ],
@@ -133,8 +137,13 @@ class ChargeDetailPage extends StatelessWidget {
); );
} }
/// 数值行(电压/电流,数字用 Flyme,单位用 Harmony) Widget _buildDetailRow(
Widget _buildDetailRow(String label, double value, String unit) { BuildContext context,
String label,
double value,
String unit,
) {
final c = context.colors;
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 6.0), padding: const EdgeInsets.symmetric(vertical: 6.0),
child: Row( child: Row(
@@ -142,24 +151,24 @@ class ChargeDetailPage extends StatelessWidget {
children: [ children: [
Text( Text(
label, label,
style: const TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: Colors.grey, color: c.textTertiary,
), ),
), ),
RichText( RichText(
text: TextSpan( text: TextSpan(
style: const TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Colors.black87, color: c.textPrimary,
), ),
children: [ children: [
TextSpan( TextSpan(
text: value.toStringAsFixed(2), text: value.toStringAsFixed(2),
style: const TextStyle(fontFamily: 'Flyme'), // 🟢 数字用 Flyme style: const TextStyle(fontFamily: 'Flyme'),
), ),
TextSpan( TextSpan(
text: unit, text: unit,
+7 -5
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../theme/app_colors.dart';
class ChargeEntry extends StatelessWidget { class ChargeEntry extends StatelessWidget {
final String summary; final String summary;
@@ -14,6 +15,7 @@ class ChargeEntry extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final c = context.colors;
return GestureDetector( return GestureDetector(
onTap: isCharging ? onTap : null, onTap: isCharging ? onTap : null,
child: Row( child: Row(
@@ -21,11 +23,11 @@ class ChargeEntry extends StatelessWidget {
children: [ children: [
Text( Text(
summary, summary,
style: const TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', // 🟢 混合文本用 Harmony fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Colors.black87, color: c.textPrimary,
), ),
), ),
Row( Row(
@@ -36,7 +38,7 @@ class ChargeEntry extends StatelessWidget {
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Colors.grey.shade700, color: c.textTertiary,
), ),
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
@@ -46,7 +48,7 @@ class ChargeEntry extends StatelessWidget {
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Colors.grey.shade700, color: c.textTertiary,
), ),
), ),
], ],
+19 -21
View File
@@ -1,11 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../theme/app_colors.dart';
// --- 潘通色定义 ---
const Color kHealthGreen = Color(0xFF00B140);
const Color kHealthYellow = Color(0xFFEFDF00);
const Color kHealthOrange = Color(0xFFD75A16);
/// 趋势图柱状图组件
class TrendChart extends StatelessWidget { class TrendChart extends StatelessWidget {
final List<Map<String, dynamic>> data; final List<Map<String, dynamic>> data;
final int designCap; final int designCap;
@@ -14,6 +9,8 @@ class TrendChart extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final c = context.colors;
List<Map<String, dynamic>> displayData = data.isNotEmpty List<Map<String, dynamic>> displayData = data.isNotEmpty
? data ? data
: _getMockData(); : _getMockData();
@@ -48,12 +45,13 @@ class TrendChart extends StatelessWidget {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'容量百分比变动趋势(近15天)', '容量百分比变动趋势(近15天)',
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: c.textPrimary,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
@@ -64,13 +62,13 @@ class TrendChart extends StatelessWidget {
height: 180, height: 180,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [ children: [
Text( Text(
'100', '100',
style: TextStyle( style: TextStyle(
fontFamily: 'Flyme', fontFamily: 'Flyme',
fontSize: 10, fontSize: 10,
color: Colors.grey, color: c.textTertiary,
height: 1.0, height: 1.0,
), ),
), ),
@@ -79,7 +77,7 @@ class TrendChart extends StatelessWidget {
style: TextStyle( style: TextStyle(
fontFamily: 'Flyme', fontFamily: 'Flyme',
fontSize: 10, fontSize: 10,
color: Colors.grey, color: c.textTertiary,
height: 1.0, height: 1.0,
), ),
), ),
@@ -106,14 +104,14 @@ class TrendChart extends StatelessWidget {
Color barColor; Color barColor;
if (value > 0) { if (value > 0) {
if (value >= 80) { if (value >= 80) {
barColor = kHealthGreen; barColor = c.healthGreen;
} else if (value >= 70) { } else if (value >= 70) {
barColor = kHealthYellow; barColor = c.healthYellow;
} else { } else {
barColor = Colors.red; barColor = c.healthRed;
} }
} else { } else {
barColor = Colors.grey.shade300; barColor = c.barEmpty;
} }
double clampedValue = value.clamp(70.0, 100.0); double clampedValue = value.clamp(70.0, 100.0);
double heightFactor = double heightFactor =
@@ -144,10 +142,10 @@ class TrendChart extends StatelessWidget {
bottom: 2, bottom: 2,
child: Text( child: Text(
sevenDaysAgoDate, sevenDaysAgoDate,
style: const TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 9, fontSize: 9,
color: Colors.grey, color: c.textTertiary,
height: 1.0, height: 1.0,
), ),
), ),
@@ -158,10 +156,10 @@ class TrendChart extends StatelessWidget {
bottom: 2, bottom: 2,
child: Text( child: Text(
todayDate, todayDate,
style: const TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 9, fontSize: 9,
color: Colors.black, color: c.textPrimary,
height: 1.0, height: 1.0,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
@@ -176,14 +174,14 @@ class TrendChart extends StatelessWidget {
], ],
), ),
if (percents.every((v) => v == 0)) if (percents.every((v) => v == 0))
const Padding( Padding(
padding: EdgeInsets.only(top: 8.0), padding: const EdgeInsets.only(top: 8.0),
child: Text( child: Text(
'暂无历史数据,请点击刷新按钮更新', '暂无历史数据,请点击刷新按钮更新',
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 12, fontSize: 12,
color: Colors.grey, color: c.textTertiary,
), ),
), ),
), ),
+1 -1
View File
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 1.7.1 version: 1.8.0
environment: environment:
sdk: ^3.9.2 sdk: ^3.9.2