Flutter文件下载管理插件fl_downloader的使用

发布于 1周前 作者 ionicwang 来自 Flutter

Flutter文件下载管理插件fl_downloader的使用

插件介绍

fl_downloader 是一个用于Flutter应用程序的插件,它允许您通过原生功能下载文件。在Android上,它使用系统的DownloadManager服务;在Windows上,它使用BITS(Background Intelligent Transfer Service)将文件下载到用户的Downloads文件夹中;在iOS上,它使用URLSession将文件下载到App Documents文件夹。

iOS配置

如果您不想让用户在Files应用中看到已下载的文件,则无需特殊配置。 如果希望用户能在Files应用中查看已下载的文件,请在您的info.plist文件中添加以下行:

<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>UIFileSharingEnabled</key>
<true/>

Android配置

对于Android 10+,无需特殊配置。 如果您的应用支持Android 9(API 28)或更低版本,在调用download()之前必须调用requestPermission()并检查权限状态。

注意:此插件期望compileSdk是最新版本的Android SDK,例如:

android {
    compileSdk 34

    [...]
}

Windows配置

Windows平台无需特殊配置。

需要注意的是,了解BITS的限制和测试其使用情况非常重要,相关文档如下:

示例代码

下面是一个完整的示例demo,展示了如何使用fl_downloader插件进行文件下载,并实时更新下载进度、处理不同状态等。

import 'dart:async';

import 'package:fl_downloader/fl_downloader.dart';
import 'package:flutter/material.dart';

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

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final TextEditingController fileNameController = TextEditingController(
    text: 'test.pdf',
  );
  final TextEditingController urlController = TextEditingController(
    text:
        'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf',
  );

  int progress = 0;
  dynamic downloadId;
  String? status;
  late StreamSubscription progressStream;

  @override
  void initState() {
    FlDownloader.initialize();
    progressStream = FlDownloader.progressStream.listen((event) {
      if (event.status == DownloadStatus.successful) {
        debugPrint('event.progress: ${event.progress}');
        setState(() {
          progress = event.progress;
          downloadId = event.downloadId;
          status = event.status.name;
        });
        // 自动打开下载完成后的文件
        FlDownloader.openFile(filePath: event.filePath);
      } else if (event.status == DownloadStatus.running) {
        debugPrint('event.progress: ${event.progress}');
        setState(() {
          progress = event.progress;
          downloadId = event.downloadId;
          status = event.status.name;
        });
      } else if (event.status == DownloadStatus.failed) {
        debugPrint('event: $event');
        setState(() {
          progress = event.progress;
          downloadId = event.downloadId;
          status = event.status.name;
        });
      } else if (event.status == DownloadStatus.paused) {
        debugPrint('Download paused');
        setState(() {
          progress = event.progress;
          downloadId = event.downloadId;
          status = event.status.name;
        });
        // 在暂停后重新附加下载进度
        Future.delayed(
          const Duration(milliseconds: 250),
          () => FlDownloader.attachDownloadProgress(event.downloadId),
        );
      } else if (event.status == DownloadStatus.pending) {
        debugPrint('Download pending');
        setState(() {
          progress = event.progress;
          downloadId = event.downloadId;
          status = event.status.name;
        });
      }
    });
    super.initState();
  }

  @override
  void dispose() {
    progressStream.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.light(useMaterial3: true),
      darkTheme: ThemeData.dark(useMaterial3: true),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('FlDownloader example app'),
        ),
        body: Column(
          children: [
            if (progress > 0 && progress < 100)
              LinearProgressIndicator(
                value: progress / 100,
                color: Colors.orange,
              ),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
              child: TextField(
                controller: urlController,
                decoration: const InputDecoration(
                  label: Text('URL'),
                  border: OutlineInputBorder(),
                ),
              ),
            ),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
              child: TextField(
                controller: fileNameController,
                decoration: const InputDecoration(
                  label: Text('File name'),
                  border: OutlineInputBorder(),
                ),
              ),
            ),
            const Spacer(),
            Text(
              'Download id: $downloadId\n'
              'Status: $status\n'
              'Progress: $progress%',
            ),
            const Spacer(),
          ],
        ),
        floatingActionButton: Row(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            FloatingActionButton(
              backgroundColor: Colors.red[300],
              child: const Icon(Icons.close),
              onPressed: () async {
                final cancelList = [downloadId];
                final cancelled = await FlDownloader.cancel(cancelList);
                if (cancelled == cancelList.length) {
                  setState(() {
                    progress = 0;
                    downloadId = null;
                    status = 'All downloads cancelled';
                  });
                } else {
                  setState(() {
                    progress = 0;
                    downloadId = null;
                    status = 'Cancelled $cancelled downloads from the list';
                  });
                }
              },
            ),
            const SizedBox(width: 10),
            FloatingActionButton(
              child: const Icon(Icons.download_sharp),
              onPressed: () async {
                final permission = await FlDownloader.requestPermission();
                if (permission == StoragePermissionStatus.granted) {
                  await FlDownloader.download(
                    urlController.text,
                    fileName: fileNameController.text,
                  );
                } else {
                  debugPrint('Permission denied =(');
                }
              },
            ),
          ],
        ),
      ),
    );
  }
}

这个示例提供了一个简单的界面,允许用户输入文件的URL和名称,然后点击按钮开始下载。同时,它还显示了下载进度条以及当前的下载状态。当下载完成后,会尝试自动打开下载的文件。此外,也提供了取消下载的功能。


更多关于Flutter文件下载管理插件fl_downloader的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter文件下载管理插件fl_downloader的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用fl_downloader插件进行文件下载管理的代码示例。fl_downloader是一个强大的Flutter插件,用于在后台处理文件下载任务,并提供下载进度的回调。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加fl_downloader依赖:

dependencies:
  flutter:
    sdk: flutter
  fl_downloader: ^2.0.3  # 确保使用最新版本

然后运行flutter pub get来安装依赖。

2. 配置Android和iOS

Android

android/app/src/main/AndroidManifest.xml中添加以下权限:

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

并配置FlutterDownloader服务:

<service
    android:name="vn.hunghd.flutterdownloader.FlutterDownloaderService"
    android:enabled="true"
    android:exported="true"
    android:foregroundServiceType="mediaProjection"/>

iOS

ios/Runner/Info.plist中添加以下权限:

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

3. 初始化插件

在你的Flutter项目的入口文件(通常是main.dart)中初始化FlutterDownloader

import 'package:flutter/material.dart';
import 'package:fl_downloader/fl_downloader.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  FlutterDownloader.initialize(
    debug: true // 开启调试模式(生产环境建议关闭)
  ).then((value) {
    runApp(MyApp());
  });
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

4. 开始下载

在你的下载页面(如HomePage.dart)中添加下载功能:

import 'package:flutter/material.dart';
import 'package:fl_downloader/fl_downloader.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final String taskId = "task_id_123";
  String downloadUrl = "https://example.com/yourfile.zip";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("File Downloader"),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            startDownload();
          },
          child: Text("Start Download"),
        ),
      ),
    );
  }

  void startDownload() async {
    final Task task = FlutterDownloader.enqueue(
      taskId: taskId,
      url: downloadUrl,
      savedDir: "${FlutterDownloader.defaultStorageDir}/downloads/",
      showNotification: true,
      openFileFromNotification: true,
    );

    task.listen(
      (id, status, progress) {
        if (status == TaskStatus.completed) {
          print("Download completed: $id");
        } else if (status == TaskStatus.running) {
          print("Download running: $progress");
        } else {
          print("Download failed: $status");
        }
      },
      onError: (id, error, exception) => print("Download error: $error"),
    );
  }
}

5. 处理下载完成后的文件

当用户点击通知栏的下载完成时,fl_downloader会自动打开文件。如果你想在下载完成后进行其他操作,可以监听task.listen中的TaskStatus.completed状态。

6. 清理和取消下载

你可以使用FlutterDownloader.cancel(taskId: taskId)来取消下载任务,或者使用FlutterDownloader.clean(taskId: taskId)来清理下载任务及其文件。

总结

以上是如何在Flutter项目中使用fl_downloader插件进行文件下载管理的完整示例。这个插件提供了强大的后台下载功能,并支持下载进度监听和通知栏显示。希望这个示例能够帮助你快速上手fl_downloader的使用。

回到顶部