LoginSignup
1
2

More than 1 year has passed since last update.

FlutterでQiitaのアプリを作ってみる その2

Last updated at Posted at 2023-03-07

前回準備と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),
      ),
    );
  }
}

右下のボタンはなんか使いそうな気もするので検索用として残しておきます
起動するとこんな感じ

サンプルの通り表示出来ました:tada:
このままだと味気がないのでitemBuilderの中をもう少し作り込みます
wireframeでこんな感じのイメージ
image.png
ちなみにこの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は全て消してレイアウトを整えていきます

  1. アイコンは丸枠のイメージがあるのでImageを丸枠にしてみる
  2. RowColumnの要素間に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取得したデータをこれに反映させてみたいと思います

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2