49

So this seems basic but I can't figure it out. I have a ListTile that has a leading checkbox, a title, and a trailing icon. With the last Flutter update, the checkbox and icon are no longer centered for some reason. I want them centered vertically. I tried adding Center(), Align(), Expanded(), and Flexible() in various ways to the checkbox but it just pushes the title off the screen or does nothing.

Any tips? Any help is appreciated.

ListTile(
        leading: Checkbox(
          value: item.checked,
          onChanged: (bool newValue) {
            setState(() {
              item.checked = newValue;
            });
            firestoreUtil.updateList(user, taskList);
          },
          activeColor: Theme.of(context).primaryColor,
        ),
        title: InkWell(
            onTap: () {
              editTask(item);
            },
            child: Text(
              item.task,
              style: TextStyle(fontSize: 18),
            )),
        trailing: ReorderableListener(
          child: Container(
              padding: EdgeInsets.only(right: 16),
              child: Icon(Icons.drag_handle)),
        ),
        contentPadding: EdgeInsets.all(8),
      ),

Debug mode: screenshot of ListTile

krishnaacharyaa
  • 14,953
  • 4
  • 49
  • 88
Jared
  • 2,029
  • 5
  • 20
  • 39

8 Answers8

71

Use Column inside the leading, and set the MainAxisAlignment to center

  leading: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: <Widget>[
      Icon(leadingnIcon, color: Colors.blueGrey,),
    ],
   ),
Isaac
  • 719
  • 5
  • 3
48

Just add Icon inside Container with double.infinity height.

leading: Container(
                height: double.infinity,
                child: Icon(Icons.star),
              ),
Fuad All
  • 871
  • 11
  • 13
  • 2
    This works, and seems to be the simplest solution on this page. This vertical misalignment happens when following App Brewery's Angela Yu's free "Introduction to Flutter Development Using Dart", at Section 6 when she introduces Card and ListTile. It looks aligned on her screen recording, maybe because the misalignment bug began after she made the lesson. – Doochz Jun 14 '20 at 09:41
  • I am looking for a solution for a ListTile that nested inside a Dismissible widget. Seems that the solution is not working for the background child. – Prabowo Murti Nov 11 '20 at 02:46
  • It is technically not a bug, but a change in the Material Design specs. It happened between two version of Flutter where they implemented that change. I don't remember the exact version of Flutter. – Sam Smets Aug 06 '21 at 04:56
  • 5
    This no longer works – Edwin Liu May 01 '22 at 00:38
  • [Container vs SizedBox](https://stackoverflow.com/a/55716668/188331) – Raptor Dec 28 '22 at 07:01
  • @EdwinLiu Add `alignment: Alignment.centerLeft,` – BeniaminoBaggins May 06 '23 at 04:13
6

Try the following code

ListTile(
  leading: const SizedBox(
       height: double.infinity,
       child: Icon(Icons.location_on_rounded)),
  tileColor: DesignColor.grey,
  title: "Title 1".textMediumRegular(),
  subtitle: "Title 2".textSmall(),
  trailing: const SizedBox(
       height: double.infinity,
       child: Icon(Icons.edit_rounded)),
)

Output:

enter image description here


Caution Doesn't work for all cases !!!

In Flutter's ListTile, the leading and trailing doesn't span accross the full height of the ListTile when subtitle is used. Hence create your own Widget using row.


Detailed Comparision between both the Widgets:

enter image description here

Use My Custom Code:

class CustomListTile extends StatelessWidget {
  Widget? trailingWidget;
  late CrossAxisAlignment crossAxisAlignment;
  late TextStyle titleStyle;
  late TextStyle subtitleStyle;
  String title;
  String? subtitle;
  Widget? leadingWidget;
  CustomListTile(
      {super.key,
      this.trailingWidget,
      this.crossAxisAlignment = CrossAxisAlignment.center,
      this.titleStyle =
          const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
      this.subtitleStyle =
          const TextStyle(fontSize: 14, fontWeight: FontWeight.w400),
      required this.title,
      this.subtitle,
      this.leadingWidget});

  @override
  Widget build(BuildContext context) {
    return Row(
      crossAxisAlignment: crossAxisAlignment,
      children: [
        leadingWidget ?? Container(),
        const SizedBox(
          width: 16,
        ),
        Expanded(
          child: Column(
            children: [
              Text(
                title,
                style: titleStyle,
              ),
              Text(subtitle ?? "", style: subtitleStyle),
            ],
          ),
        ),
        const SizedBox(width: 16),
        trailingWidget ?? Container()
      ],
    );
  }
}

Extra:

Code of the image for you to work around

        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Container(
                color: Colors.blue,
                child: ListTile(
                  minLeadingWidth: 0,
                  isThreeLine: true,
                  minVerticalPadding: 0,
                  contentPadding: EdgeInsets.zero,
                  leading: Container(
                    color: Colors.orange,
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: const [
                        Icon(
                          Icons.home,
                        ),
                      ],
                    ),
                  ),
                  title: Container(
                    color: Colors.pink,
                    child: const Text(
                        "This is very long long long long title of the list view"),
                  ),
                  subtitle: Container(
                    color: Colors.yellow,
                    child: const Text(
                        "This is very long long long long subtitle of the list view . This is very long long long long subtitle of the list view .This is very long long long long subtitle of the list view "),
                  ),
                  trailing: Container(
                    color: Colors.orange,
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: const [
                        Icon(
                          Icons.home,
                        ),
                      ],
                    ),
                  ),
                ),
              ),
              Container(
                color: Colors.blue,
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Container(
                      color: Colors.orange,
                      child: const Icon(Icons.home),
                    ),
                    const SizedBox(
                      width: 16,
                    ),
                    Expanded(
                      child: Column(
                        children: [
                          Container(
                            color: Colors.pink,
                            child: const Text(
                              "This is very long long long long title of the list view ",
                              style: TextStyle(
                                  fontSize: 16, fontWeight: FontWeight.w600),
                            ),
                          ),
                          Container(
                            color: Colors.yellow,
                            child: const Text(
                              "This is very long long long long subtitle of the list view . This is very long long long long subtitle of the list view .This is very long long long long subtitle of the list view ",
                              style: TextStyle(
                                  fontSize: 14, fontWeight: FontWeight.w400),
                            ),
                          ),
                        ],
                      ),
                    ),
                    const SizedBox(width: 16),
                    Container(
                      color: Colors.orange,
                      child: const Icon(Icons.home),
                    ),
                  ],
                ),
              )
            ],
          ),
        ));
krishnaacharyaa
  • 14,953
  • 4
  • 49
  • 88
5

I solved this by just making my own ListTile. It's basically just a row with padding. Works great and is more customizable than ListTile.

Jared
  • 2,029
  • 5
  • 20
  • 39
  • 7
    This could have been the best answer if you would have given the code – Siddy Hacks Oct 17 '22 at 10:34
  • If u need to see a snipet for a Row, u are extremely green with flutter my friend....Here's the link anyways: https://api.flutter.dev/flutter/widgets/Row-class.html – Ramiro G.M. Jul 08 '23 at 21:59
1

This below trick work for me,

leading: Container(
            constraints: const BoxConstraints(minWidth: 70.0, maxWidth: 80),
            height: double.infinity,
            child: Align(
              alignment: Alignment.centerLeft,
              child: Text( 
              ....
Arul
  • 1,031
  • 13
  • 23
0

In case you choose to go the DIY route, here's an example implementation of a simple ListTile:

class CustomListTile extends StatelessWidget {
  const CustomListTile(
      {Key? key,
      required this.title,
      this.content,
      this.leading,
      this.trailing,
      this.onTap})
      : super(key: key);
  final Widget title;
  final Widget? content;
  final Widget? leading;
  final Widget? trailing;
  final VoidCallback? onTap;

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: onTap,
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            leading ?? const SizedBox(),
            const SizedBox(width: 20),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  title,
                  const SizedBox(height: 4),
                  content ?? const SizedBox(),
                ],
              ),
            ),
            trailing ?? const SizedBox(),
          ],
        ),
      ),
    );
  }
}
Breno Teodoro
  • 433
  • 4
  • 11
0

You can do it like this, there is no better solution

ListTile(
    minLeadingWidth: 0.0,
    minVerticalPadding: 0.0,
    contentPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
    title: Text('Bla bla'),
    leading: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(Icons.close)
        ],
     ),
)
Cenk YAGMUR
  • 3,154
  • 2
  • 26
  • 42
0

I don't think it's a problem with ListTile widget. It is a widget that follows the Material 3 guide of the flutter, so if the title and sub-title are less than 2 lines, leading and trailing fields are all aligned in the center, and if the 2 lines are over, I think it is right to align to top. So I conclude that it's good to make a custom widget.

max.back
  • 1
  • 1