【Flutter】Row内に上寄せと下寄せにしたいWidgetが両方ある

対象者

  • FlutterでRow内で、上寄せと下寄せにしたい項目がある場合

はじめに

LINEのメッセージが表示されている画面をFlutterでも再現しようとした。しかし、同じRowの中で上揃えと下揃えになっている項目があったため、どのようにするのか分からなかった。
ListViewの中にRowがあるため、Expanded(child:Container())を使ってTextを寄せようとすると、エラーが発生した。
結局、上寄せのRowの中に、下寄せのRowを作ることで解決した。

実施するソース

 Row(
    // 顔アイコンが上側に来る
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      const CircleAvatar(
            child: Icon(
              Icons.favorite,
            ),
      ),
      Row(
        // メッセージと日付を下側に合わせる
        crossAxisAlignment: CrossAxisAlignment.end,
        children: [
          Container(
                child: Text(
                  messages[index],
                ),
          ),
          const SizedBox(width: 5),
          const Text(
            '12:00',
            style: TextStyle(fontSize: 10),
          ),
        ],
      ),
    ],
  ),
  • Row( crossAxisAlignment: CrossAxisAlignment.start,
    Row内の項目を上寄せにしている。そのため、アイコンとメッセージは上揃えになっている。

  • Row( crossAxisAlignment: CrossAxisAlignment.end,
    Row内の項目を下寄せにしている。そのため、メッセージと時刻が下揃えになっている

ダメだった方法

  • Columnを使って、下寄せにしたい項目の上に、Expanded(child:Container())を使う
    ListViewの中にあったので、エラーになった。
    ListViewの中でなかったら、いける。

  • Columnを使って、下寄せにしたい項目の上に、SizedBox(height:?, child:Container())を使う
    動的にメッセージ欄の高さを取得すればできなくもなさそうだが、大変そうなのでやめた。

まとめ

「LINEのメッセージ画面とか楽勝じゃん」と思ってはじめましたが、思わぬところで時間が掛かりました。Columnを二重にして、3項目中2項目は左寄せだが、一項目だけ右寄せにする、ってやったことがあったが、その応用になりますね。
ということで、ColumnでWidgetを下寄せにする方法は色々見つかりましたが、ListViewで特定の項目だけでを下寄せにする、というケースはなかったので、まとめました。ListView内でなくても動作しますので、他のケースでも使えますね。

参考

全ソース

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        useMaterial3: true,
        brightness: Brightness.dark,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<String> messages = [
    'こんにちは',
    'こんにちは。アプリの使い方を教えていただけますか?',
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Expanded(
            child: ListView.builder(
              itemCount: messages.length,
              itemBuilder: (context, index) {
                return Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Row(
                    // 顔アイコンが上側に来る
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const CircleAvatar(
                        child: Icon(
                          Icons.favorite,
                        ),
                      ),
                      Row(
                        // メッセージと日付を下側に合わせる
                        crossAxisAlignment: CrossAxisAlignment.end,
                        children: [
                          Container(
                            padding: const EdgeInsets.symmetric(
                              vertical: 5,
                              horizontal: 10,
                            ),
                            constraints: BoxConstraints(
                              maxWidth: MediaQuery.of(context).size.width * 0.7,
                            ),
                            decoration: const BoxDecoration(
                              color: Colors.grey,
                              borderRadius:
                                  BorderRadius.all(Radius.circular(10)),
                            ),
                            child: Text(
                              messages[index],
                            ),
                          ),
                          const SizedBox(width: 5),
                          const Text(
                            '12:00',
                            style: TextStyle(fontSize: 10),
                          ),
                        ],
                      ),
                    ],
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}