WebAPIと連携してフリー画像検索アプリ制作チュートリアルを学びました。の続編。備忘録です

下記動画を参考に作りました。
【Flutterの教科書】⑤ WebAPIと連携してフリー画像検索アプリを作ろう【後編】
https://youtu.be/S8AEyv2cKL8?si=1jb3v9J8OOqwgGFU

いつものように目次に沿ってメモを残します。

## 00:00 検索する画像を変更する方法
検索可能にするには、テキストを入力してそれをこの画像を表示させるってところ実装していかなければなりません
入力フィールドを作る前に、この画像一覧をどうやったら変更できるのかやっていきます

## 01:40 クエリパラメーターの解説
まずはgetしているURLに着目する
眺めていると、yellow+flowersと書いてあるところがある。
```dart
Response response = await Di
o().get('https://pixabay.com/api/?key=42370728-d42399baa5e5ae716911d91c4&q=yellow+flowers&image_type=photo&pretty=true';
```
どうやらこれが怪しいなと。ここをちゃんと変えたら画像が変わるのか。を確認
直に該当箇所を書き換えたら、ちゃんと変更された。日本語文字列も対応してくれるようだ
こういう情報は、観察と仮説を立てて実験してもいいですけど、大抵は公式のドキュメントに書いてある
そのうちのParametersという項目がある。ここでどういうふうにパラメータを与えていけいいのかが書いている

URLは？の後ろからパラメータが複数書かれている。～＝～で１つのパラメータ、＆で区切られている
試しにパラメータを追加します。デフォルトだと２０件しか取れないので、もっと取れるようにします
件数のパラメータは公式だとper_pageとあったのでここで１００件表示できるように追加します
クエリパラメータはこの様になっていました

## 02:27 TextFormFieldの追加
次はURL中のクエリパラメータの該当箇所を変更できるよう実装していきます
まずは入力フィールドを作らなきゃいけないので作っていきます
入力フィールドはどこでもいいですが、今回はappberに作ります
TextFormFieldというのを追加すると入力可能なフィールドが追加される
```dart
      appBar: AppBar(
        title: TextFormField(),
      ),
```
アプリ上で文字を入力しようとする日本語が打てない
その場合は、そのエミュレーター上で言語の設定を変えればいい（※言語を追加することになるが、日本語の順番を一番上にすること）
入力フィールドが見づらいので、装飾する
```dart
        title: TextFormField(
          decoration: InputDecoration(
            fillColor: Colors.white,//入力項目の背景色を白にする
            filled: true,
          ),
```
ここに入力した値をどうやって取得するかというとonFieldSubmittedを使っていきます
```dart
          onFieldSubmitted: (text){
```
このtextという変数の中には、アプリ上の入力フォームに入力している文字という値が入ってきます
これをプリントしてみましょう。そうするとアプリで入力して決定→コンソール上に入力した文字が返される
なので、アプリ上で決定したときに実行されるので、そのタイミングでfetchimagesを呼び出せば良いということになります

## 04:52 onFieldSubmittedで関数を実行
今引数を与えられないようになっちゃっていますのでまずは引数を与えられるようにしましょう

変更前
```dart
  Future<void> fetchimages() async {
```

変更後
```dart
Future<void> fetchimages(String text) async {
```
型はStringなのでStringと、変数名はなんでもいいですけど、textとしました
このtextを、このURLに組み込めばいい。組み込むところはさきほどの該当箇所を変える
ここでtextを評価したいんですけど
ここに単純にtextと書くと、値は単純な文字列として認識されてしまうので
これを回避するために、＄マークというのを追加すればOKです。URLに「&q=$text」てな感じ
このtextは変数と解釈され、この変数の中身が展開されてこの文字列に合体して評価されます

これで引数を与えられるようにしたので
現状だとfetchimages();にエラーが出ます
これ消してもいいけど、初回に何かを表示させたい場合は、初夏に表示させたいキーワードを入力しておくことができます
変更後例：fetchimages('車');

ユーザーにわかりやすくするために、initialValueというものがあって、ここに文字列を入れておくと、ユーザーに読ませるこtができる（入力フォームのところ）
onFieldSubmittedが実行されるときにですね、fetchimagesもあわせて実行しましょうということです
```dart
        title: TextFormField(
          initialValue: 'ここに検索したいワードを入力して下さい',
```
```dart
          onFieldSubmitted: (text) {
            print(text);//これは今後いらないから削除する
            fetchimages(text);
          },
```
これで、好きなキーワードを入力して好きな画像を検索できるところまできました

## 06:43 Stackを使ったいいね数の表示
次は見た目の方を触っていきます。
まず、hitのデータの中には「いいね」数というのも入っている
そのいいね数を表示させるをやります。
各画像の上に重ねるように表示させたいので、stackというのを使います。スタックの候補は出てこないので
colunuかrowで該当箇所を囲みます。ctrl + . で対象を囲む
```dart
return Image.network(hit['previewURL']);
 ```
を、下記のように囲む
```dart
          return Stack(
            children: [
              Image.network(hit['previewURL']),
              //いいね数。keyの名前がlikes
              //これを文字列として評価したい場合は上記のように${}で囲む。
              Text('${hit['likes']}'),
            ],
          );
```
## 07:48 Containerを使って背景色を追加
左上に数字が表示されました。ただ見えづらいので背景色をつけていきます。色々方法はありますが
今回はconeainerを使います。これにはcolorというプロパティがあるのでこれを使います
```dart
Container(color: Colors.white, child: Text('${hit['likes']}')),
 ```
 
## 08:05 見た目の修正 StackFit, BoxFit
現状縦横比がバラバラなので、正方形にきれいにします。Stackのすぐ下に下記のものを入れます
```dart
          return Stack(
            fit: StackFit.expand,
 ```
これだとコンテナーごとfitされておかしくなるので、もろもろ調整します。
```dart
          return Stack(
            fit: StackFit.expand,
            children: [
              Image.network(
                hit['previewURL'],
                fit: BoxFit.cover, //画像の比率をcoverで表示できる
              ),
              //いいね数。keyの名前がlikes
              //これを文字列として評価したい場合は上記のように${}で囲む。
              Align(
                  //整列
                  alignment: Alignment
                      .bottomRight, //文字を右下に移動。bottomRightは変えられるので位置は好きなところにおける
                  child: Container(
                    color: Colors.white,
                    child: Row(
                      //横並び
                      mainAxisSize: MainAxisSize.min, //これを与えると横幅いっぱいがちっちゃくできる
                      children: [
                        const Icon(
                          Icons.thumb_up_alt_outlined, //アイコンの追加
                          size: 14, //アイコンのサイズ
                        ),
                        Text('${hit['likes']}'),
                      ],
                    ),
                  )),
            ],
          );
```
 調整大変…
 
## 10:40 InkWellを使って画像をタップ可能にする
次はシェア機能の追加です。タップできるようにする。
タップするにも色々な方法がありますが、今回はInkWellというWidgetを使っていきます
StackをWidgetで囲って（ラップ）InkWellに変更。そしてその中にontapというものを書くことによってタップ可能になる
とりあえずタップできるか確認するのにprint(hit['likes']);してみます
```dart
          return InkWell(
            onTap: () {
              print(hit['likes']);
            },
            child: Stack(
 ```

## 11:34 画像を共有する機能の実装
コンソールでライク数が表示されました。タップ可能になりました。
このタイミングで、画像を共有するように実装する。ただこれは少々手間。
手順としてまして
 ```
//１．URLから画像をダウンロード
//２．ダウンロードしたデータをファイルに保存
//３．Shareパッケージを呼び出して共有
 ```
パッケージを追加する方法は前にもやった通り、ターミナル上でflutter pub addする
今回はpath_providerが必要なのでこれを追加します。
```
flutter pub add path_provider
```
次はshare_plusこれがShare機能追加するパッケージになります
```
flutter pub add share_plus
```
追加されたか確認したい場合は、pubspec.yamlファイルを確認する
これに追加されていれば成功です。
追加したものが反映されないことがあるので、一度■ボタンで停止します。

これで準備が整ったので、上記の１，２，３をやっていきます。

URLから画像をダウンロードするときは、dioのパッケージをそのまま使っていきます。
dioに対して、get　ここまでは一緒
今回このpathのURLについてはこのwebFormatURLっていうのが比較的きれいな画像らしいのでこのURLを指定します
hitに対してこのwebFormatURLを指定する。この中にはURLが入っている
これではまだ完結しておらず、optinsというのを渡す必要があります。optionsに対してresponseTypeそしてbytesを指定することで
画像データを取得することができるようになります。
dioを受け取らないといけないのでレスポンスに代入する。
Futureなのでontapのところでasync、responseのところでawaitとかく
```dart
            onTap: () async {
              //１．URLから画像をダウンロード
              //２．ダウンロードしたデータをファイルに保存
              //３．Shareパッケージを呼び出して共有
              Response response = await Dio().get(
                hit['webFormatURL'],
                options: Options(responseType: ResponseType.bytes),
              );
            },
```
これで１はOKです
次は２．ディレクトリという型がありまして、これで保存一時的に保存可能な領域のフォルダーのパス情報を取得することができます
```dart
Directory dir = await getTemporaryDirectory();//これもFutureがついているのでawait
```
これで一時的に保存可能な領域のパスデータを取得することができます
あとは、
レスポンスに画像データがありますので、これを書き込んで上げることをします
今はパスを取得したとこまできたので、次は
fileという変数を作ってあげて
ディレクトリのパスがあるので、ディレクトリのパスに対してスラッシュで区切る
/image.png'のところのfile名は適当で良い
これで書き込みができます。これもawaitで待ってあげましょう
```dart
              File file = await File(dir.path + '/image.png').writeAsBytes(response.data);
```
この１行でimage.pngというfileに、さっきダウンロードしてきたbyteデータを書き込むという処理をやっています
書き込んだらこの上記の
 file  に、実際書き込まれたデータが入ってくる
 
 次に３．これはパッケージのちから様様でかんたんに書ける。ShareというのがあってSharefilesというのがあります
 Listの形にしたいので[]をつけたうえでfileのパスを指定するだけです。
 ```dart
 Share.shareFiles([file.path]);
```

 これでShareが可能になります。
保存したfileのパスを、shareFilesに与えた上げると、Shareが可能になる

## ビルドエラーに遭遇…
と思ったら、ビルドエラーに遭遇…
エラー内容を見ると、JDKの依存関係でビルドエラーが起きているよう、
初心者にはきついよと思いつつ検索したら、とても良い記事を見つけ解決することができました。
有難うございます！！

参考
【Android】JDKの依存関係でビルドエラーとなった場合の対処法
https://qiita.com/YuukiYoshida/items/37dd7cea0918fe8a03d6

## 17:25 このチャプターの振り返り　19:42 次回予告

>最後にこのチャプターで学んだトピックをおさらいします。

>ドキュメントを参考にクエリパラメータを変更することで欲しいデータが得られる
>TextFormField の見た目は InputDecoration で変更できる
>onFieldSubmitted で入力した文字列を取得できる
>Stack で Widget を重ねられる
>InkWell でラップすると onTap で選択時に呼び出す関数を指定できる
>path_provider で端末の保存可能なフォルダの位置を取得できる
>share_plus でダウンロードした画像を共有できる
>理解度はどうでしょうか。わからないトピックがあればもう一度読み返してみてください。

>次回のチャプターでは機能はそのままにコードの質を高める リファクタリング を行います。実は今まで書いてきたコードはあまり>よいものとは言えません。もっと読みやすく、安全で、使いやすいコードにするための修正過程を解説します！