flutter_downloader 0.0.5

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • new50

flutter_downloader

A plugin for creating and managing download tasks. Supports iOS and Android.

This plugin is based on WorkManager in Android and NSURLSessionDownloadTask in iOS to run download task in background mode.

iOS integration

  • Open Xcode. Enable background mode.
  • Add following code to your AppDelegate (this method will be called when an URL session finished its work while your app is not running):
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler {
    completionHandler();
}

Note: If you want to download file with HTTP request, you need to disable Apple Transport Security (ATS) feature.

  • Disable ATS for a specific domain only: (add following codes to the end of your Info.plist file)
<key>NSAppTransportSecurity</key>
<dict>
  <key>NSExceptionDomains</key>
  <dict>
    <key>www.yourserver.com</key>
    <dict>
      <!-- add this key to enable subdomains such as sub.yourserver.com -->
      <key>NSIncludesSubdomains</key>
      <true/>
      <!-- add this key to allow standard HTTP requests, thus negating the ATS -->
      <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
      <true/>
      <!-- add this key to specify the minimum TLS version to accept -->
      <key>NSTemporaryExceptionMinimumTLSVersion</key>
      <string>TLSv1.1</string>
    </dict>
  </dict>
</dict>
  • Completely disable ATS: (add following codes to the end of your Info.plist file)
<key>NSAppTransportSecurity</key>  
<dict>  
    <key>NSAllowsArbitraryLoads</key><true/>  
</dict>

Usage

import 'package:flutter_downloader/flutter_downloader.dart';

To create new download task:

final taskId = await FlutterDownloader.enqueue(
  url: 'your download link', 
  savedDir: 'the path of directory where you want to save downloaded files', 
  showNotification: true // show download progress in status bar (for Android)
);

To update download progress:

FlutterDownloader.registerCallback((id, status, progress) {
  // code to update your UI
});

To load the status of download tasks:

final tasks = await FlutterDownloader.loadTasks();

To cancel a task:

FlutterDownloader.cancel(taskId: taskId);

To cancel all tasks:

FlutterDownloader.cancelAll();

0.0.1

  • initial release.

0.0.2

  • correct README document

0.0.3

  • support HTTP headers

0.0.4

  • fix bug: Worker finished with FAILURE on Android API 26 and above

0.0.5

  • update metadata

example/lib/main.dart

import 'package:flutter/material.dart';
import 'dart:async';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'dart:convert';
import 'dart:io';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Downloader'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
  final _documents = [
    {
      'name': 'Beginning Android Application Development',
      'link': 'http://www.kmvportal.co.in/Course/MAD/Android%20Book.pdf'
    },
    {
      'name': 'Android Programming Cookbook',
      'link':
          'http://enos.itcollege.ee/~jpoial/allalaadimised/reading/Android-Programming-Cookbook.pdf'
    },
    {
      'name': 'iOS Programming Guide',
      'link':
          'http://darwinlogic.com/uploads/education/iOS_Programming_Guide.pdf'
    },
    {
      'name': 'Objective-C Programming (Pre-Course Workbook',
      'link':
          'https://www.bignerdranch.com/documents/objective-c-prereading-assignment.pdf'
    }
  ];

  List<_TaskInfo> _tasks;
  bool _isLoading;
  String _localPath;

  @override
  void initState() {
    super.initState();

    WidgetsBinding.instance.addObserver(this);

    FlutterDownloader.registerCallback((id, status, progress) {
      print(
          'Download task ($id) is in status ($status) and process ($progress)');
      final task = _tasks.firstWhere((task) => task.taskId == id);
      setState(() {
        task?.status = status;
        task?.progress = progress;
      });
    });

    _isLoading = true;

    _loadTasks();
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    print('AppLifecycleState changed: $state');
    if (state == AppLifecycleState.paused) {
      _saveDocuments();
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: _isLoading
          ? new Center(
              child: new CircularProgressIndicator(),
            )
          : new Container(
              child: new ListView(
                children: _tasks
                    .map((task) => new Container(
                          padding: const EdgeInsets.symmetric(horizontal: 16.0),
                          child: new Stack(
                            children: <Widget>[
                              new Container(
                                width: double.infinity,
                                height: 64.0,
                                child: new Row(
                                  crossAxisAlignment: CrossAxisAlignment.center,
                                  children: <Widget>[
                                    new Expanded(
                                      child: new Text(
                                        task.name,
                                        maxLines: 1,
                                        softWrap: true,
                                        overflow: TextOverflow.ellipsis,
                                      ),
                                    ),
                                    new Padding(
                                      padding: const EdgeInsets.only(left: 8.0),
                                      child: _buildActionForTask(task),
                                    ),
                                  ],
                                ),
                              ),
                              task.status == DownloadTaskStatus.running
                                  ? new Positioned(
                                      left: 0.0,
                                      right: 0.0,
                                      bottom: 0.0,
                                      child: new LinearProgressIndicator(
                                        value: task.progress / 100,
                                      ),
                                    )
                                  : new Container()
                            ].where((child) => child != null).toList(),
                          ),
                        ))
                    .toList(),
              ),
            ),
    );
  }

  Widget _buildActionForTask(_TaskInfo task) {
    if (task.status == DownloadTaskStatus.undefined) {
      return new RawMaterialButton(
        onPressed: () {
          _requestDownload(task);
        },
        child: new Icon(Icons.file_download),
        shape: new CircleBorder(),
        constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
      );
    } else if (task.status == DownloadTaskStatus.running) {
      return new RawMaterialButton(
        onPressed: () {
          _cancelDownload(task);
        },
        child: new Icon(
          Icons.stop,
          color: Colors.red,
        ),
        shape: new CircleBorder(),
        constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
      );
    } else if (task.status == DownloadTaskStatus.complete) {
      return new Text(
        'Ready',
        style: new TextStyle(color: Colors.green),
      );
    } else if (task.status == DownloadTaskStatus.canceled) {
      return new Text('Canceled', style: new TextStyle(color: Colors.red));
    } else if (task.status == DownloadTaskStatus.failed) {
      return new Text('Failed', style: new TextStyle(color: Colors.red));
    } else {
      return null;
    }
  }

  void _requestDownload(_TaskInfo task) async {
    task.taskId = await FlutterDownloader.enqueue(
      url: task.link,
      savedDir: _localPath,
      showNotification: true,
    );
  }

  void _cancelDownload(_TaskInfo task) async {
    await FlutterDownloader.cancel(taskId: task.taskId);
  }

  Future<String> _findLocalPath() async {
    final directory = await getApplicationDocumentsDirectory();
    return directory.path;
  }

  Future<Null> _loadTasks() async {
    final path = _localPath ?? await _findLocalPath();
    _localPath = path;
    final file = new File('$path/tasks.json');
    final fileExisted = await file.exists();
    if (fileExisted) {
      final taskJson = await file.readAsString();
      final List<dynamic> array = json.decode(taskJson);
      if (array != null) {
        _tasks = array.map((item) => new _TaskInfo.fromJson(item)).toList();
        final tasks = await FlutterDownloader.loadTasks();
        for (final task in tasks) {
          _tasks.firstWhere((item) => item.taskId == task.taskId)
            ..status = task.status
            ..progress = task.progress;
        }
      } else {
        _tasks = _documents
            .map((document) =>
                _TaskInfo(name: document['name'], link: document['link']))
            .toList();
      }
    } else {
      _tasks = _documents
          .map((document) =>
              _TaskInfo(name: document['name'], link: document['link']))
          .toList();
    }
    setState(() {
      _isLoading = false;
    });
  }

  _saveDocuments() {
    final path = _localPath;
    final file = new File('$path/tasks.json');
    final fileExisted = file.existsSync();
    if (!fileExisted) {
      file.createSync();
    }
    file.writeAsStringSync(json.encode(_tasks));
  }
}

class _TaskInfo {
  final String name;
  final String link;

  String taskId;
  int progress = 0;
  DownloadTaskStatus status = DownloadTaskStatus.undefined;

  _TaskInfo({this.name, this.link});

  _TaskInfo.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        link = json['link'],
        taskId = json['task_id'],
        progress = json['progress'] ?? 0,
        status = DownloadTaskStatus.from(json['status'] ?? 0);

  Map<String, dynamic> toJson() => {
        'name': name,
        'link': link,
        'task_id': taskId,
        'progress': progress,
        'status': status.value
      };
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  flutter_downloader: "^0.0.5"

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter packages get

Alternatively, your editor might support flutter packages get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


      import 'package:flutter_downloader/flutter_downloader.dart';
  
Version Uploaded Documentation Archive
0.0.5 Jun 20, 2018 Go to the documentation of flutter_downloader 0.0.5 Download flutter_downloader 0.0.5 archive
0.0.4 Jun 15, 2018 Go to the documentation of flutter_downloader 0.0.4 Download flutter_downloader 0.0.4 archive
0.0.3 Jun 11, 2018 Go to the documentation of flutter_downloader 0.0.3 Download flutter_downloader 0.0.3 archive
0.0.2 Jun 8, 2018 Go to the documentation of flutter_downloader 0.0.2 Download flutter_downloader 0.0.2 archive
0.0.1 Jun 8, 2018 Go to the documentation of flutter_downloader 0.0.1 Download flutter_downloader 0.0.1 archive

Analysis

We analyzed this package on Jun 20, 2018, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.0.0-dev.63.0
  • pana: 0.11.3
  • Flutter: 0.5.4

Scores

Popularity:
Describes how popular the package is relative to other packages. [more]
0 / 100
Health:
Code health derived from static analysis. [more]
99 / 100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100 / 100
Overall score:
Weighted score of the above. [more]
50
Learn more about scoring.

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Suggestions

  • Package is pre-v1 release.

    While there is nothing inherently wrong with versions of 0.*.*, it usually means that the author is still experimenting with the general direction API.

  • Fix analysis and formatting issues.

    Analysis or formatting checks reported 1 hint.

    Strong-mode analysis of lib/flutter_downloader.dart gave the following hint:

    line: 87 col: 35
    The exception variable 'e' isn't used, so the 'catch' clause can be removed.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=1.19.0 <2.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.6 1.14.10
meta 1.1.5
sky_engine 0.0.99
typed_data 1.1.5
vector_math 2.0.6 2.0.7
Dev dependencies
flutter_test