SAF测试阶段,验证中

This commit is contained in:
2026-07-05 00:32:28 +08:00
parent dff0f6fb6b
commit 45c69c8ec4
12 changed files with 301 additions and 211 deletions
+20 -23
View File
@@ -6,7 +6,7 @@ plugins {
android { android {
namespace = "com.lxh.battery_health" namespace = "com.lxh.battery_health"
compileSdk = flutter.compileSdkVersion compileSdk = 34
ndkVersion = flutter.ndkVersion ndkVersion = flutter.ndkVersion
compileOptions { compileOptions {
@@ -21,38 +21,39 @@ android {
defaultConfig { defaultConfig {
applicationId = "com.lxh.battery_health" applicationId = "com.lxh.battery_health"
minSdk = flutter.minSdkVersion minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion targetSdk = 34
versionCode = flutter.versionCode versionCode = flutter.versionCode
versionName = flutter.versionName versionName = flutter.versionName
multiDexEnabled = true multiDexEnabled = true
} }
signingConfigs { signingConfigs {
// 如果没有正式签名,可以用 debug 签名代替(测试用)
// 正式发布时请替换为自己的密钥
getByName("debug") {
// debug 签名已经存在,无需额外配置
}
create("release") { create("release") {
// 这里配置你正式发布的签名 // 如果有正式签名,在这里配置
// 如果还没有正式签名,debug 可以用 debug 签名替代 // keyAlias = "..."
keyAlias = "your_key_alias" // keyPassword = "..."
keyPassword = "your_key_password" // storeFile = file("...")
storeFile = file("your_keystore.jks") // storePassword = "..."
storePassword = "your_store_password"
} }
} }
signingConfigs {
// 如果没有正式签名,可以暂时用 debug 签名
// 正式发布时再替换
}
buildTypes { buildTypes {
debug { debug {
// 🟢 关键:debug 版本包名加 .dev 后缀
applicationIdSuffix = ".dev" applicationIdSuffix = ".dev"
signingConfig = signingConfigs.getByName("debug") signingConfig = signingConfigs.getByName("debug")
isDebuggable = true
isMinifyEnabled = false
isShrinkResources = false
} }
release { release {
applicationIdSuffix = "" // 保持原包名 // release 版本不加后缀,保持原包名
// 🟢 如果还没有正式签名,先用 debug 签名替代 signingConfig = signingConfigs.getByName("debug") // 若没有正式签名,先用 debug 签名
signingConfig = signingConfigs.getByName("debug")
isMinifyEnabled = true isMinifyEnabled = true
isShrinkResources = true isShrinkResources = true
proguardFiles( proguardFiles(
@@ -61,16 +62,12 @@ android {
) )
} }
} }
}
// 如果有 productFlavors,可以在这里配置
// 如果没有,忽略即可
}
flutter { flutter {
source = "../.." source = "../.."
} }
dependencies { dependencies {
// 如果项目中有其他依赖,添加在这里 // 无需额外添加 Activity KTX,因为已改用 startActivityForResult
// 注意:sqflite、permission_handler 等 Flutter 插件会自动包含
} }
@@ -4,19 +4,34 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.net.Uri
import android.os.BatteryManager import android.os.BatteryManager
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.DocumentsContract
import android.provider.OpenableColumns
import androidx.activity.ComponentActivity
import androidx.annotation.NonNull import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
import java.io.BufferedReader
import java.io.InputStreamReader
class MainActivity : FlutterActivity() { class MainActivity : FlutterActivity() {
// ---------- 充电状态 ----------
private val CHARGE_CHANNEL = "com.lxh.battery_health/charge" private val CHARGE_CHANNEL = "com.lxh.battery_health/charge"
private val EVENT_CHANNEL = "com.lxh.battery_health/charge_events" private val EVENT_CHANNEL = "com.lxh.battery_health/charge_events"
private var eventSink: EventChannel.EventSink? = null private var eventSink: EventChannel.EventSink? = null
// ---------- SAF 相关 ----------
private val SAF_CHANNEL = "com.lxh.battery_health/saf"
private var safResult: MethodChannel.Result? = null
private var safUri: Uri? = null
private val FOLDER_PICKER_REQUEST = 1001
// ---------- 充电广播接收器 ----------
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
@@ -44,10 +59,132 @@ class MainActivity : FlutterActivity() {
eventSink?.success(mapOf("isCharging" to isCharging, "chargeType" to chargeType)) eventSink?.success(mapOf("isCharging" to isCharging, "chargeType" to chargeType))
} }
private fun getBatteryStatus(): Boolean {
val batteryIntent = registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
val status = batteryIntent?.getIntExtra(BatteryManager.EXTRA_STATUS, -1) ?: -1
return status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL
}
// ---------- SAF 方法 ----------
private fun openFolderPicker() {
try {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
val downloadsUri = DocumentsContract.buildDocumentUri(
"com.android.externalstorage.documents",
"primary:Downloads"
)
putExtra(DocumentsContract.EXTRA_INITIAL_URI, downloadsUri)
} catch (e: Exception) {
// 忽略
}
}
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
startActivityForResult(intent, FOLDER_PICKER_REQUEST)
} catch (e: Exception) {
android.util.Log.e("BatteryHealth", "打开文件夹选择器失败: ${e.message}")
safResult?.error("SELECT_FOLDER_FAILED", e.message, null)
safResult = null
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == FOLDER_PICKER_REQUEST && resultCode == ComponentActivity.RESULT_OK) {
val uri = data?.data
if (uri != null) {
try {
val takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableUriPermission(uri, takeFlags)
safUri = uri
android.util.Log.d("BatteryHealth", "SAF URI: $uri")
safResult?.success(uri.toString())
} catch (e: Exception) {
android.util.Log.e("BatteryHealth", "SAF 授权失败: ${e.message}")
safResult?.error("SAF_FAILED", e.message, null)
}
} else {
safResult?.success(null)
}
safResult = null
}
}
private fun getTreeDocId(): String? {
return safUri?.let { DocumentsContract.getTreeDocumentId(it) }
}
private fun listFiles(): List<String> {
val result = mutableListOf<String>()
val docId = getTreeDocId()
if (docId == null || docId.isEmpty()) return result
try {
val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(safUri!!, docId)
contentResolver.query(childrenUri, null, null, null, null)?.use { cursor ->
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
while (cursor.moveToNext()) {
val name = cursor.getString(nameIndex)
if (name != null) {
result.add(name)
}
}
}
} catch (e: Exception) {
android.util.Log.e("BatteryHealth", "列出文件失败: ${e.message}")
}
return result
}
private fun getFileUri(fileName: String): Uri? {
val docId = getTreeDocId() ?: return null
try {
val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(safUri!!, docId)
contentResolver.query(childrenUri, null, null, null, null)?.use { cursor ->
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
val idIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DOCUMENT_ID)
while (cursor.moveToNext()) {
val name = cursor.getString(nameIndex)
if (name == fileName) {
val fileDocId = cursor.getString(idIndex)
return DocumentsContract.buildDocumentUriUsingTree(safUri!!, fileDocId)
}
}
}
} catch (e: Exception) {
android.util.Log.e("BatteryHealth", "获取文件URI失败: ${e.message}")
}
return null
}
private fun readFile(uri: Uri): String? {
return try {
contentResolver.openInputStream(uri)?.use { inputStream ->
BufferedReader(InputStreamReader(inputStream)).use { reader ->
reader.readText()
}
}
} catch (e: Exception) {
android.util.Log.e("BatteryHealth", "读取文件失败: ${e.message}")
null
}
}
private fun readLogFile(fileName: String): String? {
val fileUri = getFileUri(fileName)
return fileUri?.let { readFile(it) }
}
// ---------- Flutter 通道配置 ----------
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine) super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHARGE_CHANNEL).setMethodCallHandler { call, result -> // 充电状态通道
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHARGE_CHANNEL)
.setMethodCallHandler { call, result ->
if (call.method == "getChargeState") { if (call.method == "getChargeState") {
val isCharging = getBatteryStatus() val isCharging = getBatteryStatus()
val chargeType = if (isCharging) { val chargeType = if (isCharging) {
@@ -68,7 +205,37 @@ class MainActivity : FlutterActivity() {
} }
} }
EventChannel(flutterEngine.dartExecutor.binaryMessenger, EVENT_CHANNEL).setStreamHandler( // SAF 通道
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, SAF_CHANNEL)
.setMethodCallHandler { call, result ->
when (call.method) {
"selectFolder" -> {
safResult = result
openFolderPicker()
}
"readLogFile" -> {
val fileName = call.argument<String>("fileName")
if (fileName != null) {
val content = readLogFile(fileName)
result.success(content)
} else {
result.error("INVALID_ARGUMENT", "fileName is required", null)
}
}
"listFiles" -> {
val files = listFiles()
result.success(files)
}
"getUri" -> {
result.success(safUri?.toString())
}
else -> result.notImplemented()
}
}
// 充电状态事件通道
EventChannel(flutterEngine.dartExecutor.binaryMessenger, EVENT_CHANNEL)
.setStreamHandler(
object : EventChannel.StreamHandler { object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
eventSink = events eventSink = events
@@ -86,11 +253,4 @@ class MainActivity : FlutterActivity() {
} }
) )
} }
private fun getBatteryStatus(): Boolean {
val batteryIntent = registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
val status = batteryIntent?.getIntExtra(BatteryManager.EXTRA_STATUS, -1) ?: -1
return status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL
}
} }
+3 -1
View File
@@ -20,7 +20,9 @@ class MyApp extends StatelessWidget {
title: '电池健康度', title: '电池健康度',
theme: ThemeData( theme: ThemeData(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: 'HarmonyOS_Sans_SC',
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), colorScheme: ColorScheme.fromSeed(
seedColor: const Color.fromARGB(255, 255, 255, 255),
),
useMaterial3: true, useMaterial3: true,
), ),
home: const BatteryHealthPage(), home: const BatteryHealthPage(),
+12 -6
View File
@@ -283,18 +283,23 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
), ),
ElevatedButton( ElevatedButton(
onPressed: () async { onPressed: () async {
final ctx = context; // 触发关闭
Navigator.pop(ctx); Navigator.pop(context);
// 延迟确保动画完成
await Future.delayed(const Duration(milliseconds: 150));
final result = await _safService.requestPermission(); final result = await _safService.requestPermission();
if (result.success) {
if (!mounted) return; if (!mounted) return;
if (result.success) {
setState(() { setState(() {
_safState = SafPermissionState.authorized; _safState = SafPermissionState.authorized;
}); });
readBatteryCapacity(); readBatteryCapacity();
} else { } else {
if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar(
ScaffoldMessenger.of(ctx).showSnackBar(
SnackBar( SnackBar(
content: Text(result.errorMessage ?? '授权失败,请重试'), content: Text(result.errorMessage ?? '授权失败,请重试'),
backgroundColor: Colors.red, backgroundColor: Colors.red,
@@ -303,7 +308,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
_showSafAuthorizationDialog(); _showSafAuthorizationDialog();
} }
}, },
child: const Text('选择文件夹'), // 🟢 加这行 child: const Text('选择文件夹'),
), ),
], ],
), ),
@@ -774,6 +779,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
WidgetsBinding.instance.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
await _safService.init(); await _safService.init();
_safState = await _safService.getPermissionState(); _safState = await _safService.getPermissionState();
if (_safState == SafPermissionState.unauthorized || if (_safState == SafPermissionState.unauthorized ||
_safState == SafPermissionState.expired) { _safState == SafPermissionState.expired) {
if (mounted) _showSafAuthorizationDialog(); if (mounted) _showSafAuthorizationDialog();
+54 -46
View File
@@ -1,4 +1,3 @@
import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@@ -12,55 +11,49 @@ class SafStorageService {
static const MethodChannel _channel = MethodChannel( static const MethodChannel _channel = MethodChannel(
'com.lxh.battery_health/saf', 'com.lxh.battery_health/saf',
); );
static const String _prefKeySafPath = 'saf_storage_path'; static const String _prefKeySafUri = 'saf_storage_uri';
String? _cachedPath; String? _cachedUri;
// 🟢 初始化时从 SharedPreferences 恢复 URI
Future<void> init() async { Future<void> init() async {
try {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
_cachedPath = prefs.getString(_prefKeySafPath); _cachedUri = prefs.getString(_prefKeySafUri);
if (kDebugMode) { if (kDebugMode) {
print('[SAF] 初始化,缓存路径: $_cachedPath'); print('[SAF] 初始化,缓存 URI: $_cachedUri');
}
} catch (e) {
if (kDebugMode) {
print('[SAF] 初始化失败: $e');
}
_cachedUri = null;
} }
} }
Future<SafPermissionState> getPermissionState() async { Future<SafPermissionState> getPermissionState() async {
if (_cachedPath == null || _cachedPath!.isEmpty) { if (_cachedUri == null || _cachedUri!.isEmpty) {
return SafPermissionState.unauthorized; return SafPermissionState.unauthorized;
} }
try { // 🟢 简化:直接返回 authorized,相信本地缓存的 URI
final dir = Directory(_cachedPath!);
if (!await dir.exists()) {
_clearCache();
return SafPermissionState.expired;
}
return SafPermissionState.authorized; return SafPermissionState.authorized;
} catch (e) {
_clearCache();
return SafPermissionState.expired;
}
} }
Future<SafAuthorizationResult> requestPermission() async { Future<SafAuthorizationResult> requestPermission() async {
try { try {
final String? selectedPath = await _channel.invokeMethod('selectFolder'); final String? uriString = await _channel.invokeMethod('selectFolder');
if (selectedPath == null || selectedPath.isEmpty) { if (uriString == null || uriString.isEmpty) {
return SafAuthorizationResult.denied(); return SafAuthorizationResult.denied();
} }
// 验证路径是否包含 mbattery_charger _cachedUri = uriString;
if (!selectedPath.contains('mbattery_charger')) { await _saveUri(uriString);
return SafAuthorizationResult.error('请选择 mbattery_charger 文件夹');
}
_cachedPath = selectedPath;
await _savePath(selectedPath);
if (kDebugMode) { if (kDebugMode) {
print('[SAF] 授权成功,路径: $selectedPath'); print('[SAF] 授权成功,URI: $uriString');
} }
return SafAuthorizationResult.success(selectedPath); return SafAuthorizationResult.success(uriString);
} catch (e) { } catch (e) {
if (kDebugMode) { if (kDebugMode) {
print('[SAF] 授权失败: $e'); print('[SAF] 授权失败: $e');
@@ -73,8 +66,11 @@ class SafStorageService {
final state = await getPermissionState(); final state = await getPermissionState();
if (state != SafPermissionState.authorized) return false; if (state != SafPermissionState.authorized) return false;
try { try {
final file = File('${_cachedPath!}/$fileName'); final files = await _channel.invokeMethod('listFiles');
return await file.exists(); if (files is List) {
return files.contains(fileName);
}
return false;
} catch (e) { } catch (e) {
return false; return false;
} }
@@ -84,9 +80,11 @@ class SafStorageService {
final state = await getPermissionState(); final state = await getPermissionState();
if (state != SafPermissionState.authorized) return []; if (state != SafPermissionState.authorized) return [];
try { try {
final file = File('${_cachedPath!}/$fileName'); final String? content = await _channel.invokeMethod('readLogFile', {
if (!await file.exists()) return []; 'fileName': fileName,
return await file.readAsLines(); });
if (content == null || content.isEmpty) return [];
return content.split('\n');
} catch (e) { } catch (e) {
if (kDebugMode) { if (kDebugMode) {
print('[SAF] 读取文件失败: $e'); print('[SAF] 读取文件失败: $e');
@@ -99,13 +97,11 @@ class SafStorageService {
final state = await getPermissionState(); final state = await getPermissionState();
if (state != SafPermissionState.authorized) return []; if (state != SafPermissionState.authorized) return [];
try { try {
final dir = Directory(_cachedPath!); final result = await _channel.invokeMethod('listFiles');
if (!await dir.exists()) return []; if (result is List) {
final entities = await dir.list().toList(); return result.cast<String>();
return entities }
.where((e) => e is File) return [];
.map((e) => e.path.split(Platform.pathSeparator).last)
.toList();
} catch (e) { } catch (e) {
if (kDebugMode) { if (kDebugMode) {
print('[SAF] 列出文件失败: $e'); print('[SAF] 列出文件失败: $e');
@@ -120,18 +116,30 @@ class SafStorageService {
} }
void _clearCache() { void _clearCache() {
_cachedPath = null; _cachedUri = null;
_savePath(null); _saveUri(null);
} }
Future<void> _savePath(String? path) async { Future<void> _saveUri(String? uri) async {
try {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
if (path != null && path.isNotEmpty) { if (uri != null && uri.isNotEmpty) {
await prefs.setString(_prefKeySafPath, path); await prefs.setString(_prefKeySafUri, uri);
if (kDebugMode) {
print('[SAF] 保存 URI 成功: $uri');
}
} else { } else {
await prefs.remove(_prefKeySafPath); await prefs.remove(_prefKeySafUri);
if (kDebugMode) {
print('[SAF] 清除 URI');
}
}
} catch (e) {
if (kDebugMode) {
print('[SAF] 保存 URI 失败: $e');
}
} }
} }
String? get cachedPath => _cachedPath; String? get cachedUri => _cachedUri;
} }
@@ -6,10 +6,6 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
} }
-1
View File
@@ -3,7 +3,6 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST
@@ -6,13 +6,11 @@ import FlutterMacOS
import Foundation import Foundation
import device_info_plus import device_info_plus
import file_selector_macos
import shared_preferences_foundation import shared_preferences_foundation
import sqflite_darwin import sqflite_darwin
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
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"))
} }
-72
View File
@@ -57,14 +57,6 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.19.1" version: "1.19.1"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "92c9c43c383bfa1c32079d3bc492d55d6d4318044b7b47edaff8971cbb555c51"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.5+4"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@@ -121,70 +113,6 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "7.0.1" version: "7.0.1"
file_selector:
dependency: "direct main"
description:
name: file_selector
sha256: bd15e43e9268db636b53eeaca9f56324d1622af30e5c34d6e267649758c84d9a
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
file_selector_android:
dependency: transitive
description:
name: file_selector_android
sha256: "6a26687fa65cbc28a5345c7ae6f227e89f0b47740978a4c475b1a625da7a331b"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.5.2+8"
file_selector_ios:
dependency: transitive
description:
name: file_selector_ios
sha256: e2ecf2885c121691ce13b60db3508f53c01f869fb6e8dc5c1cfa771e4c46aeca
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.5.3+5"
file_selector_linux:
dependency: transitive
description:
name: file_selector_linux
sha256: "2567f398e06ac72dcf2e98a0c95df2a9edd03c2c2e0cacd4780f20cdf56263a0"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.9.4"
file_selector_macos:
dependency: transitive
description:
name: file_selector_macos
sha256: "5e0bbe9c312416f1787a68259ea1505b52f258c587f12920422671807c4d618a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.9.5"
file_selector_platform_interface:
dependency: transitive
description:
name: file_selector_platform_interface
sha256: "35e0bd61ebcdb91a3505813b055b09b79dfdc7d0aee9c09a7ba59ae4bb13dc85"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.7.0"
file_selector_web:
dependency: transitive
description:
name: file_selector_web
sha256: "73181fbc5257776d8ecaa6a94ab3c8e920ad143b9132a6d984a9271dfc6928d3"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.9.5"
file_selector_windows:
dependency: transitive
description:
name: file_selector_windows
sha256: "62197474ae75893a62df75939c777763d39c2bc5f73ce5b88497208bc269abfd"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.9.3+5"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
+2 -2
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.3.0-dev.2 version: 1.3.0-dev.4
environment: environment:
sdk: ^3.9.2 sdk: ^3.9.2
@@ -38,7 +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
file_selector: ^1.0.0 # 🟢 新增
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
@@ -6,12 +6,9 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <file_selector_windows/file_selector_windows.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h> #include <permission_handler_windows/permission_handler_windows_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
PermissionHandlerWindowsPluginRegisterWithRegistrar( PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
} }
-1
View File
@@ -3,7 +3,6 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
file_selector_windows
permission_handler_windows permission_handler_windows
) )