diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index cdca13c..de3d612 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -6,7 +6,7 @@ plugins { android { namespace = "com.lxh.battery_health" - compileSdk = 34 + compileSdk = 36 ndkVersion = flutter.ndkVersion compileOptions { diff --git a/android/app/src/main/kotlin/com/lxh/battery_health/MainActivity.kt b/android/app/src/main/kotlin/com/lxh/battery_health/MainActivity.kt index c677d9e..48caab1 100644 --- a/android/app/src/main/kotlin/com/lxh/battery_health/MainActivity.kt +++ b/android/app/src/main/kotlin/com/lxh/battery_health/MainActivity.kt @@ -12,6 +12,7 @@ import android.os.Bundle import android.provider.DocumentsContract import android.provider.OpenableColumns import android.view.Window // 🟢 必须包含 +import android.app.Activity import androidx.activity.ComponentActivity import androidx.annotation.NonNull import io.flutter.embedding.android.FlutterActivity @@ -135,7 +136,7 @@ class MainActivity : FlutterActivity() { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) - if (requestCode == FOLDER_PICKER_REQUEST && resultCode == ComponentActivity.RESULT_OK) { + if (requestCode == FOLDER_PICKER_REQUEST && resultCode == Activity.RESULT_OK) { val uri = data?.data if (uri != null) { try { diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 53f6ed8..a680fb7 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -1,33 +1,24 @@ -allprojects { - repositories { - maven { - url = uri("https://maven.aliyun.com/repository/public") - } - maven { - url = uri("https://maven.aliyun.com/repository/google") - } - maven { - url = uri("https://maven.aliyun.com/repository/gradle-plugin") - } - google() - mavenCentral() - } +// 根项目构建脚本 +plugins { + id("com.android.application") version "8.13.0" apply false + id("org.jetbrains.kotlin.android") version "2.2.21" apply false } -val newBuildDir: Directory = - rootProject.layout.buildDirectory - .dir("../../build") - .get() +// 统一重定向构建输出目录 +val newBuildDir: Directory = rootProject.layout.buildDirectory + .dir("../../build") + .get() rootProject.layout.buildDirectory.value(newBuildDir) subprojects { val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) project.layout.buildDirectory.value(newSubprojectBuildDir) } + subprojects { project.evaluationDependsOn(":app") } tasks.register("clean") { delete(rootProject.layout.buildDirectory) -} +} \ No newline at end of file diff --git a/android/gradle.properties b/android/gradle.properties index 475a628..75e4444 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError -android.useAndroidX=true -android.enableJetifier=true +org.gradle.daemon=true +org.gradle.parallel=true # This builtInKotlin flag was added automatically by Flutter migrator -android.builtInKotlin=false +android.builtInKotlin=true # This newDsl flag was added automatically by Flutter migrator -android.newDsl=false +android.newDsl=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 1ca3856..e1f6ed7 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-8.14-all.zip zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.12-all.zip +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts index fb605bc..fdbc2e4 100644 --- a/android/settings.gradle.kts +++ b/android/settings.gradle.kts @@ -1,26 +1,44 @@ pluginManagement { - val flutterSdkPath = - run { - val properties = java.util.Properties() - file("local.properties").inputStream().use { properties.load(it) } - val flutterSdkPath = properties.getProperty("flutter.sdk") - require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } - flutterSdkPath - } + val flutterSdkPath = run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val sdkPath = properties.getProperty("flutter.sdk") + require(sdkPath != null) { "flutter.sdk not set in local.properties" } + sdkPath + } includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") repositories { + // 🟢 阿里云 gradle-plugin 镜像(专用) + maven { + url = uri("https://maven.aliyun.com/repository/gradle-plugin") + } + // 🟢 阿里云 public 聚合仓库(兜底) + maven { + url = uri("https://maven.aliyun.com/repository/public") + } google() mavenCentral() gradlePluginPortal() } } -plugins { - id("dev.flutter.flutter-plugin-loader") version "1.0.0" - id("com.android.application") version "8.9.1" apply false - id("org.jetbrains.kotlin.android") version "2.1.0" apply false +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) + repositories { + // 🟢 阿里云镜像放在最前面 + maven { url = uri("https://maven.aliyun.com/repository/public") } + google() + mavenCentral() + } } -include(":app") +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.13.0" apply false + id("org.jetbrains.kotlin.android") version "2.2.21" apply false +} + +rootProject.name = "android" +include(":app") \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index af3d972..b022cd1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,16 +1,49 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'pages/battery_health_page.dart'; import 'services/saf_storage_service.dart'; +// 🆕 1.7.0 全局 Key,用于从子页面切换主题 +final GlobalKey<_MyAppState> appKey = GlobalKey<_MyAppState>(); + void main() async { WidgetsFlutterBinding.ensureInitialized(); await SafStorageService().init(); - runApp(const MyApp()); + + final prefs = await SharedPreferences.getInstance(); + final themeModeIndex = prefs.getInt('themeMode') ?? 0; + final themeMode = + ThemeMode.values[themeModeIndex.clamp(0, ThemeMode.values.length - 1)]; + + runApp(MyApp(key: appKey, initialThemeMode: themeMode)); } -class MyApp extends StatelessWidget { - const MyApp({super.key}); +class MyApp extends StatefulWidget { + final ThemeMode initialThemeMode; + const MyApp({super.key, required this.initialThemeMode}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + late ThemeMode _themeMode; + + @override + void initState() { + super.initState(); + _themeMode = widget.initialThemeMode; + } + + void switchTheme(ThemeMode mode) { + setState(() { + _themeMode = mode; + }); + SharedPreferences.getInstance().then((prefs) { + prefs.setInt('themeMode', mode.index); + }); + } @override Widget build(BuildContext context) { @@ -18,7 +51,7 @@ class MyApp extends StatelessWidget { title: '电池健康度', theme: _lightTheme(), darkTheme: _darkTheme(), - themeMode: ThemeMode.system, // 默认跟随系统 + themeMode: _themeMode, home: const BatteryHealthPage(), debugShowCheckedModeBanner: false, ); @@ -41,6 +74,9 @@ class MyApp extends StatelessWidget { colorScheme: const ColorScheme.light( primary: Color(0xFF1A73E8), secondary: Color(0xFF1A73E8), + surface: Colors.white, + onSurface: Colors.black, // 🟢 主要文字色 + onSurfaceVariant: Colors.black87, ), textTheme: const TextTheme( bodyLarge: TextStyle(color: Colors.black), @@ -68,6 +104,9 @@ class MyApp extends StatelessWidget { colorScheme: const ColorScheme.dark( primary: Color(0xFF4FC3F7), secondary: Color(0xFF4FC3F7), + surface: Color(0xFF121212), + onSurface: Colors.white, // 🟢 主要文字色(夜间模式为白色) + onSurfaceVariant: Colors.white70, ), textTheme: const TextTheme( bodyLarge: TextStyle(color: Colors.white), diff --git a/lib/pages/about_page.dart b/lib/pages/about_page.dart index 8873c7d..3aaa13c 100644 --- a/lib/pages/about_page.dart +++ b/lib/pages/about_page.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:url_launcher/url_launcher.dart'; class AboutPage extends StatelessWidget { diff --git a/lib/pages/battery_health_page.dart b/lib/pages/battery_health_page.dart index b44fb1d..17d08f0 100644 --- a/lib/pages/battery_health_page.dart +++ b/lib/pages/battery_health_page.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; import 'package:device_info_plus/device_info_plus.dart'; // --- 本地导入 --- +import '../main.dart'; import '../models/device_matching.dart'; import '../models/saf_permission_state.dart'; import '../models/data_abnormal_type.dart'; @@ -742,7 +743,18 @@ class _BatteryHealthPageState extends State { ); break; case 'dark_mode': - // 夜间模式切换逻辑(需要全局状态管理) + // 循环切换:浅色 → 深色 → 跟随系统 → 浅色 + final brightness = Theme.of(context).brightness; + ThemeMode next; + if (brightness == Brightness.light) { + next = ThemeMode.dark; + } else if (brightness == Brightness.dark) { + next = ThemeMode.system; + } else { + next = ThemeMode.light; + } + // 🆕 通过全局 key 调用 MyApp 的 switchTheme 方法 + appKey.currentState?.switchTheme(next); break; } }, diff --git a/lib/widgets/battery_info_panel.dart b/lib/widgets/battery_info_panel.dart index c4c0192..15d98e4 100644 --- a/lib/widgets/battery_info_panel.dart +++ b/lib/widgets/battery_info_panel.dart @@ -24,9 +24,12 @@ class BatteryInfoPanel extends StatelessWidget { @override Widget build(BuildContext context) { + final isDark = Theme.of(context).brightness == Brightness.dark; final bool isVoltageAbnormal = abnormalType == DataAbnormalType.voltageAbnormal; - final Color voltColor = isVoltageAbnormal ? Colors.red : Colors.black; + + final Color defaultTextColor = Theme.of(context).colorScheme.onSurface; + final Color voltColor = isVoltageAbnormal ? Colors.red : defaultTextColor; return Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -36,7 +39,8 @@ class BatteryInfoPanel extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildInfoItem( - _buildIcon('voltage'), + context, // 🆕 + _buildIcon('voltage', isDark), '电压', '${batteryVolt.toStringAsFixed(3)}V', textColor: voltColor, @@ -50,12 +54,14 @@ class BatteryInfoPanel extends StatelessWidget { fontFamily: 'Flyme', fontSize: 14, fontWeight: FontWeight.w500, - color: Colors.red, + color: isVoltageAbnormal + ? Colors.red + : defaultTextColor, ), children: [ - const TextSpan( + TextSpan( text: '↑ ', - style: TextStyle( + style: const TextStyle( fontFamily: 'HarmonyOS_Sans_SC', color: Colors.red, ), @@ -76,12 +82,14 @@ class BatteryInfoPanel extends StatelessWidget { fontFamily: 'Flyme', fontSize: 14, fontWeight: FontWeight.w500, - color: Colors.blue, + color: isVoltageAbnormal + ? Colors.red + : defaultTextColor, ), children: [ - const TextSpan( + TextSpan( text: '↓ ', - style: TextStyle( + style: const TextStyle( fontFamily: 'HarmonyOS_Sans_SC', color: Colors.blue, ), @@ -105,20 +113,22 @@ class BatteryInfoPanel extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildInfoItem( - _buildIcon('temperature'), + context, // 🆕 + _buildIcon('temperature', isDark), '温度', '${batteryTemp.toStringAsFixed(0)}℃', + textColor: defaultTextColor, ), const SizedBox(height: 8), Row( children: [ RichText( text: TextSpan( - style: const TextStyle( + style: TextStyle( fontFamily: 'Flyme', fontSize: 14, fontWeight: FontWeight.w500, - color: Colors.red, + color: defaultTextColor, ), children: [ const TextSpan( @@ -135,11 +145,11 @@ class BatteryInfoPanel extends StatelessWidget { const SizedBox(width: 12), RichText( text: TextSpan( - style: const TextStyle( + style: TextStyle( fontFamily: 'Flyme', fontSize: 14, fontWeight: FontWeight.w500, - color: Colors.blue, + color: defaultTextColor, ), children: [ const TextSpan( @@ -162,43 +172,46 @@ class BatteryInfoPanel extends StatelessWidget { ); } - Widget _buildIcon(String type) { + Widget _buildIcon(String type, bool isDark) { + final color = isDark ? Colors.white54 : Colors.grey; switch (type) { case 'voltage': return SvgPicture.asset( 'assets/icons/si--lightning-fill.svg', width: 18, height: 18, - colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn), + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), ); case 'temperature': return SvgPicture.asset( 'assets/icons/mdi--temperature.svg', width: 18, height: 18, - colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn), + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), ); default: - return const Icon(Icons.help_outline, size: 18, color: Colors.grey); + return Icon(Icons.help_outline, size: 18, color: color); } } Widget _buildInfoItem( + BuildContext context, // 🆕 Widget icon, String label, String value, { Color textColor = Colors.black, }) { + final labelColor = Theme.of(context).colorScheme.onSurface.withOpacity(0.7); return Row( children: [ icon, const SizedBox(width: 6), Text( label, - style: const TextStyle( + style: TextStyle( fontFamily: 'HarmonyOS_Sans_SC', fontSize: 14, - color: Colors.grey, + color: labelColor, ), ), const SizedBox(width: 6), diff --git a/lib/widgets/battery_summary.dart b/lib/widgets/battery_summary.dart index 7e1c31b..5a02752 100644 --- a/lib/widgets/battery_summary.dart +++ b/lib/widgets/battery_summary.dart @@ -19,20 +19,20 @@ class BatterySummary extends StatelessWidget { @override Widget build(BuildContext context) { + final isDark = Theme.of(context).brightness == Brightness.dark; final bool isAbnormal = abnormalType != DataAbnormalType.none; final bool isCapacityAbnormal = abnormalType == DataAbnormalType.capacityTooLow || abnormalType == DataAbnormalType.capacityTooHigh; - // 健康度颜色:异常时红色,否则按阈值 - final Color healthColor = isAbnormal - ? Colors.red - : (healthPercent < 70 ? Colors.red : Colors.black); - - // 容量颜色:异常时红色,否则灰色 + // 🟢 颜色完全动态 + final Color defaultTextColor = Theme.of(context).colorScheme.onSurface; + final Color mutedTextColor = isDark + ? Colors.grey.shade400 + : const Color(0xFF5F5F5F); final Color capacityColor = isCapacityAbnormal ? Colors.red - : const Color(0xFF5F5F5F); + : mutedTextColor; return Row( crossAxisAlignment: CrossAxisAlignment.end, @@ -49,7 +49,7 @@ class BatterySummary extends StatelessWidget { fontSize: 40, fontWeight: FontWeight.w500, height: 1.0, - color: healthColor, + color: isAbnormal ? Colors.red : defaultTextColor, ), ), const SizedBox(width: 2), @@ -60,9 +60,7 @@ class BatterySummary extends StatelessWidget { fontSize: 20, fontWeight: FontWeight.w300, height: 1.0, - color: healthColor == Colors.red - ? Colors.red - : Colors.grey.shade600, + color: isAbnormal ? Colors.red : Colors.grey.shade600, ), ), ], @@ -74,21 +72,18 @@ class BatterySummary extends StatelessWidget { children: [ RichText( text: TextSpan( - style: const TextStyle( + style: TextStyle( fontFamily: 'Flyme', fontSize: 16, fontWeight: FontWeight.w500, height: 1.2, - color: Colors.black, + color: defaultTextColor, ), children: [ TextSpan(text: '$cycleCount'), - const TextSpan( + TextSpan( text: ' cycles', - style: TextStyle( - fontFamily: 'HarmonyOS_Sans_SC', - color: Colors.black, - ), + style: const TextStyle(fontFamily: 'HarmonyOS_Sans_SC'), ), ], ),