更新日志界面优化、主页部分动画调整
This commit is contained in:
@@ -54,7 +54,7 @@
|
|||||||
flutter pub get
|
flutter pub get
|
||||||
|
|
||||||
# Debug 版本
|
# Debug 版本
|
||||||
flutter run
|
flutter run --debug
|
||||||
|
|
||||||
# Release APK
|
# Release APK
|
||||||
flutter build apk --release --target-platform android-arm64
|
flutter build apk --release --target-platform android-arm64
|
||||||
|
|||||||
+109
-108
@@ -18,8 +18,8 @@ import '../widgets/battery_summary.dart';
|
|||||||
import '../widgets/battery_info_panel.dart';
|
import '../widgets/battery_info_panel.dart';
|
||||||
import '../widgets/charge_entry.dart';
|
import '../widgets/charge_entry.dart';
|
||||||
import '../widgets/charge_detail_page.dart';
|
import '../widgets/charge_detail_page.dart';
|
||||||
import '../widgets/battery_data_layout.dart'; // 🆕 1.6.0 UI 拆分
|
import '../widgets/trend_chart.dart';
|
||||||
import '../pages/changelog_page.dart'; // 🆕 1.6.0 更新日志
|
import '../pages/changelog_page.dart';
|
||||||
|
|
||||||
// --- 潘通色定义 ---
|
// --- 潘通色定义 ---
|
||||||
const Color kHealthGreen = Color(0xFF00B140);
|
const Color kHealthGreen = Color(0xFF00B140);
|
||||||
@@ -33,8 +33,7 @@ class BatteryHealthPage extends StatefulWidget {
|
|||||||
State<BatteryHealthPage> createState() => _BatteryHealthPageState();
|
State<BatteryHealthPage> createState() => _BatteryHealthPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BatteryHealthPageState extends State<BatteryHealthPage>
|
class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
||||||
with SingleTickerProviderStateMixin {
|
|
||||||
// ---------- 数据 ----------
|
// ---------- 数据 ----------
|
||||||
double _currentCap = 0;
|
double _currentCap = 0;
|
||||||
int _cycleCount = 0;
|
int _cycleCount = 0;
|
||||||
@@ -67,11 +66,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
final BatteryDataService _dataService = BatteryDataService();
|
final BatteryDataService _dataService = BatteryDataService();
|
||||||
SafPermissionState _safState = SafPermissionState.unauthorized;
|
SafPermissionState _safState = SafPermissionState.unauthorized;
|
||||||
|
|
||||||
// 动画控制
|
|
||||||
late AnimationController _chargeAnimationController;
|
|
||||||
late Animation<double> _bottomSlide;
|
|
||||||
late Animation<double> _entryFadeIn;
|
|
||||||
|
|
||||||
// 平台通道
|
// 平台通道
|
||||||
static const _methodChannel = MethodChannel('com.lxh.battery_health/charge');
|
static const _methodChannel = MethodChannel('com.lxh.battery_health/charge');
|
||||||
static const _eventChannel = EventChannel(
|
static const _eventChannel = EventChannel(
|
||||||
@@ -88,8 +82,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
super.initState();
|
super.initState();
|
||||||
dev.log('🔵 ========== initState 开始 ==========', name: 'BatteryHealth');
|
dev.log('🔵 ========== initState 开始 ==========', name: 'BatteryHealth');
|
||||||
|
|
||||||
_initAnimations();
|
|
||||||
|
|
||||||
_getInitialChargeState();
|
_getInitialChargeState();
|
||||||
|
|
||||||
_chargeEventSubscription = _eventChannel.receiveBroadcastStream().listen((
|
_chargeEventSubscription = _eventChannel.receiveBroadcastStream().listen((
|
||||||
@@ -135,31 +127,10 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
_chargeEventSubscription?.cancel();
|
_chargeEventSubscription?.cancel();
|
||||||
_pollTimer?.cancel();
|
_pollTimer?.cancel();
|
||||||
_chargeAnimationController.dispose();
|
|
||||||
_dbService.database.then((db) => db.close()).ignore();
|
_dbService.database.then((db) => db.close()).ignore();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 动画初始化 ----------
|
|
||||||
void _initAnimations() {
|
|
||||||
_chargeAnimationController = AnimationController(
|
|
||||||
vsync: this,
|
|
||||||
duration: const Duration(milliseconds: 500),
|
|
||||||
);
|
|
||||||
_bottomSlide = Tween<double>(begin: 0, end: 24).animate(
|
|
||||||
CurvedAnimation(
|
|
||||||
parent: _chargeAnimationController,
|
|
||||||
curve: Curves.easeInOutCubic,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
_entryFadeIn = Tween<double>(begin: 0, end: 1).animate(
|
|
||||||
CurvedAnimation(
|
|
||||||
parent: _chargeAnimationController,
|
|
||||||
curve: const Interval(0.0, 0.5, curve: Curves.easeIn),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- 设备信息 ----------
|
// ---------- 设备信息 ----------
|
||||||
Future<void> _getDeviceInfo() async {
|
Future<void> _getDeviceInfo() async {
|
||||||
dev.log('🔵 _getDeviceInfo 开始', name: 'BatteryHealth');
|
dev.log('🔵 _getDeviceInfo 开始', name: 'BatteryHealth');
|
||||||
@@ -276,7 +247,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
_chargeCurr = 0.0;
|
_chargeCurr = 0.0;
|
||||||
_wiredLevel = null;
|
_wiredLevel = null;
|
||||||
_chargingStartTime = null;
|
_chargingStartTime = null;
|
||||||
_updateChargeState();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,7 +281,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
_chargePower = 0.0;
|
_chargePower = 0.0;
|
||||||
_chargeVolt = 0.0;
|
_chargeVolt = 0.0;
|
||||||
_chargeCurr = 0.0;
|
_chargeCurr = 0.0;
|
||||||
_updateChargeState();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (_isCharging) {
|
if (_isCharging) {
|
||||||
@@ -350,7 +319,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
_currentCap = recent.first['capacity'].toDouble();
|
_currentCap = recent.first['capacity'].toDouble();
|
||||||
_maxVolt = recent.first['volt_max'] ?? 0;
|
_maxVolt = recent.first['volt_max'] ?? 0;
|
||||||
_minVolt = recent.first['volt_min'] ?? 0;
|
_minVolt = recent.first['volt_min'] ?? 0;
|
||||||
// 🟢 数据库目前不存温度,后续升级可扩展,暂时保留为 0
|
|
||||||
_maxTemp = 0;
|
_maxTemp = 0;
|
||||||
_minTemp = 0;
|
_minTemp = 0;
|
||||||
_cycleCount = 0;
|
_cycleCount = 0;
|
||||||
@@ -362,8 +330,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
setState(() => _hasData = false);
|
setState(() => _hasData = false);
|
||||||
}
|
}
|
||||||
if (_isCharging) _fetchChargeDataFromLog();
|
if (_isCharging) _fetchChargeDataFromLog();
|
||||||
_chargeAnimationController.reset();
|
|
||||||
_updateChargeState();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,23 +341,16 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
dev.log('🔵 文件为空', name: 'BatteryHealth');
|
dev.log('🔵 文件为空', name: 'BatteryHealth');
|
||||||
setState(() => _hasData = false);
|
setState(() => _hasData = false);
|
||||||
if (_isCharging) _fetchChargeDataFromLog();
|
if (_isCharging) _fetchChargeDataFromLog();
|
||||||
_chargeAnimationController.reset();
|
|
||||||
_updateChargeState();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🟢 提取所有有效行(包含 tbat=)
|
|
||||||
final validLines = lines.where((line) => line.contains('tbat=')).toList();
|
final validLines = lines.where((line) => line.contains('tbat=')).toList();
|
||||||
if (validLines.isEmpty) {
|
if (validLines.isEmpty) {
|
||||||
dev.log('🔵 无有效数据行', name: 'BatteryHealth');
|
dev.log('🔵 无有效数据行', name: 'BatteryHealth');
|
||||||
setState(() => _hasData = false);
|
setState(() => _hasData = false);
|
||||||
_chargeAnimationController.reset();
|
|
||||||
_updateChargeState();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🟢 1.4.7 核心改动:字段提取与容量提取解耦
|
|
||||||
// 无论 cap 是否为 null,都提取常规字段
|
|
||||||
final lastLine = validLines.last;
|
final lastLine = validLines.last;
|
||||||
final cycle = _extractCycle(lastLine);
|
final cycle = _extractCycle(lastLine);
|
||||||
final temp = _extractTemp(lastLine);
|
final temp = _extractTemp(lastLine);
|
||||||
@@ -404,16 +363,13 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
name: 'BatteryHealth',
|
name: 'BatteryHealth',
|
||||||
);
|
);
|
||||||
|
|
||||||
// 🟢 容量提取(可能为 null)
|
|
||||||
final cap = CapacityExtractor.extractCapacity(lines);
|
final cap = CapacityExtractor.extractCapacity(lines);
|
||||||
int? finalCap;
|
int? finalCap;
|
||||||
|
|
||||||
if (cap != null) {
|
if (cap != null) {
|
||||||
// ✅ 今日有充满记录,直接使用
|
|
||||||
finalCap = cap;
|
finalCap = cap;
|
||||||
dev.log('🔵 从日志提取容量: $cap', name: 'BatteryHealth');
|
dev.log('🔵 从日志提取容量: $cap', name: 'BatteryHealth');
|
||||||
} else {
|
} else {
|
||||||
// ⚠️ 今日无充满记录,尝试从数据库复制前一天容量
|
|
||||||
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) {
|
||||||
@@ -425,7 +381,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🟢 一次性 setState 更新所有字段
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_currentCap = (finalCap ?? _currentCap).toDouble();
|
_currentCap = (finalCap ?? _currentCap).toDouble();
|
||||||
_cycleCount = cycle;
|
_cycleCount = cycle;
|
||||||
@@ -444,8 +399,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _updateTrendChart();
|
await _updateTrendChart();
|
||||||
_chargeAnimationController.reset();
|
|
||||||
_updateChargeState();
|
|
||||||
dev.log(
|
dev.log(
|
||||||
'🔵 ========== readBatteryCapacity 完成 ==========',
|
'🔵 ========== readBatteryCapacity 完成 ==========',
|
||||||
name: 'BatteryHealth',
|
name: 'BatteryHealth',
|
||||||
@@ -453,8 +406,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
dev.log('🔵 readBatteryCapacity 异常: $e', name: 'BatteryHealth');
|
dev.log('🔵 readBatteryCapacity 异常: $e', name: 'BatteryHealth');
|
||||||
setState(() => _hasData = false);
|
setState(() => _hasData = false);
|
||||||
_chargeAnimationController.reset();
|
|
||||||
_updateChargeState();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,26 +539,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 动画控制 ----------
|
|
||||||
void _startChargeAnimation() {
|
|
||||||
if (_chargeAnimationController.isAnimating) return;
|
|
||||||
_chargeAnimationController.forward();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _stopChargeAnimation() {
|
|
||||||
if (_chargeAnimationController.isAnimating) return;
|
|
||||||
_chargeAnimationController.reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateChargeState() {
|
|
||||||
if (!mounted) return;
|
|
||||||
if (_isCharging) {
|
|
||||||
_startChargeAnimation();
|
|
||||||
} else {
|
|
||||||
_stopChargeAnimation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- 显示辅助 ----------
|
// ---------- 显示辅助 ----------
|
||||||
String _getDisplayPower() {
|
String _getDisplayPower() {
|
||||||
if (!_isCharging) return '';
|
if (!_isCharging) return '';
|
||||||
@@ -629,7 +560,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
// ---------- Build ----------
|
// ---------- Build ----------
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// 计算健康度百分比(显式转为 double)
|
|
||||||
final double healthPercent = (_designCap > 0)
|
final double healthPercent = (_designCap > 0)
|
||||||
? (_currentCap / _designCap) * 100
|
? (_currentCap / _designCap) * 100
|
||||||
: 0.0;
|
: 0.0;
|
||||||
@@ -661,14 +591,12 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
// 刷新按钮(左移)
|
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.refresh),
|
icon: const Icon(Icons.refresh),
|
||||||
onPressed: readBatteryCapacity,
|
onPressed: readBatteryCapacity,
|
||||||
tooltip: '刷新数据',
|
tooltip: '刷新数据',
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
),
|
),
|
||||||
// 三点菜单
|
|
||||||
PopupMenuButton<String>(
|
PopupMenuButton<String>(
|
||||||
icon: const Icon(Icons.more_vert),
|
icon: const Icon(Icons.more_vert),
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
@@ -683,10 +611,8 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'dark_mode':
|
case 'dark_mode':
|
||||||
// 1.7 再实现
|
|
||||||
break;
|
break;
|
||||||
case 'about':
|
case 'about':
|
||||||
// 1.7 再实现
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -707,38 +633,113 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: _hasData
|
body: _hasData
|
||||||
? AnimatedBuilder(
|
? Column(
|
||||||
animation: _chargeAnimationController,
|
children: [
|
||||||
builder: (context, child) {
|
// 🟢 可滚动区域(带 RepaintBoundary)
|
||||||
// 🟢 修复:传入 healthPercent 为 double,移除未定义的动画参数
|
Expanded(
|
||||||
// 动画在 BatteryDataLayout 内部不处理,由父级 AnimatedBuilder 控制
|
child: SingleChildScrollView(
|
||||||
return BatteryDataLayout(
|
padding: const EdgeInsets.all(20.0),
|
||||||
deviceModel: _deviceModel,
|
child: Column(
|
||||||
healthPercent: healthPercent,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
cycleCount: _cycleCount,
|
children: [
|
||||||
currentCap: _currentCap,
|
// 设备型号
|
||||||
designCap: _designCap,
|
Text(
|
||||||
trendData: _trendData,
|
_deviceModel,
|
||||||
isCharging: _isCharging,
|
style: const TextStyle(
|
||||||
chargeSummary: chargeSummary,
|
fontSize: 14,
|
||||||
batteryVolt: _batteryVolt,
|
color: Colors.grey,
|
||||||
batteryTemp: _batteryTemp,
|
fontWeight: FontWeight.w400,
|
||||||
maxVolt: _maxVolt,
|
),
|
||||||
minVolt: _minVolt,
|
),
|
||||||
maxTemp: _maxTemp,
|
const SizedBox(height: 4),
|
||||||
minTemp: _minTemp,
|
|
||||||
chargeType: _chargeType,
|
// 🟢 BatterySummary 用 RepaintBoundary 隔离
|
||||||
isWireless: _isWireless,
|
RepaintBoundary(
|
||||||
chargePower: _chargePower,
|
child: BatterySummary(
|
||||||
chargeVolt: _chargeVolt,
|
healthPercent: healthPercent,
|
||||||
chargeCurr: _chargeCurr,
|
cycleCount: _cycleCount,
|
||||||
wiredLevel: _wiredLevel,
|
currentCap: _currentCap,
|
||||||
// 🟢 动画参数由父级 AnimatedBuilder 控制,
|
designCap: _designCap,
|
||||||
// BatteryDataLayout 不再需要透传 animation 参数
|
),
|
||||||
);
|
),
|
||||||
},
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
// 🟢 TrendChart 用 RepaintBoundary 隔离
|
||||||
|
RepaintBoundary(
|
||||||
|
child: TrendChart(
|
||||||
|
data: _trendData,
|
||||||
|
designCap: _designCap,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
|
||||||
|
// 🟢 充电条目 - 保留 AnimatedOpacity,但加 RepaintBoundary
|
||||||
|
RepaintBoundary(
|
||||||
|
child: AnimatedOpacity(
|
||||||
|
opacity: _isCharging ? 1.0 : 0.0,
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
child: ChargeEntry(
|
||||||
|
summary: chargeSummary,
|
||||||
|
isCharging: _isCharging,
|
||||||
|
onTap: () {
|
||||||
|
if (_isCharging) {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ChargeDetailPage(
|
||||||
|
chargeType: _chargeType,
|
||||||
|
isWireless: _isWireless,
|
||||||
|
power: _chargePower,
|
||||||
|
volt: _chargeVolt,
|
||||||
|
curr: _chargeCurr,
|
||||||
|
wiredLevel: _wiredLevel,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
|
||||||
|
// 🟢 BatteryInfoPanel 也用 RepaintBoundary
|
||||||
|
RepaintBoundary(
|
||||||
|
child: BatteryInfoPanel(
|
||||||
|
batteryVolt: _batteryVolt,
|
||||||
|
batteryTemp: _batteryTemp,
|
||||||
|
maxVolt: _maxVolt,
|
||||||
|
minVolt: _minVolt,
|
||||||
|
maxTemp: _maxTemp,
|
||||||
|
minTemp: _minTemp,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
: const EmptyStateLayout(),
|
: const Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.battery_unknown, size: 64, color: Colors.grey),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'暂无数据',
|
||||||
|
style: TextStyle(fontSize: 18, color: Colors.grey),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'点击右上角刷新尝试',
|
||||||
|
style: TextStyle(fontSize: 14, color: Colors.grey),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,9 +65,11 @@ class _ChangelogPageState extends State<ChangelogPage> {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
: SingleChildScrollView(
|
: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Markdown(
|
child: Markdown(
|
||||||
data: _markdownContent,
|
data: _markdownContent,
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
styleSheet: MarkdownStyleSheet(
|
styleSheet: MarkdownStyleSheet(
|
||||||
h1: const TextStyle(
|
h1: const TextStyle(
|
||||||
fontFamily: 'HarmonyOS_Sans_SC',
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ flutter:
|
|||||||
- asset: assets/fonts/flymeFont.ttf
|
- asset: assets/fonts/flymeFont.ttf
|
||||||
assets:
|
assets:
|
||||||
- assets/icons/
|
- assets/icons/
|
||||||
|
- assets/CHANGELOG.md
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
# assets:
|
# assets:
|
||||||
|
|||||||
Reference in New Issue
Block a user