【Flutter】任意の場所にファイルをダウンロードする

# はじめに
flutterでpdfファイルを端末内の任意の場所に保存するための方法を紹介します。
androidおよびiOSで動作することを確認しました。
この記事で紹介する内容を応用すればmp3,mp4,txt・・・など複数のファイル形式のダウンロードも可能になるかと思います。

# 結論
[flutter_file_dialog](https://pub.dev/packages/flutter_file_dialog)を利用する

```dart:
  final params = SaveFileDialogParams(
    data: data.bodyBytes,
    fileName: '$fileName.pdf',
  );
  final savedFiledPath = await FlutterFileDialog.saveFile(params: params);
```

# 実装
実際に実装してみました。今回はpdfのURLからそのファイルをダウンロードするというものを実装しています。


![画面収録 2023-12-23 20.43.28.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2230776/b2393884-d28c-c642-a8d9-dea2513fe1de.gif)

## パッケージのインストール
`flutter_file_dialog`, `http`, `flutter_hooks`の３つを使用しました。
```
flutter pub add flutter_file_dialog
flutter pub add http
flutter pub add flutter_hooks
```
## UIの作成
テキストフィールド２つと送信ボタンだけのシンプルなものを作成しました。この記事の要旨ではないので↓のトグルに入れておきます。

<details><summary>main.dart(UI)のコード</summary>


```dart:main.dart
import 'package:download_to_optional_folder/save_file.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Pdf download Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'File Dialog Demo'),
    );
  }
}

class MyHomePage extends HookWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  Widget build(BuildContext context) {
    final urlController = useTextEditingController();
    final fileNameController = useTextEditingController(); // Add this line

    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(
              controller: urlController,
              decoration: const InputDecoration(
                labelText: 'Enter URL',
              ),
            ),
            TextField(
              // Add this block
              controller: fileNameController,
              decoration: const InputDecoration(
                labelText: 'Enter File Name',
              ),
            ),
            ElevatedButton(
              onPressed: () async {
                final url = urlController.text;
                final fileName = fileNameController.text;
                final isSuccessful = await savePdfFile(url, fileName);
                if (!isSuccessful && context.mounted) {
                  showDialog(
                    context: context,
                    builder: (BuildContext context) {
                      return AlertDialog(
                        title: const Text('Failed to Download'),
                        content: const Text('Failed to download the file.'),
                        actions: [
                          TextButton(
                            onPressed: () {
                              Navigator.of(context).pop();
                            },
                            child: const Text('OK'),
                          ),
                        ],
                      );
                    },
                  );
                }
              },
              child: const Text('Submit'),
            ),
          ],
        ),
      ),
    );
  }
}
```


</details>

## ダウンロードを行う関数の作成
以下のような関数で実装しています。
処理の流れとしては以下の通りです。
- httpパッケージでpdfファイルを取得
- flutter_file_dialogで保存。
- データの取得・保存に失敗すると例外を投げるようにする
- 正常にダウンロードできればtrueが帰る



```dart: save_pdf_file.dart
import 'package:flutter/foundation.dart';
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
import 'package:http/http.dart' as http;

Future<bool> savePdfFile(String url, String fileName) async {
  final data = await http.get(Uri.parse(url));
  try {
    if (data.statusCode == 200) {
      final params = SaveFileDialogParams(
        data: data.bodyBytes,
        fileName: '$fileName.pdf',
      );
      final savedFiledPath = await FlutterFileDialog.saveFile(params: params);
      if (savedFiledPath == null) {
        throw Exception('Failed to download pdf');
      }
    } else {
      throw Exception("Error while downloading file");
    }
  } catch (e) {
    if (kDebugMode) {
      print(e);
    }
    return false;
  }
  return true;
}
```

#最後に
参考になれば幸いです😀
もっと良い実装方法や補足等ありましたらコメント・もしくは編集リクエスト等で **優しく** 指摘してください🥺