When a Flutter app that saves files with path_provider also targets web and desktop, what breaks or behaves differently, and how do you adapt?
answer
- no web implementation, no dart:io files
- desktop documents is the user's folder
- support under AppData or XDG data
- temporary is system-wide on Windows and Linux
- hide storage behind an interface
basics
~20 spath_provider has no web implementation and dart:io files do not exist in browsers, so web needs browser storage or a download prompt. On Windows and Linux, documents is the user's shared Documents folder and temporary is system-wide, so app data belongs in support.
solid answer
~40 sOn the web, `path_provider` registers no implementation, so its calls fail (typically with a `MissingPluginException`), and `dart:io`'s `File` is unavailable anyway; Flutter's own read-and-write-files recipe says it does not work on web. There I store offline data in browser storage through a package built for it, or hand the user a browser download. On desktop the directories change meaning: `getApplicationDocumentsDirectory()` returns the user's real Documents folder on Windows and Linux, so writing app data there clutters the user's files; `getApplicationSupportDirectory()` gives an app-specific folder under roaming AppData or the XDG data home; `getApplicationCacheDirectory()` is app-specific under local AppData or the XDG cache home; and `getTemporaryDirectory()` returns the system temp folder, shared with other programs. I hide these differences behind one storage interface with a web implementation chosen by conditional import.
code
dart · 11 lines// note_store.dart
import 'note_store_io.dart' if (dart.library.js_interop) 'note_store_web.dart'
as impl;
abstract interface class NoteStore {
Future<void> save(String noteId, List<int> bytes);
Future<List<int>?> open(String noteId);
Future<void> delete(String noteId);
}
NoteStore createNoteStore() => impl.createNoteStore();go deeper
Recall that path_provider and dart:io files do not work on Flutter web.
Explain how documents, support, cache and temporary map on Windows, Linux and macOS, and why desktop documents is a shared folder.
Design a storage interface with a file implementation and a web implementation selected by conditional import, with per-platform directory choices.
Decide which features must work offline on the web at all, given eviction and origin-scoped storage, versus mobile and desktop.
## One codebase, three storage models Flutter builds the same Dart code for mobile, desktop and the web, but **file storage is not portable**. The `path_provider` plugin covers Android, iOS, macOS, Windows and Linux, and each platform maps its directories differently. The web has no file system that Dart can write to at all. For the lecture-notes app, "save this PDF for offline reading" therefore needs a different implementation per platform family. ## The web: no path_provider, no dart:io files - `path_provider`'s pubspec declares implementations for Android, iOS, Linux, macOS and Windows only. On the web, a call falls through to the default method-channel implementation, finds no handler, and fails, typically with `MissingPluginException`. - `dart:io`, which provides `File` and `Directory`, is not supported when compiling for the web, so even a hard-coded path could not be written. - Flutter's own documentation recipe for reading and writing files says it does not work with web apps. Workable web strategies: 1. Keep downloaded bytes in **browser storage** through a package that wraps it; several persistence packages, including Drift's web backend, store data in IndexedDB or the browser's origin-private file system. 2. Let the browser handle files: trigger a **download** so the PDF lands in the user's Downloads folder, or open it in a new tab. 3. Rely on HTTP caching or a service worker for re-fetchable content. Browser storage can be evicted under storage pressure and is scoped to the origin, so treat it like a cache unless the platform grants persistence. ## Desktop: same functions, different meanings | Function | Windows | Linux | macOS | |---|---|---|---| | `getApplicationDocumentsDirectory()` | the user's Documents known folder | XDG `DOCUMENTS` user directory | the user's Documents (or the sandbox container) | | `getApplicationSupportDirectory()` | roaming AppData, app subfolder | XDG data home, app subfolder | Application Support plus bundle ID | | `getApplicationCacheDirectory()` | local AppData, app subfolder | XDG cache home, app subfolder | Caches plus bundle ID | | `getTemporaryDirectory()` | system temp path (`GetTempPath`) | `TMPDIR` or `/tmp` | Caches plus bundle ID | The consequences: - On Windows and Linux, the **documents** function returns the user's own Documents folder, shared with every other program. Writing a database or a folder of cached PDFs there looks like clutter to the user. App-owned data belongs in **support**. - The **temporary** directory on Windows and Linux is system-wide, not app-scoped, even though the API documentation describes it as scoped to the app. Create a uniquely named subfolder for your files and clean it up. - On macOS, the plugin appends the bundle identifier to Application Support and Caches so non-sandboxed apps do not collide. ## Structuring the code - Define a small interface such as `NoteStore` with `save`, `open`, `delete` and `usage`. - Implement it with `path_provider` and `dart:io` for mobile and desktop, and with browser storage for the web, selecting the web version with a **conditional import** so `dart:io` never reaches the web build. - Choose directories per platform family inside the file-based implementation: support for app data everywhere, documents only for files the user should see. - Test each implementation against the same contract, and fake the interface in widget tests. ## Testing across platforms - Run the storage contract tests against the file implementation on each desktop platform in CI, with a fake `PathProviderPlatform` pointing at a temporary folder so tests never touch the real Documents directory. - Run the web implementation in a browser test target, since `flutter test` on the Dart VM cannot load browser storage APIs. - Check the desktop paths manually once: open the resolved support and cache folders on Windows and Linux and confirm the app subfolder naming matches what support staff will tell users. ## What interviewers want to hear A candidate who says "path_provider works everywhere" misses both the web and the desktop semantics. A strong answer names the missing web implementation, the user-visible desktop Documents folder, the shared desktop temp directory, and an abstraction that keeps platform choices in one place.
- Why does a Flutter Windows app that stores its database under getApplicationDocumentsDirectory() draw user complaints?On Windows that function returns the user's own Documents known folder, the same one their files live in, so the app's database and cache files appear among personal documents and can be moved or deleted by the user. App-owned data belongs in `getApplicationSupportDirectory()`, which resolves to an app subfolder of roaming AppData.
- Why is the conditional import needed rather than just checking kIsWeb before calling path_provider?A `kIsWeb` check only skips the call at runtime; the web build still has to compile every imported library. Importing `dart:io`-based code into a web build is the problem, so a conditional import swaps in a web implementation at compile time and keeps `dart:io` out entirely.
saying these in an interview costs you the question
- path_provider works on the web through the browser's file system.
- getApplicationDocumentsDirectory() is app-private on Windows and Linux.
- getTemporaryDirectory() on Windows is scoped to the calling app.
- A kIsWeb check alone keeps dart:io code out of the web build.
- Browser storage is as durable as an app's support directory.