FlutterDownloader 可以在 android 下载 pdf 但在 iOS 失败

FlutterDownloader works downloading pdf in android but fails on iOS

我是 flutter/iOS 的新手。

我正在使用:

Flutter 1.22.6 • 通道稳定 • https://github.com/flutter/flutter.git 框架 • 修订版 9b2d32b605 • 2021-01-22 14:36:39 -0800 引擎 • 修订版 2f0af37152 工具 • Dart 2.10.5 flutter_downloader: ^1.4.4

我必须更正一个我没有编码但试图理解它的应用程序。它下载一个 pdf 文件并打开它,但在 iOS.

中不起作用

我在 https://github.com/fluttercommunity/flutter_downloader 中阅读的所有配置都是正确的。

Flutter医生还行

下面我给大家展示部分代码

main.dart

final _prefs = SharedPreferences();

void main() async {
  WidgetsFlutterBinding.ensureInitialized(); 
  final prefs = SharedPreferences();
  await prefs.initPrefs();
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown
  ]);
  WidgetsFlutterBinding.ensureInitialized();
  await FlutterDownloader.initialize(debug: true);
  _prefs.uid = await getId();
  runApp(MyApp());
}

pages/registry/facture.dart

List<Widget> _actionsCreateBar(BuildContext context) {
  return <Widget>[
    (document.id != null)
        ? IconButton(
            icon: Icon(EvaIcons.downloadOutline),
            onPressed: () async {
              _downloadAction(); // This method is executed when user press download icon
            },
            color: primaryColor,
            iconSize: 25,
          )
        : Container(),
  ];
}

void _downloadAction() async {
  if (await utils.isInternetAvailable()) {
    if (await _validateUrlRideBeforeDownload()) {
      await _pdfBloc.downloadPdf(document.url_ride, Theme.of(context).platform);
      setState(() {});
      return;
    }
    _showDialogOk(
        context, 'Download', 'Wait please');
  } else {
    _showDialogOk(context, 'Info',
        'No conection');
  }
}

bloc/pdf/pdfbloc.dart

class PdfBloc {

  final _downloadingController    = BehaviorSubject<bool>();
  final _loadingController        = BehaviorSubject<bool>();
  final _progressStringController = BehaviorSubject<String>();
  final _pdfProvider              = DownloadProvider();
  
  
  Stream<String> get progressStringStream => _progressStringController.stream;
  Stream<bool> get loadingStream => _loadingController.stream;
  Stream<bool> get downloadingStream => _downloadingController.stream;

  Future<ResponseData> downloadPdf(String url, var platform) async {
    _downloadingController.sink.add(true);
    ResponseData resData = await _pdfProvider.downloadPdf(url, _progressStringController, platform);
    _downloadingController.sink.add(false);
    return resData;
  }

  dispose() {
    _downloadingController.close();
    _progressStringController.close();
    _loadingController.close();
  }
}

provider/download/downloadprovider.dart

class DownloadProvider {

  Future<ResponseData> downloadPdf(String url, dynamic progressString, var platform) async {
  ResponseData resData = ResponseData();
  final _prefs = SharedPreferences();
    try {
      var path = await findLocalPath(platform) + '/';
      FlutterDownloader.cancelAll();
      final String taskId = await FlutterDownloader.enqueue(
        url: url,
        savedDir: path,
        showNotification: true, // show download progress in status bar (for Android)
        openFileFromNotification: true, // click on notification to open downloaded file (for Android)
        headers: {HttpHeaders.authorizationHeader: _prefs.token, 'Content-type': 'application/json'},
      );

      // Last developer used this "while" to wait while a dialog is shown
      // Android behaviour: flutter says "only success task can be opened" but then it works
      // iOS behaviour: flutter says "only success task can be opened" infinitely and never 
      // shows the pdf

      // In iOS this loop iterates forever
      while(!await FlutterDownloader.open(taskId: taskId,)) {
        // Last developer did this validation, but I don't know why
        if (platform == TargetPlatform.iOS) {
          await FlutterDownloader.open(taskId: taskId);
        }
      }
      _setResponseData(resData, 'Completed', false);
      return resData;
    } catch(e) {
      _setResponseData(resData, 'Error', true);
      return resData;
    }
  }

  _setResponseData(ResponseData resData, String message, bool state) {
    resData.setData(message);
    resData.setError(state);
  }

}
Future<String> findLocalPath(var platform) async {
  final directory = platform == TargetPlatform.android
      ? await getExternalStorageDirectory()
      : await getApplicationDocumentsDirectory();
  return directory.path;
}

我试过几个版本的ios和iphone都没有成功。

有什么想法吗? 请帮助我,我卡住了。

谢谢。

在 Xcode

中打开您的 ios 项目

添加 sqlite 库。

配置 AppDelegate:

/// AppDelegate.h
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>

@interface AppDelegate : FlutterAppDelegate

@end

// AppDelegate.m
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
#include "FlutterDownloaderPlugin.h"

@implementation AppDelegate

void registerPlugins(NSObject<FlutterPluginRegistry>* registry) {   
  if (![registry hasPlugin:@"FlutterDownloaderPlugin"]) {
     [FlutterDownloaderPlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterDownloaderPlugin"]];
  }
}

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];
  [FlutterDownloaderPlugin setPluginRegistrantCallback:registerPlugins];
  // Override point for customization after application launch.
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end

完全禁用 ATS:(将以下代码添加到您的 Info.plist 文件)

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key><true/>
</dict>

配置最大并发任务数:插件默认允许同时下载3个任务运行(如果入队超过3个任务,则只有3个任务运行,其他任务处于挂起状态)。您可以通过将以下代码添加到 Info.plist 文件来更改此数字。

<!-- changes this number to configure the maximum number of concurrent tasks -->
<key>FDMaximumConcurrentTasks</key>
<integer>5</integer>

本地化通知消息:如果您的应用程序不在前台 运行 所有文件都已下载,该插件将发送一条通知消息来通知用户。此消息默认为英文。您可以通过在 Info.plist 文件中添加和本地化以下消息来本地化此消息。 (您可以在此 link 中找到 Info.plist 本地化的详细信息)

 <key>FDAllFilesDownloadedMessage</key>
<string>All files have been downloaded</string>

我可以解决这个问题。以前的开发人员犯了一个错误的编程习惯,当试图在不检查其状态的情况下强制打开任务时,在 ios 中导致了竞争条件。

我不得不更改“while”循环并在其中检查下载任务的状态和进度。一旦它达到 100% 进度并且它的状态是完成,然后我们打破循环并最终打开任务。

provider/download/downloadprovider.dart

  bool waitTask = true;
  while(waitTask) {
    String query = "SELECT * FROM task WHERE task_id='" + taskId + "'";
    var _tasks = await FlutterDownloader.loadTasksWithRawQuery(query: query);
    String taskStatus = _tasks[0].status.toString();
    int taskProgress = _tasks[0].progress;
    if(taskStatus == "DownloadTaskStatus(3)" && taskProgress == 100){ 
      waitTask = false;
    }
  }

  await FlutterDownloader.open(taskId: taskId);