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

Flutter从Google Drive下载APK并安装失败求助

解决方案:Flutter APK下载安装失败问题修复

针对你遇到的APK未下载完成、找不到文件的问题,从Google Drive链接处理、权限适配、存储路径、安装流程四个核心点修复,具体步骤如下:

1. 修复Google Drive大文件下载问题

20MB的APK属于Google Drive大文件范畴,直接用uc?export=download链接会触发验证页面,导致Dio无法正确下载。通过Dio拦截器处理跳转和Cookie:

// 在下载方法中添加拦截器
final dio = Dio();
dio.interceptors.add(InterceptorsWrapper(
  onRequest: (options, handler) {
    // 替换为你的Drive文件ID
    options.headers['Cookie'] = 'download_warning_1qh2axZcDbj_OZn_4NHI5CUWTR0ocNFiN=1';
    handler.next(options);
  },
  onResponse: (response, handler) {
    // 处理Google Drive的确认跳转
    if (response.headers['location'] != null) {
      final redirectUrl = response.headers['location']![0];
      if (redirectUrl.contains('confirm')) {
        dio.download(redirectUrl, savePath, onReceiveProgress: (count, total) {
          print('下载进度:${(count/total*100).toStringAsFixed(2)}%');
        });
        return handler.resolve(response);
      }
    }
    handler.next(response);
  },
));

2. 适配Android 10+存储权限

Android 10(API 29)及以上废弃了WRITE_EXTERNAL_STORAGE权限,应用私有存储无需申请权限,同时Android 11+需要单独申请安装权限:

修改权限请求方法

Future<bool> _requestPermission() async {
  if (!Platform.isAndroid) return true;

  final androidInfo = await DeviceInfoPlugin().androidInfo;
  final sdkVersion = androidInfo.version.sdkInt;

  if (sdkVersion >= 30) {
    // Android 11+ 申请安装权限
    var installStatus = await Permission.requestInstallPackages.status;
    if (installStatus.isDenied) {
      installStatus = await Permission.requestInstallPackages.request();
    }
    return installStatus.isGranted;
  } else {
    // Android 9及以下 申请存储权限
    var storageStatus = await Permission.storage.status;
    if (storageStatus.isDenied) {
      storageStatus = await Permission.storage.request();
    }
    return storageStatus.isGranted;
  }
}

更新AndroidManifest.xml

移除冗余权限,添加Android 11+包可见性配置:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<!-- Android 11+ 允许调用安装器 -->
<queries>
  <intent>
    <action android:name="android.intent.action.VIEW" />
    <data android:scheme="content" />
    <data android:scheme="file" />
    <data android:mimeType="application/vnd.android.package-archive" />
  </intent>
</queries>

3. 更换可访问的存储路径

getExternalStorageDirectory()在Android 11+访问受限,改用应用外部私有目录,无需权限且安装器可访问:

final directory = await getExternalFilesDir(null);
final savePath = '${directory!.path}/replace.apk';

4. 增加下载验证与排查日志

添加进度监听、文件存在性检查,方便定位问题:

// 下载前删除旧文件
final file = File(savePath);
if (await file.exists()) {
  await file.delete();
}

// 监听下载进度
await dio.download(apkUrl, savePath, onReceiveProgress: (count, total) {
  if (total != -1) {
    print('已下载:${count/1024/1024}MB / 总大小:${total/1024/1024}MB');
  }
});

// 验证文件是否下载完成
if (await file.exists()) {
  final fileSize = await file.length();
  print('文件保存成功,路径:$savePath,大小:${fileSize/1024/1024}MB');
  await OpenFile.open(savePath);
} else {
  print('错误:下载完成后未找到文件');
}

最终完整代码

需要先在pubspec.yaml添加依赖:

dependencies:
  flutter:
    sdk: flutter
  dio: ^5.4.0+1
  permission_handler: ^11.0.1
  open_file: ^3.3.2
  device_info_plus: ^9.1.1

完整main.dart代码:

import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:open_file/open_file.dart';
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final String apkUrl = 'https://drive.google.com/uc?export=download&id=1qh2axZcDbj_OZn_4NHI5CUWTR0ocNFiN';
  final String fileId = '1qh2axZcDbj_OZn_4NHI5CUWTR0ocNFiN';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('DCS App'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: downloadAndInstallApk,
          child: const Text('下载并安装DCS App'),
        ),
      ),
    );
  }

  Future<void> downloadAndInstallApk() async {
    if (await _requestPermission()) {
      final directory = await getExternalFilesDir(null);
      final savePath = '${directory!.path}/replace.apk';
      final file = File(savePath);

      if (await file.exists()) {
        await file.delete();
        print('已删除旧APK文件');
      }

      try {
        final dio = Dio();
        dio.interceptors.add(InterceptorsWrapper(
          onRequest: (options, handler) {
            options.headers['Cookie'] = 'download_warning_$fileId=1';
            handler.next(options);
          },
          onResponse: (response, handler) {
            if (response.headers['location'] != null) {
              final redirectUrl = response.headers['location']![0];
              if (redirectUrl.contains('confirm')) {
                dio.download(redirectUrl, savePath, onReceiveProgress: (count, total) {
                  _printDownloadProgress(count, total);
                });
                return handler.resolve(response);
              }
            }
            handler.next(response);
          },
        ));

        await dio.download(apkUrl, savePath, onReceiveProgress: (count, total) {
          _printDownloadProgress(count, total);
        });

        if (await file.exists()) {
          final fileSize = await file.length();
          print('文件保存成功,路径:$savePath,大小:${fileSize/1024/1024}MB');
          final result = await OpenFile.open(savePath);
          print('安装启动结果:${result.message}');
        } else {
          print('错误:下载完成后未找到文件');
        }
      } catch (e, stackTrace) {
        print('下载失败:$e');
        print('堆栈信息:$stackTrace');
      }
    } else {
      print('权限被拒绝,无法继续');
    }
  }

  void _printDownloadProgress(int count, int total) {
    if (total != -1) {
      final progress = (count / total * 100).toStringAsFixed(2);
      print('下载进度:$progress%,已下载:${count/1024/1024}MB,总大小:${total/1024/1024}MB');
    } else {
      print('已下载:${count/1024/1024}MB');
    }
  }

  Future<bool> _requestPermission() async {
    if (!Platform.isAndroid) return true;

    final androidInfo = await DeviceInfoPlugin().androidInfo;
    final sdkVersion = androidInfo.version.sdkInt;

    if (sdkVersion >= 30) {
      var installStatus = await Permission.requestInstallPackages.status;
      if (installStatus.isDenied) {
        installStatus = await Permission.requestInstallPackages.request();
      }
      return installStatus.isGranted;
    } else {
      var storageStatus = await Permission.storage.status;
      if (storageStatus.isDenied) {
        storageStatus = await Permission.storage.request();
      }
      return storageStatus.isGranted;
    }
  }
}

内容的提问来源于stack exchange,提问作者PLAA Hemantha

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 09:49:52