Flutter入門 QiitaのAPIを叩いて記事一覧を表示してみる

Flutterに入門したのですが、まだまだ日本語の記事が少ない印象です。
とりあえず個人的チュートリアルであるQiitaのAPIの叩いて表示する奴をやってみたいと思います。

ちなみに私は[Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)を程度です。
Dartの文法からわかりません。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166241/54fa0dcb-4557-583b-bb94-86caaf21248a.png" width="330"/>

### API

#### Entity

``` .dart
class User {
  final String id;
  final String iconUrl;

  User({this.id, this.iconUrl});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'],
      iconUrl: json['profile_image_url'],
    );
  }
}
```


``` .dart
class Article {
  final String title;
  final String url;
  final User user;

  Article({this.title, this.url, this.user});

  factory Article.fromJson(Map<String, dynamic> json) {
    return Article(
      title: json['title'],
      url: json['url'],
      user: User.fromJson(json['user']),
    );
  }
}
```

- DartのClass
　https://dart.dev/guides/language/language-tour#classes 
- Factoryコンストラクタ
- ネストしたJSONのパース
 https://medium.com/flutter-community/parsing-complex-json-in-flutter-747c46655f51

### Request

``` .dart
class QiitaClient {
  static Future<List<Article>> fetchArticle() async {
    final url = 'https://qiita.com/api/v2/items';
    final response = await http.get(url);
    if (response.statusCode == 200) {
      final List<dynamic> jsonArray = json.decode(response.body);
      return jsonArray.map((json) => Article.fromJson(json)).toList();
    } else {
      throw Exception('Failed to load article');
    }
  }
}
```

https://flutter.dev/docs/cookbook/networking/fetch-data

ドキュメントのExampleとほぼ同じ感じです。
ただAPIがJSONの配列を返してくるので、その辺がまだ自信ない。
とりあえず動いたレベル。


main関数に適当に書いてリクエストしてみます。

``` .dart
void main() {
    QiitaClient.fetchArticle().then((articles) => {
        articles.forEach((article) => {
            print(article.title)
        })
    })
}
```

VSCodeのDEBUG CONSOLEを貼っておく。

```
flutter: JP1を使うにあたっての基礎知識
flutter: 知っておく価値のある珍しいHTMLタグ
flutter: 「純粋関数型JavaScriptのつくりかた」に触発されて
flutter: 自宅状況の見える化（M5Stack版）
flutter: 機械学習初心者のためのおすすめ教材について（動画編）
flutter: プログラミング歴2ヶ月の俺が超絶怒涛のアウトプットをした
flutter: GIMPでグラデーション付きの着色をする．
flutter: プログラミング学習記録91〜復活〜
flutter: Live/Tv..!...djokovic vs struff EN Vivo!streaM
flutter: 錦織圭 vs ブノワ・ペール 生放送 全仏オープン テニス
flutter: CoreTweetを使ってTweetアプリを作ろうとしたときの備忘録
```

ここら辺はほぼJSのPromiseと同じなのでスラスラ書けた。



### UI


#### ListView

https://api.flutter.dev/flutter/widgets/ListView-class.html

4種類の生成方法があるけど、UITableViewやLicyclaViewのように再利用してくれるやつは2のItemBuilderを使うやつっぽい。

cookbookにexampleがある。

<img src="https://flutter.dev/images/cookbook/long-lists.gif", width=230,>

https://flutter.dev/docs/cookbook/lists/long-lists




Flutterが用意する[ListTile](https://api.flutter.dev/flutter/material/ListTile-class.html)を使って
シンプルに実装しました。


``` .dart
class ArticleListView extends StatelessWidget {
  final List<Article> articles;
  ArticleListView({Key key, this.articles}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: articles.length,
      itemBuilder: (BuildContext context, int index) {
        final article = articles[index];
        return ListTile(
          leading: CircleAvatar(
            backgroundImage: NetworkImage(article.user.iconUrl),
          ),
          title: Text(article.title),
        );
      },
    );
  }
}
```

セルのタップイベントはListTileにonTapがあるのでそこで取得できます。


``` .dart
    ListTile(
        ... 
        onTap: () { 
          // 画面遷移
        }
    )
```





#### WebView

https://github.com/flutter/plugins/tree/master/packages/webview_flutter


`pubspec.yaml`に追記します。

``` .yml
dependencies:
  flutter:
    sdk: flutter
  # 中略
  webview_flutter: ^0.1.0+1
```

iOSの場合はInfo.plistに以下を追加する必要がある。

``` .xml
    <key>io.flutter.embedded_views_preview</key>
    <string>YES</string>
```


Usage

``` .dart
   WebView(initialUrl: "url")
```


``` .dart
class ArticleDetailPage extends StatelessWidget {
  final Article article;

  ArticleDetailPage({Key key, this.article}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: WebView(
            initialUrl: article.url,
          ),
        ),
      ),
    );
  }
}
```


#### 画面遷移

``` .dart
    ListTile(
        ... 
        onTap: () { 
            Navigator.push(
                context, 
                MaterialPageRoute(builder: (context) => ArticleDetailPage(article: article)),
            );
        }
    )
```

[Navigation](https://flutter.dev/docs/cookbook/navigation/navigation-basics)



<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166241/cd9a6112-9259-a364-8467-802bd70f36e0.png" width="280"/>


ソースコード　https://gist.github.com/churabou/6a34a0236bcf1ca69006b832cfbea789


### 感想

ドキュメントがしっかりしててよかった。特につまずくこともなくスラスラと書けたと思う。
UITableView(delegate, dataSource)やListView(adapter)、GUIでのUI開発のやり方を覚える必要がないので、
QiitaのAPIを表示するということに限れば、ネイティブ開発より何倍も早く実装できると思う。
urlの画像も標準で表示してくれるので凄い楽。簡単なモバイル開発には向いていると思いました。
２日前くらいにFlutter触り始めたのに、SwiftUIの発表で一旦Flutterの学習をやめようかと思いました。
今後Android開発をすることになったら間違いなくFlutterを使いますね。


### Next

- UIをもっと作り込みたい。iOSはListTileだと物足りない。
- Pull to Refresh, 無限スクロース
- リファクタ