Flutter で Instagram から映えてる写真を取得してみた

[株式会社ACCESS Advent Calendar 2021](https://qiita.com/advent-calendar/2021/access)　21日目の記事は Flutter で Instagram から映えてる写真を取得してみたという内容になります。

# 作ったもの

作ったのものは至ってシンプル。
単語検索（ハッシュタグ検索）をかけて、キャプション（写真の説明）に検索する単語が入っている写真を取得し表示しています。
![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96450/7169fa28-5cb3-177a-6432-1c952ff0babc.gif)



# アーキテクチャ

作りました！おわり！だけでは物足りないのでコードについて書いていきます。
アーキテクチャは　MVVM + Repository で作りました。
このアーキテクチャを採用した理由は、自分がこのアーキテクチャで作ってみたかったためです。
Flutter では MVVM で作るのが流行りと聞いたので流行りに乗るためにこちらを採用しました。
自己解釈も含めてアプリの概要を図で載せておきます。

![ブログ記事 (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96450/6134843c-17d6-7757-c48c-fbd6aa97fbef.jpeg)


全体コードは　Github にあげています。

https://github.com/Momijinn/my_flutter_instagram

## library

使用した主要ライブラリは以下のものです。

状態管理は [riverpod](https://pub.dev/packages/riverpod) で行い、model等の生成は [freezed](https://pub.dev/packages/freezed) を使いました。
api のリクエストには [http](https://pub.dev/packages/http) を使いました。
※ flutter_hooks も入れましたが、今回出番はなかったです。

```
dependencies:
  state_notifier: ^0.7.1
  freezed_annotation: ^1.1.0
  http: ^0.13.4
  hooks_riverpod: ^1.0.3
  flutter_hooks: ^0.18.1

dev_dependencies:
  flutter_lints: ^1.0.0
  build_runner: ^2.1.5
  freezed: ^1.1.0
  json_serializable: ^6.1.3
```

## View

シンプルに `TextField`と`Card`を使ったものです。

![Screenshot_1640006420.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96450/130890dd-b36b-4a61-562d-bfbd161c44e0.png)

https://github.com/Momijinn/my_flutter_instagram/blob/main/lib/view/home/home_page.dart

## ViewModel

view で api を呼べるようにしています。

```dart
import 'package:my_instagram_flutter/api/app_api.dart';

class AppRepository {
  final _instagramAPI = InstagramApi();

  dynamic fetchInstagram(String text) async {
    return await _instagramAPI.fetchImage(text);
  }
}
```

https://github.com/Momijinn/my_flutter_instagram/blob/main/lib/repository/app_repository.dart

## Repository

Instagram api をリクエストするようにしています。

```
class AppRepository {
  final _instagramAPI = InstagramApi();

  dynamic fetchInstagram(String text) async {
    return await _instagramAPI.fetchImage(text);
  }
}
```

https://github.com/Momijinn/my_flutter_instagram/blob/main/lib/repository/app_repository.dart

## Remote(API Request)

api request の呼び出しについては長くなるので割愛します。
詳細は私が書いているブログを参考にしてください。
https://www.autumn-color.com/archives/2685

検索する単語で、いいねが多い数 + 上位20個の投稿を取得してきています。

```dart
class InstagramApi {
  dynamic _fetchHashTagId(String noum) async {
    var url = Uri.https(AppCommon.apiEndpoint, '/v12.0/ig_hashtag_search', {
      'user_id': AppCommon.userId,
      'access_token': AppCommon.accessToken,
      'q': noum
    });

    var response = await http.get(url);

    if (response.statusCode == 200) {
      final body = jsonDecode(response.body);

      return body['data'][0]['id'] as String;
    }
    return '';
  }

  dynamic fetchImage(String text) async {
    var hashTagId = await _fetchHashTagId(text);
    if (hashTagId == '') return [];

    var url = Uri.https(AppCommon.apiEndpoint, '/v12.0/$hashTagId/top_media', {
      'user_id': AppCommon.userId,
      'access_token': AppCommon.accessToken,
      'fields': 'media_url',
      'limit': '20',
    });

    var response = await http.get(url);

    if (response.statusCode == 200) {
      final res = InstagramModel.fromJson(json.decode(response.body));

      return res.data;
    }

    return [];
  }
}
```

https://github.com/Momijinn/my_flutter_instagram/blob/main/lib/api/app_api.dart

# 残件

駆け足で作ったので実装漏れがかなりあります。
ぱっと思い浮かぶものを並べます。

* unit test を書いてない。
* エラーハンドリングがズタボロ。
* state の型がテキトーすぎる。とりあえず dynamic で型定義しているのは使いづらい。
* api のレスポンスが返ってくるまでのローディング画面がない。

時間があるときに修正したいです。

# まとめ

Flutter で Instagram API を使って映える写真を検索し表示するアプリを作ってみました。
とりあえずは動くものができてよかったです。

22日の担当は @irisTa56 さんです。
投稿楽しみにしています。

それではよいクリスマス🎅&よいお年🎍を！

# 参考

* https://zenn.dev/yumiba/articles/10db684c90069d
* https://github.com/wasabeef/flutter-architecture-blueprints