首次提交:关于页、夜间模式选项
This commit is contained in:
+58
-45
@@ -1,12 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'pages/battery_health_page.dart';
|
import 'pages/battery_health_page.dart';
|
||||||
import 'services/saf_storage_service.dart';
|
import 'services/saf_storage_service.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
await SafStorageService().init();
|
await SafStorageService().init();
|
||||||
|
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,51 +16,65 @@ class MyApp extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: '电池健康度',
|
title: '电池健康度',
|
||||||
theme: ThemeData(
|
theme: _lightTheme(),
|
||||||
// 🟢 中文用 HarmonyOS Sans SC
|
darkTheme: _darkTheme(),
|
||||||
fontFamily: 'HarmonyOS_Sans_SC',
|
themeMode: ThemeMode.system, // 默认跟随系统
|
||||||
// 🟢 数字回退到 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,
|
|
||||||
),
|
|
||||||
home: const BatteryHealthPage(),
|
home: const BatteryHealthPage(),
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThemeData _lightTheme() {
|
||||||
|
return ThemeData(
|
||||||
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
fontFamilyFallback: const ['Flyme'],
|
||||||
|
useMaterial3: true,
|
||||||
|
brightness: Brightness.light,
|
||||||
|
scaffoldBackgroundColor: Colors.white,
|
||||||
|
appBarTheme: const AppBarTheme(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
foregroundColor: Colors.black,
|
||||||
|
elevation: 0,
|
||||||
|
centerTitle: false,
|
||||||
|
),
|
||||||
|
iconTheme: const IconThemeData(color: Colors.black),
|
||||||
|
colorScheme: const ColorScheme.light(
|
||||||
|
primary: Color(0xFF1A73E8),
|
||||||
|
secondary: Color(0xFF1A73E8),
|
||||||
|
),
|
||||||
|
textTheme: const TextTheme(
|
||||||
|
bodyLarge: TextStyle(color: Colors.black),
|
||||||
|
bodyMedium: TextStyle(color: Colors.black87),
|
||||||
|
labelLarge: TextStyle(color: Colors.black),
|
||||||
|
),
|
||||||
|
dividerColor: Colors.grey.shade200,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThemeData _darkTheme() {
|
||||||
|
return ThemeData(
|
||||||
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
fontFamilyFallback: const ['Flyme'],
|
||||||
|
useMaterial3: true,
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
scaffoldBackgroundColor: const Color(0xFF121212),
|
||||||
|
appBarTheme: const AppBarTheme(
|
||||||
|
backgroundColor: Color(0xFF1E1E1E),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
elevation: 0,
|
||||||
|
centerTitle: false,
|
||||||
|
),
|
||||||
|
iconTheme: const IconThemeData(color: Colors.white),
|
||||||
|
colorScheme: const ColorScheme.dark(
|
||||||
|
primary: Color(0xFF4FC3F7),
|
||||||
|
secondary: Color(0xFF4FC3F7),
|
||||||
|
),
|
||||||
|
textTheme: const TextTheme(
|
||||||
|
bodyLarge: TextStyle(color: Colors.white),
|
||||||
|
bodyMedium: TextStyle(color: Colors.white70),
|
||||||
|
labelLarge: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
dividerColor: Colors.grey.shade800,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
class AboutPage extends StatelessWidget {
|
||||||
|
const AboutPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text(
|
||||||
|
'关于',
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
elevation: 0,
|
||||||
|
foregroundColor: Theme.of(context).iconTheme.color,
|
||||||
|
iconTheme: IconThemeData(color: Theme.of(context).iconTheme.color),
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 40.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
// Logo
|
||||||
|
Container(
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: isDark
|
||||||
|
? Colors.grey.shade800
|
||||||
|
: const Color(0xFFF0F4F8),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.battery_charging_full,
|
||||||
|
size: 64,
|
||||||
|
color: Color(0xFF00B140),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
// APP 名称
|
||||||
|
const Text(
|
||||||
|
'MEIZU Battery Healthy',
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
// 版本号
|
||||||
|
Text(
|
||||||
|
'版本 1.7.0',
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey.shade500,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
// 分隔线
|
||||||
|
Divider(
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
thickness: 0.5,
|
||||||
|
indent: 40,
|
||||||
|
endIndent: 40,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
// 作者信息
|
||||||
|
const Text(
|
||||||
|
'专机专配 · 仅针对魅族独家开发',
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'通过读取 BMS 日志直接获取电池真实数据',
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
fontSize: 13,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
// Gitea 链接
|
||||||
|
InkWell(
|
||||||
|
onTap: _launchUrl,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 20,
|
||||||
|
vertical: 12,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isDark
|
||||||
|
? Colors.grey.shade800
|
||||||
|
: const Color(0xFFF0F4F8),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.code,
|
||||||
|
size: 18,
|
||||||
|
color: Theme.of(context).iconTheme.color,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'查看源码 / 反馈',
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
fontSize: 14,
|
||||||
|
color: Theme.of(context).iconTheme.color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Icon(
|
||||||
|
Icons.open_in_new,
|
||||||
|
size: 14,
|
||||||
|
color: Colors.grey.shade500,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 40),
|
||||||
|
// 底部版本信息
|
||||||
|
Text(
|
||||||
|
'Made with ❤️ in Flyme · 2026',
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 🟢 修复:url_launcher 的正确调用方式
|
||||||
|
Future<void> _launchUrl() async {
|
||||||
|
const url = 'https://git.whitetop.xyz/lxh2875931338/MEIZU-Battery-Healthy';
|
||||||
|
final uri = Uri.parse(url);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (await canLaunchUrl(uri)) {
|
||||||
|
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||||
|
} else {
|
||||||
|
// 降级方案:无法打开链接时的处理
|
||||||
|
debugPrint('无法打开链接: $url');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('打开链接失败: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import 'package:device_info_plus/device_info_plus.dart';
|
|||||||
// --- 本地导入 ---
|
// --- 本地导入 ---
|
||||||
import '../models/device_matching.dart';
|
import '../models/device_matching.dart';
|
||||||
import '../models/saf_permission_state.dart';
|
import '../models/saf_permission_state.dart';
|
||||||
import '../models/data_abnormal_type.dart'; // 🆕 1.7.0
|
import '../models/data_abnormal_type.dart';
|
||||||
import '../services/database_service.dart';
|
import '../services/database_service.dart';
|
||||||
import '../services/battery_data_service.dart';
|
import '../services/battery_data_service.dart';
|
||||||
import '../services/saf_storage_service.dart';
|
import '../services/saf_storage_service.dart';
|
||||||
@@ -21,6 +21,7 @@ import '../widgets/charge_entry.dart';
|
|||||||
import '../widgets/charge_detail_page.dart';
|
import '../widgets/charge_detail_page.dart';
|
||||||
import '../widgets/trend_chart.dart';
|
import '../widgets/trend_chart.dart';
|
||||||
import '../pages/changelog_page.dart';
|
import '../pages/changelog_page.dart';
|
||||||
|
import '../pages/about_page.dart';
|
||||||
|
|
||||||
// --- 潘通色定义 ---
|
// --- 潘通色定义 ---
|
||||||
const Color kHealthGreen = Color(0xFF00B140);
|
const Color kHealthGreen = Color(0xFF00B140);
|
||||||
@@ -48,6 +49,9 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
|||||||
double _minVolt = 0;
|
double _minVolt = 0;
|
||||||
bool _hasData = false;
|
bool _hasData = false;
|
||||||
|
|
||||||
|
// ---------- 🆕 1.7.0 异常状态标记 ----------
|
||||||
|
bool _hasAbnormalDataToday = false;
|
||||||
|
|
||||||
// ---------- 趋势图数据 ----------
|
// ---------- 趋势图数据 ----------
|
||||||
List<Map<String, dynamic>> _trendData = [];
|
List<Map<String, dynamic>> _trendData = [];
|
||||||
|
|
||||||
@@ -293,6 +297,9 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
|||||||
Future<void> readBatteryCapacity() async {
|
Future<void> readBatteryCapacity() async {
|
||||||
dev.log('🔴🔴🔴 readBatteryCapacity 被调用了 🔴🔴🔴', name: 'BatteryHealth');
|
dev.log('🔴🔴🔴 readBatteryCapacity 被调用了 🔴🔴🔴', name: 'BatteryHealth');
|
||||||
|
|
||||||
|
// 🟢 每次读取时重置异常标记
|
||||||
|
_hasAbnormalDataToday = false;
|
||||||
|
|
||||||
_safState = await _safService.getPermissionState();
|
_safState = await _safService.getPermissionState();
|
||||||
dev.log('🔵 SAF状态: $_safState', name: 'BatteryHealth');
|
dev.log('🔵 SAF状态: $_safState', name: 'BatteryHealth');
|
||||||
|
|
||||||
@@ -364,29 +371,54 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
|||||||
name: 'BatteryHealth',
|
name: 'BatteryHealth',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 🆕 1.7.0 计算异常比例
|
||||||
|
final abnormalRatio = _calculateAbnormalRatio(lines, _designCap);
|
||||||
|
dev.log(
|
||||||
|
'🔵 异常比例: ${(abnormalRatio * 100).toStringAsFixed(1)}%',
|
||||||
|
name: 'BatteryHealth',
|
||||||
|
);
|
||||||
|
|
||||||
final cap = CapacityExtractor.extractCapacity(lines);
|
final cap = CapacityExtractor.extractCapacity(lines);
|
||||||
int? finalCap;
|
int? finalCap;
|
||||||
|
|
||||||
if (cap != null) {
|
// 🆕 1.7.0 异常处理逻辑
|
||||||
|
if (cap != null && abnormalRatio < 0.5) {
|
||||||
|
// 短时尖刺:走校验
|
||||||
|
final validatedCap = _validateCapacity(cap, _designCap);
|
||||||
|
if (validatedCap != null) {
|
||||||
|
finalCap = validatedCap;
|
||||||
|
_hasAbnormalDataToday = false;
|
||||||
|
dev.log('🔵 短时尖刺校验通过: $cap', name: 'BatteryHealth');
|
||||||
|
} else {
|
||||||
|
finalCap = null;
|
||||||
|
_hasAbnormalDataToday = true;
|
||||||
|
dev.log('⚠️ 短时尖刺,已丢弃: $cap', name: 'BatteryHealth');
|
||||||
|
}
|
||||||
|
} else if (cap != null && abnormalRatio >= 0.5) {
|
||||||
|
// 长期偏离:放行(疑似扩容)
|
||||||
finalCap = cap;
|
finalCap = cap;
|
||||||
dev.log('🔵 从日志提取容量: $cap', name: 'BatteryHealth');
|
_hasAbnormalDataToday = false;
|
||||||
|
dev.log(
|
||||||
|
'🔵 长期偏离(${(abnormalRatio * 100).toStringAsFixed(0)}% 记录 > 115%),疑似扩容电池,已放行: $cap',
|
||||||
|
name: 'BatteryHealth',
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
|
// cap == null,尝试复制前一天
|
||||||
dev.log('🔵 今日无充满记录,尝试复制前一天容量', name: 'BatteryHealth');
|
dev.log('🔵 今日无充满记录,尝试复制前一天容量', name: 'BatteryHealth');
|
||||||
final recent = await _dbService.queryRecentData(1);
|
final recent = await _dbService.queryRecentData(1);
|
||||||
if (recent.isNotEmpty) {
|
if (recent.isNotEmpty) {
|
||||||
finalCap = recent.first['capacity'] as int;
|
finalCap = recent.first['capacity'] as int;
|
||||||
|
_hasAbnormalDataToday = false;
|
||||||
dev.log('🔵 复制前一天容量: $finalCap', name: 'BatteryHealth');
|
dev.log('🔵 复制前一天容量: $finalCap', name: 'BatteryHealth');
|
||||||
} else {
|
} else {
|
||||||
finalCap = null;
|
finalCap = null;
|
||||||
|
_hasAbnormalDataToday = false;
|
||||||
dev.log('🔵 无历史数据可复制', name: 'BatteryHealth');
|
dev.log('🔵 无历史数据可复制', name: 'BatteryHealth');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🟢 1.7.0 容量有效性校验
|
|
||||||
final validatedCap = _validateCapacity(finalCap, _designCap);
|
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_currentCap = (validatedCap ?? _currentCap).toDouble();
|
_currentCap = (finalCap ?? _currentCap).toDouble();
|
||||||
_cycleCount = cycle;
|
_cycleCount = cycle;
|
||||||
_batteryTemp = temp;
|
_batteryTemp = temp;
|
||||||
_batteryVolt = volt;
|
_batteryVolt = volt;
|
||||||
@@ -413,17 +445,40 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------- 🆕 1.7.0 统计当天超过 115% 的容量记录占比 ----------
|
||||||
|
double _calculateAbnormalRatio(List<String> lines, int designCap) {
|
||||||
|
final capReg = RegExp(r'(?:learned_cap|remaining_cap)=(\d+)');
|
||||||
|
int totalCount = 0;
|
||||||
|
int abnormalCount = 0;
|
||||||
|
|
||||||
|
for (String line in lines) {
|
||||||
|
if (!line.contains('tbat=')) continue;
|
||||||
|
|
||||||
|
final match = capReg.firstMatch(line);
|
||||||
|
if (match == null) continue;
|
||||||
|
|
||||||
|
final value = int.parse(match.group(1)!);
|
||||||
|
final capInMah = value > 100000 ? value ~/ 1000 : value;
|
||||||
|
|
||||||
|
totalCount++;
|
||||||
|
if (capInMah > designCap * 1.15) {
|
||||||
|
abnormalCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalCount == 0) return 0.0;
|
||||||
|
return abnormalCount / totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------- 🆕 1.7.0 容量有效性校验 ----------
|
// ---------- 🆕 1.7.0 容量有效性校验 ----------
|
||||||
int? _validateCapacity(int? capacity, int designCap) {
|
int? _validateCapacity(int? capacity, int designCap) {
|
||||||
if (capacity == null) return null;
|
if (capacity == null) return null;
|
||||||
|
|
||||||
// 容量必须大于 0
|
|
||||||
if (capacity <= 0) {
|
if (capacity <= 0) {
|
||||||
dev.log('⚠️ 容量异常:<= 0,已忽略', name: 'BatteryHealth');
|
dev.log('⚠️ 容量异常:<= 0,已忽略', name: 'BatteryHealth');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 容量不能低于设计容量的 60%
|
|
||||||
if (capacity < designCap * 0.6) {
|
if (capacity < designCap * 0.6) {
|
||||||
dev.log(
|
dev.log(
|
||||||
'⚠️ 容量异常:低于设计容量 60%($capacity < ${designCap * 0.6}),已忽略',
|
'⚠️ 容量异常:低于设计容量 60%($capacity < ${designCap * 0.6}),已忽略',
|
||||||
@@ -432,7 +487,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 容量不能超过设计容量的 115%
|
|
||||||
if (capacity > designCap * 1.15) {
|
if (capacity > designCap * 1.15) {
|
||||||
dev.log(
|
dev.log(
|
||||||
'⚠️ 容量异常:高于设计容量 115%($capacity > ${designCap * 1.15}),已忽略',
|
'⚠️ 容量异常:高于设计容量 115%($capacity > ${designCap * 1.15}),已忽略',
|
||||||
@@ -449,12 +503,14 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
|||||||
double currentCap,
|
double currentCap,
|
||||||
int designCap,
|
int designCap,
|
||||||
double voltage,
|
double voltage,
|
||||||
|
bool hasAbnormalDataToday,
|
||||||
) {
|
) {
|
||||||
// 容量异常:高于 115% 或低于 60%
|
// 🟢 优先:今天有异常数据被丢弃 → 强制显示红色
|
||||||
|
if (hasAbnormalDataToday) return DataAbnormalType.capacityTooHigh;
|
||||||
|
|
||||||
|
// 其次:当前值异常
|
||||||
if (currentCap > designCap * 1.15) return DataAbnormalType.capacityTooHigh;
|
if (currentCap > designCap * 1.15) return DataAbnormalType.capacityTooHigh;
|
||||||
if (currentCap < designCap * 0.6) return DataAbnormalType.capacityTooLow;
|
if (currentCap < designCap * 0.6) return DataAbnormalType.capacityTooLow;
|
||||||
|
|
||||||
// 电压异常:低于 3.0V 或高于 5.0V
|
|
||||||
if (voltage < 3.0 || voltage > 5.0) return DataAbnormalType.voltageAbnormal;
|
if (voltage < 3.0 || voltage > 5.0) return DataAbnormalType.voltageAbnormal;
|
||||||
|
|
||||||
return DataAbnormalType.none;
|
return DataAbnormalType.none;
|
||||||
@@ -611,6 +667,8 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
|||||||
// ---------- Build ----------
|
// ---------- Build ----------
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
|
|
||||||
final double healthPercent = (_designCap > 0)
|
final double healthPercent = (_designCap > 0)
|
||||||
? (_currentCap / _designCap) * 100
|
? (_currentCap / _designCap) * 100
|
||||||
: 0.0;
|
: 0.0;
|
||||||
@@ -619,45 +677,52 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
|||||||
? '充电中 · ${_getChargeTypeLabel()} · ${_getDisplayPower()}'
|
? '充电中 · ${_getChargeTypeLabel()} · ${_getDisplayPower()}'
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
// 🆕 1.7.0 异常检测
|
|
||||||
final abnormalType = _checkAbnormalType(
|
final abnormalType = _checkAbnormalType(
|
||||||
_currentCap,
|
_currentCap,
|
||||||
_designCap,
|
_designCap,
|
||||||
_batteryVolt,
|
_batteryVolt,
|
||||||
|
_hasAbnormalDataToday,
|
||||||
);
|
);
|
||||||
|
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: const SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarColor: Color.fromRGBO(157, 212, 237, 0.1),
|
statusBarColor: isDark
|
||||||
statusBarIconBrightness: Brightness.dark,
|
? const Color(0xFF1E1E1E)
|
||||||
statusBarBrightness: Brightness.dark,
|
: const Color.fromRGBO(157, 212, 237, 0.1),
|
||||||
|
statusBarIconBrightness: isDark ? Brightness.light : Brightness.dark,
|
||||||
|
statusBarBrightness: isDark ? Brightness.light : Brightness.dark,
|
||||||
),
|
),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Theme.of(context).appBarTheme.backgroundColor,
|
||||||
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: isDark ? Colors.white : Colors.black,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.refresh),
|
icon: Icon(
|
||||||
|
Icons.refresh,
|
||||||
|
color: isDark ? Colors.white : Colors.black,
|
||||||
|
),
|
||||||
onPressed: readBatteryCapacity,
|
onPressed: readBatteryCapacity,
|
||||||
tooltip: '刷新数据',
|
tooltip: '刷新数据',
|
||||||
color: Colors.black,
|
|
||||||
),
|
),
|
||||||
PopupMenuButton<String>(
|
PopupMenuButton<String>(
|
||||||
icon: const Icon(Icons.more_vert),
|
icon: Icon(
|
||||||
color: Colors.white,
|
Icons.more_vert,
|
||||||
|
color: isDark ? Colors.white : Colors.black,
|
||||||
|
),
|
||||||
|
color: isDark ? const Color(0xFF2C2C2C) : Colors.white,
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 'changelog':
|
case 'changelog':
|
||||||
@@ -668,24 +733,23 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'dark_mode':
|
|
||||||
break;
|
|
||||||
case 'about':
|
case 'about':
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const AboutPage(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'dark_mode':
|
||||||
|
// 夜间模式切换逻辑(需要全局状态管理)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
itemBuilder: (context) => [
|
itemBuilder: (context) => [
|
||||||
const PopupMenuItem(value: 'changelog', child: Text('更新日志')),
|
const PopupMenuItem(value: 'changelog', child: Text('更新日志')),
|
||||||
const PopupMenuItem(
|
const PopupMenuItem(value: 'dark_mode', child: Text('夜间模式')),
|
||||||
value: 'dark_mode',
|
const PopupMenuItem(value: 'about', child: Text('关于')),
|
||||||
enabled: false,
|
|
||||||
child: Text('深色模式(开发中)'),
|
|
||||||
),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 'about',
|
|
||||||
enabled: false,
|
|
||||||
child: Text('关于(开发中)'),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -701,22 +765,23 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
_deviceModel,
|
_deviceModel,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: Colors.grey,
|
color: isDark
|
||||||
|
? Colors.grey.shade400
|
||||||
|
: Colors.grey,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
|
|
||||||
// 🟢 BatterySummary(带异常检测)
|
|
||||||
RepaintBoundary(
|
RepaintBoundary(
|
||||||
child: BatterySummary(
|
child: BatterySummary(
|
||||||
healthPercent: healthPercent,
|
healthPercent: healthPercent,
|
||||||
cycleCount: _cycleCount,
|
cycleCount: _cycleCount,
|
||||||
currentCap: _currentCap,
|
currentCap: _currentCap,
|
||||||
designCap: _designCap,
|
designCap: _designCap,
|
||||||
abnormalType: abnormalType, // 🆕 1.7.0
|
abnormalType: abnormalType,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -761,7 +826,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
|||||||
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
|
|
||||||
// 🟢 BatteryInfoPanel(带异常检测)
|
|
||||||
RepaintBoundary(
|
RepaintBoundary(
|
||||||
child: BatteryInfoPanel(
|
child: BatteryInfoPanel(
|
||||||
batteryVolt: _batteryVolt,
|
batteryVolt: _batteryVolt,
|
||||||
@@ -770,7 +834,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
|||||||
minVolt: _minVolt,
|
minVolt: _minVolt,
|
||||||
maxTemp: _maxTemp,
|
maxTemp: _maxTemp,
|
||||||
minTemp: _minTemp,
|
minTemp: _minTemp,
|
||||||
abnormalType: abnormalType, // 🆕 1.7.0
|
abnormalType: abnormalType,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -779,19 +843,29 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: const Center(
|
: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.battery_unknown, size: 64, color: Colors.grey),
|
Icon(
|
||||||
SizedBox(height: 16),
|
Icons.battery_unknown,
|
||||||
|
size: 64,
|
||||||
|
color: isDark ? Colors.grey.shade600 : Colors.grey,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
'暂无数据',
|
'暂无数据',
|
||||||
style: TextStyle(fontSize: 18, color: Colors.grey),
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
color: isDark ? Colors.grey.shade400 : Colors.grey,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'点击右上角刷新尝试',
|
'点击右上角刷新尝试',
|
||||||
style: TextStyle(fontSize: 14, color: Colors.grey),
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: isDark ? Colors.grey.shade500 : Colors.grey,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
url_launcher_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ import Foundation
|
|||||||
import device_info_plus
|
import device_info_plus
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import sqflite_darwin
|
import sqflite_darwin
|
||||||
|
import url_launcher_macos
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -597,6 +597,70 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
|
url_launcher:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: url_launcher
|
||||||
|
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "6.3.2"
|
||||||
|
url_launcher_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_android
|
||||||
|
sha256: b413d49b73867ac08dd2f9890efd3cc11f2a0e577618d50843440a1fb3776c32
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "6.3.32"
|
||||||
|
url_launcher_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_ios
|
||||||
|
sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "6.4.1"
|
||||||
|
url_launcher_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_linux
|
||||||
|
sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.2"
|
||||||
|
url_launcher_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_macos
|
||||||
|
sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.5"
|
||||||
|
url_launcher_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_platform_interface
|
||||||
|
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
url_launcher_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_web
|
||||||
|
sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.3"
|
||||||
|
url_launcher_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_windows
|
||||||
|
sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.5"
|
||||||
vector_graphics:
|
vector_graphics:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ dependencies:
|
|||||||
flutter_svg: ^2.0.10+1
|
flutter_svg: ^2.0.10+1
|
||||||
shared_preferences: ^2.2.0
|
shared_preferences: ^2.2.0
|
||||||
flutter_markdown: ^0.7.0
|
flutter_markdown: ^0.7.0
|
||||||
|
url_launcher: ^6.3.0
|
||||||
|
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
|
|||||||
@@ -7,8 +7,11 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||||
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||||
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
permission_handler_windows
|
permission_handler_windows
|
||||||
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
Reference in New Issue
Block a user