You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用File Picker与Permission Handler保存文件时Flutter应用崩溃

Flutter保存文本文件崩溃,仅生成0字节空文件问题解决

问题描述

开发Flutter应用时,尝试将文本文件保存到设备存储,使用file_picker选择保存位置、permission_handler申请权限,但保存时应用崩溃,仅生成0字节空文件。

现有代码实现

Flutter保存函数

Future<void> saveTextFile() async {
    var storageStatus = await Permission.storage.status;
    var manageStorageStatus = await Permission.manageExternalStorage.status;

if (kDebugMode) {
 print('::::::::::::: Storage status: $storageStatus');
 print('::::::::::::: Manage storage status: $manageStorageStatus');
}

if (!storageStatus.isGranted) {
storageStatus = await Permission.storage.request();
} 

if (storageStatus.isDenied || storageStatus.isPermanentlyDenied) {
  manageStorageStatus = await Permission.manageExternalStorage.request();
}

if (storageStatus.isGranted || manageStorageStatus.isGranted) {
  try {
    // Create some lorem ipsum text
    String loremText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
    // Convert the text to bytes
  final bytes = utf8.encode(loremText);
  String fileName = 'LoremIpsum.txt';

  // Open the file picker for the user to choose the save location
  String? outputFile = await FilePicker.platform.saveFile(
    dialogTitle: 'Save your text file',
    fileName: fileName,
    type: FileType.custom,
    allowedExtensions: ['txt'],
  );

  if (outputFile != null) {
    final file = File(outputFile);
    await file.writeAsBytes(bytes, flush: true);

    // Show a message indicating the file has been saved.
    Get.snackbar('Success', 'Lorem ipsum text has been saved as a text file.');
  } else {
    // User canceled the file save dialog
    Get.snackbar('Cancelled', 'File save operation was cancelled.');
  }
} catch (e) {
  // Catch any errors during the file writing process
  Get.snackbar('Error', 'An error occurred while saving the file: $e');
  debugPrint('Error saving file: $e');
 }
} else {
// Handle permission denial
Get.snackbar('Permission Denied', 'Storage permission is required to save the file.');
}

AndroidManifest.xml配置

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

<application
    android:label="SJIC Admin"
    android:name="${applicationName}"
    android:icon="@mipmap/launcher_icon"
    android:requestLegacyExternalStorage="true">
    
    <activity
        android:name=".MainActivity"
        android:exported="true"
        android:launchMode="singleTop"
        android:theme="@style/LaunchTheme"
        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
        android:hardwareAccelerated="true"
        android:windowSoftInputMode="adjustResize">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

问题现象

  1. 通过permission_handler申请存储权限
  2. 生成文本内容并转为字节
  3. 用file_picker选择保存位置
  4. 保存时应用崩溃,仅生成0字节空文件

已尝试操作

  • build.gradle配置:minSdkVersion 30
  • 依赖版本:Flutter SDK 3.19.3,path_provider: ^2.1.3,permission_handler: ^11.3.1,file_picker: ^8.0.3

解决方案

1. 修正权限逻辑(适配Android 11+存储机制)

Android 11(API 30)及以上,WRITE_EXTERNAL_STORAGE权限已被废弃,MANAGE_EXTERNAL_STORAGE仅适用于文件管理器类应用,普通应用无需申请。file_picker的saveFile方法通过系统存储访问框架(SAF)实现,用户选择保存位置后自动获得该文件的写入权限,无需额外申请全局存储权限。

仅在Android 10及以下版本,需要申请传统存储权限。

2. 优化后的Flutter代码

import 'package:device_info_plus/device_info_plus.dart';

Future<void> saveTextFile() async {
  // 获取Android SDK版本
  final androidInfo = await DeviceInfoPlugin().androidInfo;
  final int sdkVersion = androidInfo.version.sdkInt;

  if (kDebugMode) {
    print('当前SDK版本: $sdkVersion');
  }

  // 仅在Android 10及以下申请存储权限
  if (sdkVersion < 30) {
    var storageStatus = await Permission.storage.status;
    if (!storageStatus.isGranted) {
      storageStatus = await Permission.storage.request();
      if (storageStatus.isDenied || storageStatus.isPermanentlyDenied) {
        Get.snackbar('权限拒绝', '需要存储权限才能保存文件');
        return;
      }
    }
  }

  try {
    const String loremText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
    const String fileName = 'LoremIpsum.txt';

    String? outputFile = await FilePicker.platform.saveFile(
      dialogTitle: '保存文本文件',
      fileName: fileName,
      type: FileType.custom,
      allowedExtensions: ['txt'],
    );

    if (outputFile != null) {
      final file = File(outputFile);
      // 直接写入字符串更高效,无需手动转字节
      await file.writeAsString(loremText, flush: true);
      Get.snackbar('成功', '文本文件已保存');
    } else {
      Get.snackbar('已取消', '文件保存操作已取消');
    }
  } catch (e, stackTrace) {
    Get.snackbar('错误', '保存文件时出错: $e');
    // 打印堆栈信息便于排查
    debugPrint('保存文件错误详情: $e\n$stackTrace');
  }
}

3. 修正AndroidManifest.xml配置

移除不必要的权限,仅保留低版本所需的读取权限:

<!-- 仅在Android 10及以下需要读取权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="29" />

<application
    android:label="SJIC Admin"
    android:name="${applicationName}"
    android:icon="@mipmap/launcher_icon">
    
    <activity
        android:name=".MainActivity"
        android:exported="true"
        android:launchMode="singleTop"
        android:theme="@style/LaunchTheme"
        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
        android:hardwareAccelerated="true"
        android:windowSoftInputMode="adjustResize">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

4. 添加依赖

需要添加device_info_plus来获取Android SDK版本,在pubspec.yaml中添加:

dependencies:
  device_info_plus: ^10.1.0

问题原因说明

  1. 权限逻辑错误:在Android 11+下错误申请MANAGE_EXTERNAL_STORAGE权限,该权限与SAF机制冲突,导致写入文件时权限异常。
  2. 传统存储权限失效:Android 11+不再认可WRITE_EXTERNAL_STORAGE权限,直接使用会导致写入失败。
  3. 异常捕获不完整:原代码未捕获堆栈信息,难以定位崩溃具体原因。

内容的提问来源于stack exchange,提问作者abishek perera

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 01:37:32