前回準備とqiitaのAPIをアプリから叩いてみた
repository:
今回はAPIの結果をもって表示させてみたい
かといってFlutterのLayoutの組み方なんて分からないのでまずはリストで適当なものを表示させてみるところを目標とする
とりあえずQiita APIから取得した情報から以下を表示する想定
- user icon
- title
- tags
- 記事の見出し
Widget
とりあえずFlutterのドキュメントを漁っているとWidget Catalogのページを発見
あらかじめ結構な種類のWidgetが用意されている
このWidgetを組み合わせてレイアウトを作っていくっぽい
このWidgetの中からListで表示出来そうなものを探してみると、Material Components widgetsの中からGrid Viewを発見
これを使ってリスト表示をやってみよう
...と思ったがGridViewのドキュメントのサンプルを読み進めるとListViewを使っている箇所を発見
今回はListView
のようにしたいと思っていたのでこちらを使ってみる
後々のことを考えるとAPIのレスポンス次第で要素数は可変になるはずなのでListView.separated
を採用
_MyHomePageState
が以下になる
class _MyHomePageState extends State<MyHomePage> {
final List<String> entries = <String>['A', 'B', 'C'];
final List<int> colorCodes = <int>[600, 500, 100];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView.separated(
padding: const EdgeInsets.all(8),
itemCount: entries.length,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 50,
color: Colors.amber[colorCodes[index]],
child: Center(child: Text('Entry ${entries[index]}')),
);
},
separatorBuilder: (BuildContext context, int index) => const Divider(),
),
floatingActionButton: const FloatingActionButton(
onPressed: null,
tooltip: 'Search',
child: Icon(Icons.search),
),
);
}
}
右下のボタンはなんか使いそうな気もするので検索用として残しておきます
起動するとこんな感じ
サンプルの通り表示出来ました
このままだと味気がないのでitemBuilder
の中をもう少し作り込みます
wireframeでこんな感じのイメージ
ちなみにこのwireframeはuizardにあるテンプレートから引っ張ってきてます。手書きのwireframeを自動でデザインに起こしてくれたりします。とても便利
とりあえずググりながらレイアウトの枠組みを組んでみる
borderでContainerの表示領域がどこなのか表示しつつやった結果以下が出来た
body: ListView.separated(
padding: const EdgeInsets.all(8),
itemCount: entries.length,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 150,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
border: Border.all(
color: Colors.red,
width: 1.0,
),
),
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
width: 1.0,
),
),
child: Row(
children: [
Image.network(
'https://picsum.photos/200/300',
fit: BoxFit.cover,
),
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.green,
width: 1.0,
),
),
child: Column(
children: [
Container(
alignment: Alignment.centerLeft,
decoration: BoxDecoration(
border: Border.all(
color: Colors.yellow,
width: 1.0,
),
),
child: const Text('article title')),
Container(
alignment: Alignment.centerLeft,
decoration: BoxDecoration(
border: Border.all(
color: Colors.blue,
width: 1.0,
),
),
child: const Text('tags')),
Container(
alignment: Alignment.centerLeft,
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey,
width: 1.0,
),
),
child: const Text('headline')),
],
),
))
],
),
));
},

使用したWidgetについて
widget | 役割 |
---|---|
Row | 子要素を横並びにしてくれる |
Column | 子要素を縦並びにしてくれる |
Expanded | 親要素の表示領域に広がってくれる |
Image | 画像を表示してくれる |
なんとなくの大枠は出来た気がするのでborderは全て消してレイアウトを整えていきます
- アイコンは丸枠のイメージがあるので
Image
を丸枠にしてみる -
Row
やColumn
の要素間にmarginを設定したい
1番はググるとCircleAvatar
を使えるというstack overflowが出てきたので使ってみよう
2番もググるとこれまたoverflowが既にありました
色々方法があるみたいだが、均等に入れたいわけではないので今回はSizedBox
を使う方法を採用
結果としてこんなコードになりました
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView.separated(
padding: const EdgeInsets.all(8),
itemCount: entries.length,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 150,
padding: const EdgeInsets.all(16),
child: Row(
children: [
const CircleAvatar(
radius: 25,
backgroundImage:
NetworkImage('https://picsum.photos/200/300')),
const SizedBox(width: 24),
Expanded(
child: Column(
children: [
Container(
alignment: Alignment.centerLeft,
child: const Text(
'article title',
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 24),
)),
Container(
alignment: Alignment.centerLeft,
child: const Text(
'tags',
style: TextStyle(color: Colors.grey),
)),
const SizedBox(height: 8),
Container(
alignment: Alignment.centerLeft,
child: const Text('headline')),
],
),
)
],
),
);
},
separatorBuilder: (BuildContext context, int index) => const Divider(),
),
floatingActionButton: const FloatingActionButton(
onPressed: null,
tooltip: 'Search',
child: Icon(Icons.search),
),
);
}
土台は出来たので次回API取得したデータをこれに反映させてみたいと思います