potatotipsのLT用にサンプルアプリを作ったので紹介です。
この記事で使っている画像は発表スライドから抜粋してます。
環境
- Flutter: beta-1
- Dart: 1系
アプリ
入力前 | 検索結果 |
---|---|
コード
コードは https://github.com/rkowase/flutter-sample に置きました。
サンプルアプリを簡単に解説
main.dart
UI部分
サンプルアプリのコンテンツ領域(body部分)の表示要素としてはTextField, Button, ListViewなのでListViewのchildrenにそれぞれのWidgetを配置していきます。
検索ボタン押下時の処理やListViewの各要素の生成処理は後述します。
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new ListView(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
children: <Widget>[
new TextField(
decoration: const InputDecoration(
hintText: 'Flutter',
labelText: 'Query',
),
maxLines: 1,
controller: _controller,
),
new Container(
padding: const EdgeInsets.all(20.0),
child: new RaisedButton(
child: const Text('Search'),
onPressed: _search,
),
),
new Container(
height: 500.0,
child: new ListView(
key: _listViewKey,
itemExtent: 50.0,
children: _createWidgets(_items),
),
),
],
),
),
);
処理部分
以下の_search()
メソッドは検索ボタン押下時の処理で、GithubClientから取得してきた結果を_item
に入れてsetState()
でUI再描画(再構築)してます。
void _search() {
var client = new GithubClient();
client.get(_controller.text).then((result) {
setState(() {
_items = result;
});
});
}
以下の_createWidgets()
メソッドはList Widgetの各要素であるListTileを生成しています。
アバター画像に関してはNetworkImage
を使って非同期に簡単に取得できます。
Iterable<Widget> _createWidgets(List<GithubRepo> items) {
var ret = new List<Widget>();
if (items == null) {
return ret;
}
items.forEach((item) {
ret.add(
new ListTile(
leading: new CircleAvatar(
backgroundImage: new NetworkImage(item.avatarUrl),
),
title: new Text('${item.name} / ⭐ ${item.starCount}'),
)
);
});
return ret;
}
API Client
async/awaitが使えるのでシュッと書けます。
class GithubClient {
Future<List<GithubRepo>> get(String query) async {
var url = 'https://api.github.com/search/repositories?sort=stars&q=';
var httpClient = new HttpClient();
try {
var request = await httpClient.getUrl(Uri.parse(url + query));
var response = await request.close();
if (response.statusCode != HttpStatus.OK) {
return null;
}
var json = await response.transform(UTF8.decoder).join();
return GithubRepo.fromJson(json);
} catch (exception) {
return null;
}
}
}
Model
今回はリポジトリ名、Star数、アバターを表示したかったので以下のようになりました。
fromJson()
メソッドでGitHub APIのレスポンスをパースしてます。
class GithubRepo {
var name;
var starCount;
var avatarUrl;
static fromJson(json) {
var data = JSON.decode(json);
List<Map<String, Object>> itemList = data['items'];
return itemList.map((item) {
var repo = new GithubRepo();
Map<String, Object> owner = item['owner'];
repo.avatarUrl = owner['avatar_url'];
repo.name = item['full_name'];
repo.starCount = item['stargazers_count'];
return repo;
}).toList();
}
}
感想
- 環境構築は非常に簡単でAndroidStudioをすでに使っていれば数分でHello Worldを動かすことができました。
- VSCodeでもそんな変わらないと思います。
- Flutterはドキュメントが非常に充実してるので https://flutter.io/ を見るのが分かりやすいです。
- 今回作ったサンプルアプリは色々調べながらですが1日くらいで出来ました。
- Hot Reload最高
- Androidだと一覧を表示するためにAdapterを用意しないといけなかったりなにかとやることが多いですが、Flutterだと簡単に書けてかなり楽でした。
- 公式Widgetが充実しているので知れば知るほどできることが増えて早く開発できそうです。
- Nativeとの連携部分はまだ試せていないのでハードウェア(カメラなど)を使うアプリとかも作ってみたいです。
補足
- 通信中のインジケータ表示やエラー時(通信失敗など)の処理は入れていません。
- 固定文字列(URLなど)はconstとかにしたほうがいいですがリファクタリングしてません。
- Viewはもっとうまく作る方法がある気がします。(こういう書き方のほうがいいとかありましたら教えてください!)
- (追記)画面内の要素を上から順に配置するために
ListView
を使っていますが、今回の用途だとColumn
を使ったほうがよかったです。 - (追記)あとリポジトリ一覧の
ListView
の親要素はContainer
ではなくExpanded
にしたほうがよかったです。
- (追記)画面内の要素を上から順に配置するために
おまけ
エラー画面のカラーコード
- Background: 7C160E
- Text: FFFE7F
Hello Worldを簡単に解説
Hello Worldという文字列を表示するだけでView再描画の必要がないのでStatelessWidget
を継承しています。
画面全体の入れ物にあたるScaffold
の中に画面上部のappBar
とbody
があり、それぞれにWidgetを入れていきます。
リンク
potatotipsで「try! Flutter」という発表をしました - Rui Kowase's blog
https://rkowase.hatenablog.com/entry/2018/03/18/154816
try! Flutter // Speaker Deck
https://speakerdeck.com/rkowase/try-flutter