Skip to content

Replace flutter_downloader with file_saver_ffi#104

Open
vanvixi wants to merge 1 commit intoNetShareOSS:masterfrom
vanvixi:features/implement-file_saver_ffi
Open

Replace flutter_downloader with file_saver_ffi#104
vanvixi wants to merge 1 commit intoNetShareOSS:masterfrom
vanvixi:features/implement-file_saver_ffi

Conversation

@vanvixi
Copy link

@vanvixi vanvixi commented Mar 5, 2026

Replace flutter_downloader with file_saver_ffi for cross-platform downloads

Motivation

flutter_downloader works well on Android/iOS, but its architecture adds significant complexity to the codebase:

  • Requires a background isolate with IsolateNameServer and ReceivePort just to receive download callbacks
  • Needs a @pragma("vm:entry-point") static callback and manual IsolateNameServer registration/cleanup in initState/dispose
  • Desktop downloads had to fall back to a separate http-based implementation with no progress tracking
  • Task state must be queried from an internal SQLite DB via loadTasksWithRawQuery
  • Contains a workaround for Flutter engine bug #119589

All of this makes ClientWidget hard to understand and maintain.

Solution

This PR replaces flutter_downloader with file_saver_ffi — a cross-platform file saving library that:

  • Runs on a native background thread (Swift on iOS/macOS, Kotlin on Android) — no UI blocking, no Dart isolate management needed
  • On Windows and Linux, uses pure Dart with chunked I/O so the main thread is never blocked
  • Saves files in chunks and reports progress on all platforms
  • Wraps everything behind a clean Stream<SaveProgress> API — callers need zero knowledge of threading or platform internals

Key improvements:

Before After
Platform support Android + iOS only (desktop: separate http impl) Android, iOS, macOS, Windows, Linux
Threading Manual Dart isolate + IsolateNameServer boilerplate Handled internally by the library (native threads / chunked Dart)
Progress tracking Not implemented Real-time via SaveProgressUpdate(double progress)
Lines removed ~100 lines of boilerplate deleted

Changes

  • ClientWidget: Removed ReceivePort, IsolateNameServer, _initDownloadModule(), downloadCallback() static method, and 3 unused imports. _downloadStreamListener() is unchanged.
  • DownloadService: Replaced two platform-branched download methods with a single startDownloading() using FileSaver.save(). MIME type detected automatically via the existing mime package.
  • DownloadEntity: Removed unused id and manner fields; added progress field and copyWith().
  • FileProvider: Added in-memory _progressMap + getProgress() for granular progress updates without polluting the Hive entity.
  • FileTileClient: Progress indicator now shows determinate progress (0-100%) using Selector<FileProvider, double> with shouldRebuild to avoid full-list rebuilds on every tick.
  • Removed download_manner.dart, uuid dependency, and android_path_provider.

Result

The entire download flow — including real-time progress — now works identically on all platforms with significantly less code. Threading and chunk management are handled by file_saver_ffi internally; the app just listens to a stream.


Note: file_saver_ffi is published on pub.dev (^0.8.1). No additional native setup is required beyond what Flutter already provides.

Copy link
Member

@huynguyennovem huynguyennovem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awsome. Thank you for doing this! Overall looks good to me, except two issues:

  1. ios and macos file changes don't relate to this work. Could you please revert them and re-commit the change?
  2. After downloading a file, it is no longer possible to open it (please watch the video). I printed the output path and saw that it is something like content://media/external_primary/file/1000000232 now, which seems to be exposed from the file_saver_ffi package, doesn't it? Previously, a file was downloaded and stored to AndroidPathProvider.downloadsPath. I also see it's AndroidSaveLocation.downloads on Android by default in package file_saver_ffi. Not sure if these two paths are the same. Could you please check this?
Screen.Recording.2026-03-07.at.17.01.34.mov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants