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);
我是 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);