11import 'dart:async' ;
2+ import 'dart:convert' ;
23import 'dart:io' ;
34import 'dart:ui' ;
45
56import 'package:flutter/services.dart' ;
67import 'package:flutter/widgets.dart' ;
8+ import 'package:flutter_downloader/src/exceptions.dart' ;
79
810import 'callback_dispatcher.dart' ;
911import 'models.dart' ;
@@ -21,9 +23,14 @@ class FlutterDownloader {
2123 static const _channel = MethodChannel ('vn.hunghd/downloader' );
2224
2325 static bool _initialized = false ;
26+
27+ /// Whether the plugin is initialized. The plugin must be initialized before
28+ /// use.
2429 static bool get initialized => _initialized;
2530
2631 static bool _debug = false ;
32+
33+ /// If true, more logs are printed.
2734 static bool get debug => _debug;
2835
2936 /// Initializes the plugin. This must be called before any other method.
@@ -47,10 +54,10 @@ class FlutterDownloader {
4754 WidgetsFlutterBinding .ensureInitialized ();
4855
4956 final callback = PluginUtilities .getCallbackHandle (callbackDispatcher)! ;
50- await _channel.invokeMethod ('initialize' , < dynamic > [
57+ await _channel.invokeMethod < void > ('initialize' , < dynamic > [
5158 callback.toRawHandle (),
52- debug ? 1 : 0 ,
53- ignoreSsl ? 1 : 0 ,
59+ if ( debug) 1 else 0 ,
60+ if ( ignoreSsl) 1 else 0 ,
5461 ]);
5562
5663 _initialized = true ;
@@ -87,62 +94,78 @@ class FlutterDownloader {
8794 required String url,
8895 required String savedDir,
8996 String ? fileName,
90- Map <String , String >? headers,
97+ Map <String , String > headers = const {} ,
9198 bool showNotification = true ,
9299 bool openFileFromNotification = true ,
93100 bool requiresStorageNotLow = true ,
94101 bool saveInPublicStorage = false ,
95102 }) async {
96103 assert (_initialized, 'plugin flutter_downloader is not initialized' );
97- assert (Directory (savedDir).existsSync (), "savedDir does not exist" );
98-
99- StringBuffer headerBuilder = StringBuffer ();
100- if (headers != null ) {
101- headerBuilder.write ('{' );
102- headerBuilder.writeAll (
103- headers.entries.map ((entry) => '"${entry .key }": "${entry .value }"' ),
104- ',' ,
105- );
106- headerBuilder.write ('}' );
107- }
104+ assert (Directory (savedDir).existsSync (), 'savedDir does not exist' );
105+
108106 try {
109- String ? taskId = await _channel.invokeMethod ('enqueue' , {
107+ final taskId = await _channel.invokeMethod < String > ('enqueue' , {
110108 'url' : url,
111109 'saved_dir' : savedDir,
112110 'file_name' : fileName,
113- 'headers' : headerBuilder. toString ( ),
111+ 'headers' : jsonEncode (headers ),
114112 'show_notification' : showNotification,
115113 'open_file_from_notification' : openFileFromNotification,
116114 'requires_storage_not_low' : requiresStorageNotLow,
117115 'save_in_public_storage' : saveInPublicStorage,
118116 });
117+
118+ if (taskId == null ) {
119+ throw const FlutterDownloaderException (
120+ message: '`enqueue` returned null taskId' ,
121+ );
122+ }
123+
119124 return taskId;
120- } on PlatformException catch (e) {
121- _log ('Download task is failed with reason(${e .message })' );
122- return null ;
125+ } on FlutterDownloaderException catch (err) {
126+ _log ('Failed to enqueue. Reason: ${err .message }' );
127+ } on PlatformException catch (err) {
128+ _log ('Failed to enqueue. Reason: ${err .message }' );
123129 }
130+
131+ return null ;
124132 }
125133
126134 /// Loads all tasks from SQLite database.
127135 static Future <List <DownloadTask >?> loadTasks () async {
128136 assert (_initialized, 'plugin flutter_downloader is not initialized' );
129137
130138 try {
131- List <dynamic > result = await _channel.invokeMethod ('loadTasks' );
132- return result
133- .map ((item) => DownloadTask (
134- taskId: item['task_id' ],
135- status: DownloadTaskStatus (item['status' ]),
136- progress: item['progress' ],
137- url: item['url' ],
138- filename: item['file_name' ],
139- savedDir: item['saved_dir' ],
140- timeCreated: item['time_created' ]))
141- .toList ();
142- } on PlatformException catch (e) {
143- _log (e.message);
139+ final result = await _channel.invokeMethod <List <dynamic >>('loadTasks' );
140+
141+ if (result == null ) {
142+ throw const FlutterDownloaderException (
143+ message: '`loadTasks` returned null' ,
144+ );
145+ }
146+
147+ return result.map (
148+ (dynamic item) {
149+ // item as Map<String, dynamic>; // throws
150+
151+ return DownloadTask (
152+ taskId: item['task_id' ] as String ,
153+ status: DownloadTaskStatus (item['status' ] as int ),
154+ progress: item['progress' ] as int ,
155+ url: item['url' ] as String ,
156+ filename: item['file_name' ] as String ? ,
157+ savedDir: item['saved_dir' ] as String ,
158+ timeCreated: item['time_created' ] as int ,
159+ );
160+ },
161+ ).toList ();
162+ } on FlutterDownloaderException catch (err) {
163+ _log ('Failed to load tasks. Reason: ${err .message }' );
164+ } on PlatformException catch (err) {
165+ _log (err.message);
144166 return null ;
145167 }
168+ return null ;
146169 }
147170
148171 /// Loads tasks from SQLite database using raw [query] .
@@ -167,20 +190,34 @@ class FlutterDownloader {
167190 assert (_initialized, 'plugin flutter_downloader is not initialized' );
168191
169192 try {
170- List <dynamic > result = await _channel
171- .invokeMethod ('loadTasksWithRawQuery' , {'query' : query});
172- return result
173- .map ((item) => DownloadTask (
174- taskId: item['task_id' ],
175- status: DownloadTaskStatus (item['status' ]),
176- progress: item['progress' ],
177- url: item['url' ],
178- filename: item['file_name' ],
179- savedDir: item['saved_dir' ],
180- timeCreated: item['time_created' ]))
181- .toList ();
182- } on PlatformException catch (e) {
183- _log (e.message);
193+ final result = await _channel.invokeMethod <List <dynamic >>(
194+ 'loadTasksWithRawQuery' ,
195+ {'query' : query},
196+ );
197+
198+ if (result == null ) {
199+ throw const FlutterDownloaderException (
200+ message: '`loadTasksWithRawQuery` returned null' ,
201+ );
202+ }
203+
204+ return result.map (
205+ (dynamic item) {
206+ // item as Map<String, dynamic>; // throws
207+
208+ return DownloadTask (
209+ taskId: item['task_id' ] as String ,
210+ status: DownloadTaskStatus (item['status' ] as int ),
211+ progress: item['progress' ] as int ,
212+ url: item['url' ] as String ,
213+ filename: item['file_name' ] as String ? ,
214+ savedDir: item['saved_dir' ] as String ,
215+ timeCreated: item['time_created' ] as int ,
216+ );
217+ },
218+ ).toList ();
219+ } on PlatformException catch (err) {
220+ _log ('Failed to loadTasksWithRawQuery. Reason: ${err .message }' );
184221 return null ;
185222 }
186223 }
@@ -190,9 +227,9 @@ class FlutterDownloader {
190227 assert (_initialized, 'plugin flutter_downloader is not initialized' );
191228
192229 try {
193- return await _channel.invokeMethod ('cancel' , {'task_id' : taskId});
194- } on PlatformException catch (e ) {
195- _log (e .message);
230+ await _channel.invokeMethod < void > ('cancel' , {'task_id' : taskId});
231+ } on PlatformException catch (err ) {
232+ _log (err .message);
196233 }
197234 }
198235
@@ -202,8 +239,8 @@ class FlutterDownloader {
202239
203240 try {
204241 return await _channel.invokeMethod ('cancelAll' );
205- } on PlatformException catch (e ) {
206- _log (e .message);
242+ } on PlatformException catch (err ) {
243+ _log (err .message);
207244 }
208245 }
209246
@@ -292,23 +329,32 @@ class FlutterDownloader {
292329 }
293330 }
294331
295- /// Opens the downloaded file with [taskId] . Returns true if the downloaded
296- /// file can be opened, false otherwise.
332+ /// Opens the file downloaded by download task with [taskId] . Returns true if
333+ /// the downloaded file can be opened, false otherwise.
297334 ///
298335 /// On Android, there are two requirements for opening the file:
299336 /// - The file must be saved in external storage where other applications have
300337 /// permission to read the file
301- /// - There is at least 1 application that can read the files of type of the
302- /// file.
338+ /// - There must be at least 1 application that can read the files of type of
339+ /// the file.
303340 static Future <bool > open ({required String taskId}) async {
304341 assert (_initialized, 'plugin flutter_downloader is not initialized' );
305342
306343 try {
307- return await _channel.invokeMethod ('open' , {'task_id' : taskId});
308- } on PlatformException catch (e) {
309- _log (e.message);
344+ final result = await _channel.invokeMethod <bool >(
345+ 'open' ,
346+ {'task_id' : taskId},
347+ );
348+
349+ if (result == null ) {
350+ throw const FlutterDownloaderException (message: '`open` returned null' );
351+ }
352+ } on PlatformException catch (err) {
353+ _log ('Failed to open downloaded file. Reason: ${err .message }' );
310354 return false ;
311355 }
356+
357+ return false ;
312358 }
313359
314360 /// Registers a [callback] to track the status and progress of a download
@@ -354,7 +400,10 @@ class FlutterDownloader {
354400 /// send.send([id, status, progress]);
355401 ///}
356402 ///```
357- static registerCallback (DownloadCallback callback, {int step = 10 }) {
403+ static Future <void > registerCallback (
404+ DownloadCallback callback, {
405+ int step = 10 ,
406+ }) async {
358407 assert (_initialized, 'plugin flutter_downloader is not initialized' );
359408
360409 final callbackHandle = PluginUtilities .getCallbackHandle (callback);
@@ -368,7 +417,7 @@ class FlutterDownloader {
368417 'step size is not in the inclusive <0;100> range' ,
369418 );
370419
371- _channel.invokeMethod (
420+ await _channel.invokeMethod < void > (
372421 'registerCallback' ,
373422 < dynamic > [callbackHandle! .toRawHandle (), step],
374423 );
0 commit comments