25 Commits

Author SHA1 Message Date
lxh2875931338 c85a523ec0 机型匹配情况 2026-07-21 19:21:05 +08:00
lxh2875931338 067f0d17e8 机型匹配情况说明 2026-07-21 19:19:50 +08:00
lxh2875931338 a63be9f675 20inf最低支持版本说明 2026-07-21 19:15:43 +08:00
lxh2875931338 86c38a97f5 增加机型匹配信息与适配说明 2026-07-21 19:13:27 +08:00
lxh2875931338 2026e55fde readme机型匹配列表更新 2026-07-21 18:57:35 +08:00
lxh2875931338 286162d41b readme机型匹配列表更新 2026-07-21 18:56:49 +08:00
lxh2875931338 4006684466 Merge branch 'main' of http://192.168.3.16:3800/lxh2875931338/MEIZU-Battery-Healthy 2026-07-21 18:54:50 +08:00
lxh2875931338 e5856296d4 readme机型匹配列表更新 2026-07-21 18:54:19 +08:00
lxh2875931338 e049cab9ad readme机型匹配列表更新 2026-07-21 18:49:27 +08:00
lxh2875931338 2b3d9471cd 补充版本号更新:1.6.0-Release 2026-07-21 18:39:26 +08:00
lxh2875931338 76fd0417e7 紧急补充:魅族21note字段更新 2026-07-21 00:09:47 +08:00
lxh2875931338 471d8310d7 更新日志 2026-07-20 23:59:33 +08:00
lxh2875931338 92f6c30699 更新日志说明 2026-07-20 23:58:48 +08:00
lxh2875931338 2e438aa089 针对readme部分做性能优化,目标帧率120达成 2026-07-20 23:57:01 +08:00
lxh2875931338 5e755d860f 更新日志界面优化、主页部分动画调整 2026-07-20 23:34:42 +08:00
lxh2875931338 0d8c6b081d 更新日志铺路部分,dev中 2026-07-20 23:16:39 +08:00
lxh2875931338 c8866a740a 21note字段确认为乱码,撤回支持 2026-07-20 22:49:16 +08:00
lxh2875931338 41f3ea114b 21Note字段更新(字段预测) 2026-07-20 22:39:17 +08:00
lxh2875931338 b5628510f6 readme更新 2026-07-19 20:57:04 +08:00
lxh2875931338 b390311827 版本号调整 2026-07-19 20:14:32 +08:00
lxh2875931338 f3a414b097 readme更新 2026-07-19 20:05:47 +08:00
lxh2875931338 cf7cec6be3 readme更新 2026-07-19 19:55:32 +08:00
lxh2875931338 6808f099f4 去除魅族22的机型匹配信息(机型没有响应的log文件) 2026-07-19 19:50:20 +08:00
lxh2875931338 38624a8d50 修改机型名称(匹配其他机型的非全大写) 2026-07-19 19:48:55 +08:00
lxh2875931338 b34af6af66 20inf识别字更新 2026-07-19 19:48:07 +08:00
11 changed files with 761 additions and 204 deletions
-3
View File
@@ -1,3 +0,0 @@
{
"cmake.ignoreCMakeListsMissing": true
}
+35 -8
View File
@@ -26,9 +26,10 @@
| :--- | :--- | :--- | | :--- | :--- | :--- |
| 1.5.0 | 功率计算优化 | ✅ 已发布 | | 1.5.0 | 功率计算优化 | ✅ 已发布 |
| 1.5.2 | 部分场景字体替换 | ✅ 已发布 | | 1.5.2 | 部分场景字体替换 | ✅ 已发布 |
| 1.6.0 | 更新日志独立页面 | 📋近期开发 | | 1.6.0 | 更新日志独立页面 | ✅ 已发布 |
| 2.0.0 | 数据看板扩展等系列更新 | 📋 远景 | | 1.7.0 | 适配夜间模式 | 📋近期开发 |
| 2.0.0 | 数据看板月份显示扩展等系列更新 | 📋 远景 |
| 3.0.0 | 搭配PC上位机记录充电信息等 | 📋 远景 |
--- ---
## 环境要求 ## 环境要求
@@ -39,10 +40,36 @@
--- ---
## 机型匹配情况 ## 机型匹配情况
目前据群友反馈已知:
- 魅族22无mbattery_charger目录生成,无法实时计算相应百分比 | 机型 | 系统版本 | 容量来源 | 设计容量 | 状态 |
- 魅族18在后续的版本中关闭了mbattery_log生成,疑似无效,老版本可能支持 |---|---|---|---|---|
- 最完美的支持版本至少可覆盖魅族20/21系机型(20inf需后续确认) | 魅族 22 | Flyme 12.x | 系统未生成 `mbattery_charger` 目录 | 5510 mAh | ❌ 不支持 |
| 魅族 21 | Flyme 12.x | `learned_cap` | 4800 mAh | ✅ 与 21 Pro 同步获得支持 |
| 魅族 21 Pro | Flyme 12.x | `learned_cap` | 5050 mAh | ✅ 初始支持 |
| 魅族 21 Note | Flyme 12.x | `learned_cap` | 5500 mAh | ✅ 1.6.0 新增支持 |
| 魅族 21 Note | Flyme 10 / 11 | 日志格式异常 | — | ❌ 不支持 |
| 魅族 20 | Flyme 12.x | `remaining_cap` + `status=Full` | 4700 mAh | ✅ 1.4.0 首次支持 |
| 魅族 20 Pro | Flyme 12.x | `remaining_cap` + `status=Full` | 5000 mAh | ✅ 1.4.0 首次支持 |
| 魅族 20 INFINITY | Flyme 10.x | `remaining_cap` + `status=Full` | 4800 mAh | ✅ 1.5.3 字段匹配 |
| 魅族 18 / 18s / 18 Pro / 18s Pro / 18X | Flyme 9 | 日志停止生成 | 4000 / 4500 mAh | ❌ 无适配条件 |
| 魅族 18 / 18s / 18 Pro / 18s Pro / 18X | Flyme 10 | 不再主动适配 | 4000 / 4500 mAh | ⚠️ 实验性支持 |
| 魅族 17 / 17Pro | Flyme 8 / 9 / 10 | 等待群友提供字段信息 | 4500 / 4500mAh | ⚠️ 待验证 |
| 魅族 16th / 16th Plus / 16X / 16s / 16sPro / 16Xs / 16T | Flyme 7 / 8 / 9 | 需回退原厂固件验证 | - | ⚠️ 若新版本无日志,则变为实验性支持,不主动适配 |
### 状态说明
| 状态 | 含义 |
|---|---|
| ✅ 完美支持 | 已通过实机验证,所有字段完整解析 |
| ⚠️ 实验性支持 | 理论上可支持,但需要用户协助验证 |
| ⚠️ 待验证 | 尚未收到实机日志反馈,待确认 |
| ❌ 不支持 | 系统未生成 BMS 日志,或日志格式无法解析 |
### 特别说明
- **Flyme 版本差异**:同一机型在不同 Flyme 版本下日志格式可能不同。例如 21 Note 在 Flyme 12.x 下可正常读取 `learned_cap`,但在 Flyme 10/11 下日志格式异常。
- **日志停止生成**:部分机型在升级到新系统后(如 17/18 系升级到 Flyme 10),系统不再向 `mbattery_charger` 目录写入新日志,文件夹虽存在但停止更新。
- **适配策略**:优先支持日志格式稳定的 Flyme 12.x 机型,旧版本系统暂不主动适配,若有用户长期停留在旧版本并愿意协助测试,可酌情增加支持。
--- ---
@@ -53,7 +80,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
@@ -11,6 +11,7 @@ import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.DocumentsContract import android.provider.DocumentsContract
import android.provider.OpenableColumns import android.provider.OpenableColumns
import android.view.Window // 🟢 必须包含
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.annotation.NonNull import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
@@ -32,6 +33,9 @@ class MainActivity : FlutterActivity() {
private val PREF_NAME = "battery_health_prefs" private val PREF_NAME = "battery_health_prefs"
private val KEY_SAF_URI = "saf_uri" private val KEY_SAF_URI = "saf_uri"
// 🟢 刷新率控制通道
private val FRAME_RATE_CHANNEL = "com.lxh.battery_health/frame_rate"
private val chargeReceiver = object : BroadcastReceiver() { private val chargeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
intent ?: return intent ?: return
@@ -74,7 +78,6 @@ class MainActivity : FlutterActivity() {
try { try {
safUri = Uri.parse(uriString) safUri = Uri.parse(uriString)
android.util.Log.d("BatteryHealth", "通过 MethodChannel 恢复 URI: $safUri") android.util.Log.d("BatteryHealth", "通过 MethodChannel 恢复 URI: $safUri")
// 同时保存到 SharedPreferences
val prefs: SharedPreferences = getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) val prefs: SharedPreferences = getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
prefs.edit().putString(KEY_SAF_URI, uriString).apply() prefs.edit().putString(KEY_SAF_URI, uriString).apply()
} catch (e: Exception) { } catch (e: Exception) {
@@ -222,9 +225,34 @@ class MainActivity : FlutterActivity() {
return fileUri?.let { readFile(it) } return fileUri?.let { readFile(it) }
} }
// 🟢 刷新率控制方法
// 🟢 刷新率控制方法(使用 preferredRefreshRate,兼容性更好)
private fun setHighRefreshRate() {
try {
val params = window.attributes
params.preferredRefreshRate = 120f
window.attributes = params
android.util.Log.d("BatteryHealth", "setHighRefreshRate: 设置 120Hz")
} catch (e: Exception) {
android.util.Log.e("BatteryHealth", "setHighRefreshRate 失败: ${e.message}")
}
}
private fun clearRefreshRate() {
try {
val params = window.attributes
params.preferredRefreshRate = 0f // 0 表示让系统自动决定
window.attributes = params
android.util.Log.d("BatteryHealth", "clearRefreshRate: 恢复默认")
} catch (e: Exception) {
android.util.Log.e("BatteryHealth", "clearRefreshRate 失败: ${e.message}")
}
}
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine) super.configureFlutterEngine(flutterEngine)
// 充电状态通道
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHARGE_CHANNEL) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHARGE_CHANNEL)
.setMethodCallHandler { call, result -> .setMethodCallHandler { call, result ->
if (call.method == "getChargeState") { if (call.method == "getChargeState") {
@@ -247,6 +275,7 @@ class MainActivity : FlutterActivity() {
} }
} }
// SAF 通道
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, SAF_CHANNEL) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, SAF_CHANNEL)
.setMethodCallHandler { call, result -> .setMethodCallHandler { call, result ->
when (call.method) { when (call.method) {
@@ -283,6 +312,23 @@ class MainActivity : FlutterActivity() {
} }
} }
// 🟢 刷新率控制通道
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, FRAME_RATE_CHANNEL)
.setMethodCallHandler { call, result ->
when (call.method) {
"setHighRefreshRate" -> {
setHighRefreshRate()
result.success(true)
}
"clearRefreshRate" -> {
clearRefreshRate()
result.success(true)
}
else -> result.notImplemented()
}
}
// 事件通道
EventChannel(flutterEngine.dartExecutor.binaryMessenger, EVENT_CHANNEL) EventChannel(flutterEngine.dartExecutor.binaryMessenger, EVENT_CHANNEL)
.setStreamHandler( .setStreamHandler(
object : EventChannel.StreamHandler { object : EventChannel.StreamHandler {
+137
View File
@@ -0,0 +1,137 @@
## [1.6.0] - 2026-07-20
### ✨ 新增
- **魅族 21 Note 机型适配**:新增 `21 Note` 特征字识别,设计容量 5500mAh
- 21 Note 运行 Flyme 12.6 版本,日志格式与 21 系列一致
- 支持 `learned_cap` 字段直接读取,与 21 / 21 Pro 逻辑相同
- 已加入 `DeviceMatching` 机型映射表
- **更新日志页面**:内置 `CHANGELOG.md` 渲染,版本历史一目了然
- 使用 `flutter_markdown` 解析,支持标准 Markdown 格式
- 滚动进度条:实时显示阅读进度,提升浏览体验
- 进入页面自动请求 120Hz 高刷新率,滑动更流畅(Flyme LTPO 适配)
- 滚动时呼吸动画维持高刷感知,停止后自动恢复省电策略
- **AppBar 菜单重构**:刷新按钮左移,右侧新增三点菜单
- `更新日志`:已可用
- `深色模式`:占位(1.7.0 实现)
- `关于`:占位(1.7.0 实现)
- **原生层高刷控制**:新增 `MethodChannel` 通道,支持 Dart 侧动态申请/释放 120Hz
- 进入更新日志页面自动触发
- 退出页面自动释放,不影响系统省电策略
### ✅ 已确认完美支持(更新)
| 机型 | 容量来源 | 设计容量 |
|---|---|---|
| 魅族 20 / 20 Pro / 20 INFINITY | `remaining_cap` + `status=Full` | 4700 / 5000 / 4800 mAh |
| 魅族 21 / 21 Pro | `learned_cap` | 4800 / 5050 mAh |
| **魅族 21 Note(新增)** | `learned_cap` | **5500 mAh** |
### ⚡ 优化
- **UI 拆分**:将 `BatteryHealthPage` 的 UI 部分抽离至独立文件,降低耦合度
- **动画简化**:移除复合动画控制器(`AnimationController`),改用轻量 `AnimatedOpacity` 控制充电条目显隐
- **更新日志性能**:缓存 Markdown 渲染结果,避免重复解析
- **LTPO 适配**:通过进度条微动 + 原生层 `preferredRefreshRate`,解决更新日志页面降频卡顿问题
### 🐛 修复
- 修复更新日志页面因 `ListView` + `shrinkWrap` 导致的 `unbounded height` 渲染异常
- 修复充电条目动画状态残留导致的显示异常
- 修复部分 `TextStyle``color``foreground` 混用导致的渲染断言错误
### 📊 影响范围
- `device_matching.dart`:新增 21 Note 机型映射
- `battery_health_page.dart`AppBar 重构,动画简化
- `battery_data_layout.dart`(新增):UI 布局抽离
- `changelog_page.dart`(新增):更新日志页面
- `MainActivity.kt`:新增原生高刷控制通道
- `pubspec.yaml`:新增 `flutter_markdown` 依赖,添加 `assets/CHANGELOG.md`
### 📌 备注
- 本次更新为 1.6.0 封版版本,后续将聚焦 1.7.0(深色模式 + 关于页面)
- 21 Note 在 Flyme 12.6 上测试通过,日志解析正常 ✅
----
## [1.5.3] - 2026-07-19
- 新增:机型匹配优化
### ✅ 已确认完美支持
- 魅族 20 / 20 Pro / 20 INFINITY(基于remaining_cap + status=full联合判断)
- 魅族 21 / 21 Pro(直接读取learned_cap字段信息)
### ⚠️ 部分支持(测试中)
- 魅族 18 / 18s / 18 Pro / 18s Pro(日志存在,字段解析适配中)
- 魅族 16s(需回退原厂固件验证)
### ❌ 确认不支持
- 魅族 21 Note(星纪团队固件,日志格式乱码无法识别,导致异常)
- 魅族 22(系统未生成 `mbattery_charger` 目录)
### 📌 说明
- 适配状态仅针对 BMS 日志可读性,与手机硬件本身无关
- 部分旧机型(如 16 系)若无日志生成,则无法使用本工具
- 后续若发现其他机型可用,将持续补充
----
## [1.5.2] - 2026-07-14
### 新增
- 浪漫雅圆字体,工程内文件为FlymeFont.ttf
### 🎨 UI 优化
- **字体体系重构**:数字全局替换为 FlymeFont(浪漫雅圆),中文保留 HarmonyOS Sans SC
- 健康度百分比、容量数值、循环次数、电压/温度读数均使用 FlymeFont
- 中文/单位/符号保持 HarmonyOS Sans SC,确保阅读舒适度
- **温标符号修正**:温度显示由 `°C`(度符号 + C)改为专用字符 `℃`U+2103),与 FlymeFont 原生适配
- **容量行视觉权重优化**
- 当前容量与设计容量数字使用 FlymeFont(字号 20,字重 300
- `mAh` 单位改用 HarmonyOS Sans SC(字号 16,字重 500),与 `cycles` 保持一致
- 整行颜色统一为 `rgb(95, 95, 95)`,弱化视觉重量,突出健康度大数字
- **电压/温度极值显示优化**:数值与单位统一使用 FlymeFont,箭头符号保留 HarmonyOS
### 🐛 修复
- 修复 FlymeFont 数字 1 在部分场景下显示为红色的问题(字体内部颜色预设)
- 修复容量行 `designCap`(设计容量)未正确应用 FlymeFont 的问题
- 修复部分 `TextStyle``color``foreground` 混用导致的渲染断言错误
- 统一主界面与二级页面状态栏样式
### 📊 影响范围
- `battery_summary.dart`:健康度/循环/容量显示样式重构
- `battery_info_panel.dart`:电压/温度显示字体统一
- `charge_detail_page.dart`:数值字体统一
- `trend_chart.dart`:坐标轴数字字体统一
### 📌 备注
- 本次更新纯 UI 优化,不影响数据读取与计算逻辑
----
## [1.5.0] - 2026-07-09
### 🐛 修复
- 修复有线充电功率计算单位错误(µV × µA 计算时少除 1000,导致功率放大 1000 倍,属于潜藏bug,外面看不出问题)
- 修复魅族 21 系列 `learned_cap` 因受 `status=Full` 限制导致容量未及时更新的问题
- `learned_cap` 优先级提升:在所有状态行中优先匹配,不再局限于 `status=Full`
### ⚡ 优化
- 有线充电功率显示重构:
- 主界面:`≈{实际功率}w/{请求功率}w`(例:`≈30w/65w`
- 二级页面:拆分为“请求档位”(`≈80w/L-10`)和“实际功率”(`≈30w`)两行
- USB 慢充保持 `≈7.5w` 不变
- UI 统一:
- 统一主界面与二级页面状态栏背景色
- 统一主界面与二级页面 AppBar 背景色为 `Colors.white`
- 状态栏图标保持深色(`Brightness.dark`
## 更早期日志请移步至项目Git Release页面查看
+3
View File
@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
+4 -4
View File
@@ -2,11 +2,11 @@
class DeviceMatching { class DeviceMatching {
/// 根据机型返回设计容量(mAh) /// 根据机型返回设计容量(mAh)
static int getDesignCapacity(String model) { static int getDesignCapacity(String model) {
if (model.contains('22')) return 5510;
if (model.contains('21 Pro')) return 5050; if (model.contains('21 Pro')) return 5050;
if (model.contains('21 Note')) return 5500;
if (model.contains('21')) return 4800; if (model.contains('21')) return 4800;
if (model.contains('20 Pro')) return 5000; if (model.contains('20 Pro')) return 5000;
if (model.contains('20 INFINITY')) return 4800; if (model.contains('20 Inf')) return 4800;
if (model.contains('20')) return 4700; if (model.contains('20')) return 4700;
if (model.contains('18s Pro')) return 4500; if (model.contains('18s Pro')) return 4500;
if (model.contains('18s')) return 4000; if (model.contains('18s')) return 4000;
@@ -29,11 +29,11 @@ class DeviceMatching {
/// 根据机型返回显示名称 /// 根据机型返回显示名称
static String getModelName(String model) { static String getModelName(String model) {
if (model.contains('22')) return '魅族22';
if (model.contains('21 Pro')) return '魅族21 Pro'; if (model.contains('21 Pro')) return '魅族21 Pro';
if (model.contains('21 Note')) return '魅族21 Note';
if (model.contains('21')) return '魅族21'; if (model.contains('21')) return '魅族21';
if (model.contains('20 Pro')) return '魅族20 Pro'; if (model.contains('20 Pro')) return '魅族20 Pro';
if (model.contains('20 INFINITY')) return '魅族20 INFINITY'; if (model.contains('20 Inf')) return '魅族20 Infinity';
if (model.contains('20')) return '魅族20'; if (model.contains('20')) return '魅族20';
if (model.contains('18s Pro')) return '魅族18s Pro'; if (model.contains('18s Pro')) return '魅族18s Pro';
if (model.contains('18s')) return '魅族18s'; if (model.contains('18s')) return '魅族18s';
+104 -131
View File
@@ -4,21 +4,22 @@ 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 '../models/device_matching.dart'; // ✅ 存在 import '../models/device_matching.dart';
import '../models/saf_permission_state.dart'; // ✅ 存在 import '../models/saf_permission_state.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';
import '../logic/charge_parser.dart'; // ✅ 存在 import '../logic/charge_parser.dart';
import '../logic/capacity_extractor.dart'; // ✅ 存在(注意:不是 utils/) import '../logic/capacity_extractor.dart';
import '../utils/time_utils.dart'; // ✅ 存在 import '../utils/time_utils.dart';
import '../utils/charge_utils.dart'; // ✅ 存在 import '../utils/charge_utils.dart';
import '../widgets/trend_chart.dart'; // ⚠️ 需要确认文件内容完整 import '../widgets/battery_summary.dart';
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/trend_chart.dart';
import '../pages/changelog_page.dart';
// --- 潘通色定义 --- // --- 潘通色定义 ---
const Color kHealthGreen = Color(0xFF00B140); const Color kHealthGreen = Color(0xFF00B140);
@@ -32,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;
@@ -66,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(
@@ -87,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((
@@ -134,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');
@@ -275,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;
} }
@@ -310,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) {
@@ -349,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;
@@ -361,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;
} }
@@ -374,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);
@@ -403,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) {
@@ -424,7 +381,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
} }
} }
// 🟢 一次性 setState 更新所有字段
setState(() { setState(() {
_currentCap = (finalCap ?? _currentCap).toDouble(); _currentCap = (finalCap ?? _currentCap).toDouble();
_cycleCount = cycle; _cycleCount = cycle;
@@ -443,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',
@@ -452,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();
} }
} }
@@ -587,33 +539,12 @@ 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 '';
if (_isWireless) { if (_isWireless) {
return ChargeUtils.getDisplayPowerWireless(_chargePower); return ChargeUtils.getDisplayPowerWireless(_chargePower);
} else { } else {
// 🟢 1.5.0 改用新格式:实际/标定
return ChargeUtils.getMainDisplayPowerWired( return ChargeUtils.getMainDisplayPowerWired(
_chargePower, _chargePower,
_wiredLevel, _wiredLevel,
@@ -629,9 +560,16 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
// ---------- Build ---------- // ---------- Build ----------
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final double healthPercent = (_designCap > 0)
? (_currentCap / _designCap) * 100
: 0.0;
final String chargeSummary = _isCharging
? '充电中 · ${_getChargeTypeLabel()} · ${_getDisplayPower()}'
: '';
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: const SystemUiOverlayStyle( value: const SystemUiOverlayStyle(
// 🟢 1.5.0 统一状态栏颜色
statusBarColor: Color.fromRGBO(157, 212, 237, 0.1), statusBarColor: Color.fromRGBO(157, 212, 237, 0.1),
statusBarIconBrightness: Brightness.dark, statusBarIconBrightness: Brightness.dark,
statusBarBrightness: Brightness.dark, statusBarBrightness: Brightness.dark,
@@ -659,42 +597,52 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
tooltip: '刷新数据', tooltip: '刷新数据',
color: Colors.black, color: Colors.black,
), ),
], PopupMenuButton<String>(
), icon: const Icon(Icons.more_vert),
body: _hasData ? _buildDataLayout() : _buildEmptyState(), color: Colors.white,
onSelected: (value) {
switch (value) {
case 'changelog':
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ChangelogPage(),
), ),
); );
break;
case 'dark_mode':
break;
case 'about':
break;
} }
},
Widget _buildEmptyState() { itemBuilder: (context) => [
return const Center( const PopupMenuItem(value: 'changelog', child: Text('更新日志')),
child: Column( const PopupMenuItem(
mainAxisAlignment: MainAxisAlignment.center, value: 'dark_mode',
enabled: false,
child: Text('深色模式(开发中)'),
),
const PopupMenuItem(
value: 'about',
enabled: false,
child: Text('关于(开发中)'),
),
],
),
],
),
body: _hasData
? Column(
children: [ children: [
Icon(Icons.battery_unknown, size: 64, color: Colors.grey), // 🟢 可滚动区域(带 RepaintBoundary
SizedBox(height: 16), Expanded(
Text('暂无数据', style: TextStyle(fontSize: 18, color: Colors.grey)), child: SingleChildScrollView(
Text('点击右上角刷新尝试', style: TextStyle(fontSize: 14, color: Colors.grey)),
],
),
);
}
Widget _buildDataLayout() {
final healthPercent = (_designCap > 0)
? (_currentCap / _designCap) * 100
: 0;
String chargeSummary = '';
if (_isCharging) {
chargeSummary = '充电中 · ${_getChargeTypeLabel()} · ${_getDisplayPower()}';
}
return 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: [
// 设备型号
Text( Text(
_deviceModel, _deviceModel,
style: const TextStyle( style: const TextStyle(
@@ -705,25 +653,33 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
BatterySummary( // 🟢 BatterySummary 用 RepaintBoundary 隔离
healthPercent: healthPercent.toDouble(), // 显式转换 RepaintBoundary(
child: BatterySummary(
healthPercent: healthPercent,
cycleCount: _cycleCount, cycleCount: _cycleCount,
currentCap: _currentCap, currentCap: _currentCap,
designCap: _designCap, designCap: _designCap,
), ),
),
const SizedBox(height: 24), const SizedBox(height: 24),
TrendChart(data: _trendData, designCap: _designCap), // 🟢 TrendChart 用 RepaintBoundary 隔离
RepaintBoundary(
child: TrendChart(
data: _trendData,
designCap: _designCap,
),
),
const SizedBox(height: 8), const SizedBox(height: 8),
AnimatedBuilder( // 🟢 充电条目 - 保留 AnimatedOpacity,但加 RepaintBoundary
animation: _chargeAnimationController, RepaintBoundary(
builder: (context, child) { child: AnimatedOpacity(
final entryHeight = 28 * _entryFadeIn.value; opacity: _isCharging ? 1.0 : 0.0,
final chargeEntry = Opacity( duration: const Duration(milliseconds: 300),
opacity: _entryFadeIn.value,
child: ChargeEntry( child: ChargeEntry(
summary: chargeSummary, summary: chargeSummary,
isCharging: _isCharging, isCharging: _isCharging,
@@ -745,13 +701,13 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
} }
}, },
), ),
); ),
),
return Column( const SizedBox(height: 4),
children: [
SizedBox(height: entryHeight, child: chargeEntry), // 🟢 BatteryInfoPanel 也用 RepaintBoundary
Transform.translate( RepaintBoundary(
offset: Offset(0, _bottomSlide.value),
child: BatteryInfoPanel( child: BatteryInfoPanel(
batteryVolt: _batteryVolt, batteryVolt: _batteryVolt,
batteryTemp: _batteryTemp, batteryTemp: _batteryTemp,
@@ -762,12 +718,29 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
), ),
), ),
], ],
);
},
), ),
const Spacer(), ),
),
],
)
: 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),
),
], ],
), ),
),
),
); );
} }
} }
+205
View File
@@ -0,0 +1,205 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; // 🟢 添加这一行
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter_markdown/flutter_markdown.dart';
class ChangelogPage extends StatefulWidget {
const ChangelogPage({super.key});
@override
State<ChangelogPage> createState() => _ChangelogPageState();
}
class _ChangelogPageState extends State<ChangelogPage>
with SingleTickerProviderStateMixin {
// ---------- 数据 ----------
String _markdownContent = '';
bool _isLoading = true;
String? _error;
Widget? _cachedMarkdown;
// ---------- 滚动 ----------
final ScrollController _scrollController = ScrollController();
double _scrollProgress = 0.0;
bool _isScrolling = false;
// ---------- 呼吸动画(维持高刷感知) ----------
late AnimationController _breathController;
late Animation<double> _breathAnimation;
// ---------- 原生刷新率控制 ----------
// 🟢 改用 static final(不是 const
static final _frameRateChannel = MethodChannel(
'com.lxh.battery_health/frame_rate',
);
// ---------- 生命周期 ----------
@override
void initState() {
super.initState();
_loadChangelog();
// 进入页面时申请高刷(原生层)
_frameRateChannel.invokeMethod('setHighRefreshRate');
// 呼吸动画:让进度条在滚动时轻微抖动,维持高刷感知
_breathController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
)..repeat(reverse: true);
_breathAnimation = Tween<double>(begin: 0.0, end: 0.03).animate(
CurvedAnimation(parent: _breathController, curve: Curves.easeInOut),
);
_scrollController.addListener(() {
_updateProgress();
final isScrolling = _scrollController.position.isScrollingNotifier.value;
if (isScrolling != _isScrolling) {
setState(() {
_isScrolling = isScrolling;
});
if (isScrolling) {
_breathController.repeat(reverse: true);
} else {
_breathController.stop();
_breathController.reset();
}
}
});
}
@override
void dispose() {
// 离开页面时恢复默认刷新率
_frameRateChannel.invokeMethod('clearRefreshRate');
_scrollController.removeListener(_updateProgress);
_scrollController.dispose();
_breathController.dispose();
super.dispose();
}
// ---------- 方法 ----------
void _updateProgress() {
if (!_scrollController.hasClients) return;
final maxScroll = _scrollController.position.maxScrollExtent;
final currentScroll = _scrollController.position.pixels;
final progress = maxScroll > 0 ? currentScroll / maxScroll : 0.0;
setState(() {
_scrollProgress = progress;
});
}
Future<void> _loadChangelog() async {
try {
final content = await rootBundle.loadString('assets/CHANGELOG.md');
setState(() {
_markdownContent = content;
_isLoading = false;
_cachedMarkdown = _buildMarkdown(content);
});
} catch (e) {
setState(() {
_error = '无法加载更新日志: $e';
_isLoading = false;
});
}
}
Widget _buildMarkdown(String content) {
return Markdown(
data: content,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
styleSheet: MarkdownStyleSheet(
h1: const TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 24,
fontWeight: FontWeight.w700,
color: Colors.black,
),
h2: const TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 20,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
h3: const TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
p: const TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16,
color: Colors.black87,
),
listBullet: const TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16,
color: Colors.black87,
),
code: const TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 14,
color: Colors.black87,
backgroundColor: Color(0xFFF5F5F5),
),
),
);
}
// ---------- Build ----------
@override
Widget build(BuildContext context) {
final breathOffset = _isScrolling ? _breathAnimation.value : 0.0;
final displayProgress = (_scrollProgress + breathOffset).clamp(0.0, 1.0);
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text(
'更新日志',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.black,
),
),
backgroundColor: Colors.white,
elevation: 0,
foregroundColor: Colors.black,
iconTheme: const IconThemeData(color: Colors.black),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(2.0),
child: LinearProgressIndicator(
value: displayProgress,
backgroundColor: Colors.grey.shade200,
valueColor: const AlwaysStoppedAnimation<Color>(
Color.fromRGBO(157, 212, 237, 1.0),
),
minHeight: 2.0,
),
),
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: _error != null
? Center(
child: Text(
_error!,
style: const TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.grey,
),
),
)
: SingleChildScrollView(
controller: _scrollController,
padding: const EdgeInsets.all(16.0),
child: _cachedMarkdown ?? const SizedBox.shrink(),
),
);
}
}
+151
View File
@@ -0,0 +1,151 @@
import 'package:flutter/material.dart';
import '../widgets/trend_chart.dart';
import '../widgets/battery_summary.dart';
import '../widgets/battery_info_panel.dart';
import '../widgets/charge_entry.dart';
import '../widgets/charge_detail_page.dart';
/// 电池数据布局(含数据展示、趋势图、充电状态、电压温度面板)
class BatteryDataLayout extends StatelessWidget {
final String deviceModel;
final double healthPercent;
final int cycleCount;
final double currentCap;
final int designCap;
final List<Map<String, dynamic>> trendData;
final bool isCharging;
final String chargeSummary;
final double batteryVolt;
final double batteryTemp;
final double maxVolt;
final double minVolt;
final double maxTemp;
final double minTemp;
final String chargeType;
final bool isWireless;
final double chargePower;
final double chargeVolt;
final double chargeCurr;
final int? wiredLevel;
const BatteryDataLayout({
super.key,
required this.deviceModel,
required this.healthPercent,
required this.cycleCount,
required this.currentCap,
required this.designCap,
required this.trendData,
required this.isCharging,
required this.chargeSummary,
required this.batteryVolt,
required this.batteryTemp,
required this.maxVolt,
required this.minVolt,
required this.maxTemp,
required this.minTemp,
required this.chargeType,
required this.isWireless,
required this.chargePower,
required this.chargeVolt,
required this.chargeCurr,
this.wiredLevel,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
deviceModel,
style: const TextStyle(
fontSize: 14,
color: Colors.grey,
fontWeight: FontWeight.w400,
),
),
const SizedBox(height: 4),
BatterySummary(
healthPercent: healthPercent,
cycleCount: cycleCount,
currentCap: currentCap,
designCap: designCap,
),
const SizedBox(height: 24),
TrendChart(data: trendData, designCap: designCap),
const SizedBox(height: 8),
// 充电状态 + 信息面板(带进场动画)
_buildChargeAndInfoPanel(context),
const Spacer(),
],
),
);
}
Widget _buildChargeAndInfoPanel(BuildContext context) {
return Column(
children: [
// 充电状态入口(保持原有的透明度/位移动画由父级控制)
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(
batteryVolt: batteryVolt,
batteryTemp: batteryTemp,
maxVolt: maxVolt,
minVolt: minVolt,
maxTemp: maxTemp,
minTemp: minTemp,
),
],
);
}
}
/// 空状态布局(无数据时显示)
class EmptyStateLayout extends StatelessWidget {
const EmptyStateLayout({super.key});
@override
Widget build(BuildContext context) {
return 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)),
],
),
);
}
}
+16
View File
@@ -126,6 +126,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "5.0.0" version: "5.0.0"
flutter_markdown:
dependency: "direct main"
description:
name: flutter_markdown
sha256: "08fb8315236099ff8e90cb87bb2b935e0a724a3af1623000a9cec930468e0f27"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.7+1"
flutter_svg: flutter_svg:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -224,6 +232,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
markdown:
dependency: transitive
description:
name: markdown
sha256: ee85086ad7698b42522c6ad42fe195f1b9898e4d974a1af4576c1a3a176cada9
url: "https://pub.flutter-io.cn"
source: hosted
version: "7.3.1"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
+3 -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.5.2 version: 1.6.0
environment: environment:
sdk: ^3.9.2 sdk: ^3.9.2
@@ -38,6 +38,7 @@ dependencies:
path: ^1.9.0 path: ^1.9.0
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
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
@@ -80,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: