# Download

Offer a stored file to the user with `Storage.download`: the share sheet on mobile, macOS and Windows, the save dialog on Linux, a browser download on the web.

- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [How the Handler Works](#how-the-handler-works)
- [Without the Plugin](#without-the-plugin)
- [Gotchas](#gotchas)
- [Related Documentation](#related-documentation)

## Basic Usage

```dart
import 'package:magic/magic.dart';

Future<void> main() async {
  await Storage.download('reports/2024.pdf', name: 'Annual Report.pdf');
}
```

`Storage` is core's facade. `magic_picker` supplies the part core cannot ship, the share sheet, by registering `PickerServiceProvider`. Register it once in `lib/config/app.dart` (the installer does) and the call above works on every platform.

## Quick Reference

| Platform | What `Storage.download` does |
|:---------|:-----------------------------|
| Android, iOS, macOS and Windows | Calls `LocalAdapterContract.downloadHandler`, which `PickerServiceProvider` sets to open the share sheet. |
| Linux | The same handler opens the save dialog with the file's bytes, because `share_plus` cannot share a file there. Closing the dialog ends the call without an error. |
| Web | Triggers a browser download. The handler is never called. |

The `name` argument is the file name offered to the user. Without it the file's own base name is used.

## How the Handler Works

`LocalAdapterContract.downloadHandler` is a static `Future<void> Function(String fullPath, String name)?` in core. `PickerServiceProvider.boot()` assigns it, so the share sheet is wired after `Magic.init` has booted the providers. The handler shares the stored file through `share_plus` and passes `name` as the file name override, because `XFile` ignores its `name` on IO and would otherwise offer the path's base name. On Linux `share_plus` throws `UnimplementedError` for any file, so the handler reads the file and passes `name` and the bytes to `Pick.saveFile`, which opens the save dialog.

On Windows older than 10 version 1809 (build 17763), `share_plus` falls back to an implementation that throws `UnimplementedError` for any file, so `download` throws there. The provider does not detect it.

`PickerServiceProvider` takes an optional `share` callback that replaces the share sheet. It exists for tests, where there is no platform channel to answer; see [Testing](https://anilcancakir.com/magic/docs/picker/guides/testing.md).

## Without the Plugin

On mobile and desktop `download` throws `UnsupportedError` while no handler is set. The message names `magic_picker`:

```text
Storage download on IO platforms needs a handler. Install magic_picker or set LocalAdapterContract.downloadHandler.
```

Install the plugin, or assign your own handler, for example one that copies the file to a folder you choose. A file that does not exist makes `download` throw an `Exception` before the handler is reached.

## Gotchas

| Mistake | Fix |
|:--------|:----|
| Calling `Storage.download` on mobile or desktop without the plugin | Register `PickerServiceProvider`, or set `LocalAdapterContract.downloadHandler` yourself. |
| Expecting a share sheet on Linux | There is none: the user gets a save dialog and picks where the file goes. |
| Expecting the handler to run on the web | The web adapter downloads through the browser and ignores it. |
| Assigning your own handler before `Magic.init` finishes | `PickerServiceProvider.boot()` runs during init and overwrites it. Assign yours after boot. |
| Downloading a path that was never stored | `download` throws an `Exception` saying the file was not found. |

## Related Documentation

- [Pick](https://anilcancakir.com/magic/docs/picker/basics/pick.md): picking the file that you later store.
- [XFile Conversion](https://anilcancakir.com/magic/docs/picker/basics/xfile.md): the `XFile` the share sheet receives.
- [Testing](https://anilcancakir.com/magic/docs/picker/guides/testing.md): recording share calls and resetting the handler.
