如何在Flutter中添加ListTile——一个有例子的教程

2,011 阅读4分钟

大多数时候,在开发时,您可能会发现自己用某种预定义的格式填充ListView。与其自己使用行、列和容器来创建这种布局,你可以使用Flutter中现成的小部件,即ListTile小部件来帮忙。

在本教程中,我将通过一些实际例子向您展示如何在Flutter应用程序中添加ListTile小组件。

什么是ListTile?

Flutter中的ListTile小组件是一个显示相关信息的UI元素。

它遵循 Material Design 的List规范。一个典型的ListTile被分为三个部分:开始中心结束。开始部分包含领先的小组件;中心部分包括标题和副标题;结束部分包含尾随的小组件。

Diagram depicting list tile sections

它主要用于填充可滚动的视图,如ListView、Column和Row。例如,你可以使用ListTile来显示待办事项、电子邮件、导航选项等的列表。你也可以通过点击ListTile来接收点击事件。

如果你是一个视觉学习者,请看这个快速视频教程。

添加ListTile

这里是在ListView小组件内显示ListTile的最小代码:

ListView(
  children: const [
    ListTile(
      leading: Icon(Icons.car_rental),
      title: Text('Car'),
      trailing: Icon(Icons.more_vert),
    ),
    ListTile(
      leading: Icon(Icons.flight),
      title: Text('Flight'),
      trailing: Icon(Icons.more_vert),
    ),
    ListTile(
      leading: Icon(Icons.train),
      title: Text('Train'),
      trailing: Icon(Icons.more_vert),
    )
  ],
)

下面是代码是如何转化为设计的:

List tile code with result showed on iPhone

当你想使用ListTile来填充可能来自你的后台的长列表时,你可以在ListView.Builder里面包裹单个ListTile部件,并在ListTile里面显示数据,如下面的代码所示:

final List<String> items = List<String>.generate(10000, (i) => '$i');
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      leading: CircleAvatar(
        backgroundColor: const Color(0xff764abc),
        child: Text(items[index]),
      ),
      title: Text('Item ${items[index]}'),
      subtitle: Text('Item description'),
      trailing: Icon(Icons.more_vert),
    );
  },
)

输出:

List tile items on iPhone

ListTile变化

还有其他类型的ListTile,它们的存在允许你对其进行特定的操作。

这些是:

  1. CheckboxListTile
  2. RadioListTile
  3. 切换式列表(SwitchListTile

CheckboxListTile

CheckboxListTile小组件是ListTile和Checkbox小组件的组合。

你可以使用这个小组件来标记任何项目的完成--例如;待办事项。默认情况下,复选框显示在ListTile的右侧(对于从左到右的语言)。

下面是你如何添加CheckboxListTile小组件:

class Language {
  String name;
  bool isChecked;
  Language(this.name, {this.isChecked = false});
}
// 1.
final List<Language> languages = [Language('English'), Language('French'), Language('German')];
ListView.builder(
  itemCount: languages.length,
  itemBuilder: (context, index) {
    return CheckboxListTile(
      // 2.
      title: Text('${languages[index].name}'),
      // 3.
      value: languages[index].isChecked,
      // 4.
      onChanged: (bool? value) {
        setState(() {
          languages[index].isChecked = value!;
        });
      },
      // 5.
      secondary: const Icon(Icons.language),
    );
  },
)

代码块中的数字解释:

  1. 一个持有语言列表的变量
  2. 这显示了复选框的标签
  3. 这决定了是否要检查或取消检查项目
  4. 当你点击ListTile时,这个被调用
  5. 这充当了一个领先的图标

输出

English, French and German with checkable boxes

要交换次要的(领先的)部件和复选框,你可以使用controlAffinity 属性并将其设置为ListTileControlAffinity.leading

Checkboxes now on the left of the text

你也可以通过添加checkboxShape 参数并将其设置为RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)) ,来改变复选框的形状:

Circular checkboxes

RadioListTile

RadioListTile小组件是ListTile和RadioButton小组件的组合--这个小组件被用来从一个项目列表中选择一个单一的选项。

下面是你如何添加RadioListTile小组件:

// 1.
enum Gender { male, female }
// 2.
Gender? _gender = Gender.male;
ListView(
  children: [
    // 3.
    RadioListTile<Gender>(
      secondary: Icon(Icons.male),
      controlAffinity: ListTileControlAffinity.trailing,
      title: const Text('Male'),
      // 4.
      value: Gender.male,
      // 5.
      groupValue: _gender,
      // 6.
      onChanged: (Gender? value) {
        setState(() {
          _gender = value;
        });
      },
    ),
    RadioListTile<Gender>(
      secondary: Icon(Icons.female),
      controlAffinity: ListTileControlAffinity.trailing,
      title: const Text('Female'),
      value: Gender.female,
      groupValue: _gender,
      onChanged: (Gender? value) {
        setState(() {
          _gender = value;
        });
      },
    ),
  ],
)

代码块中的数字解释:

  1. 一个枚举,用于保存RadioListTile的所有选择值
  2. 这样就可以使用枚举保存默认的选择
  3. 添加枚举类型的RadioListTile
  4. 将选择值分配给当前的列表瓦片。ListTile代表这个值
  5. 用来显示当前的选择值
  6. 当你切换单选按钮的时候,这个功能会被调用,并带有选择。

输出

male or female option

SwitchListTile

SwitchListTile小组件是ListTile和Switch小组件的组合。

你可以使用这个小部件来构建UI交互,让用户切换应用程序的设置。

下面是你如何添加SwitchListTile小组件:

class Appliance {
  String name;
  bool isOn;
  Appliance(this.name, {this.isOn = false});
}
// 1.
final List<Appliance> appliances = [
  Appliance('TV'),
  Appliance('Fan'),
  Appliance('Refrigerator'),
];
ListView.builder(
  itemCount: appliances.length,
  itemBuilder: (context, index) {
    return SwitchListTile(
      // 2.
      title: Text('${appliances[index].name}'),
      // 3.
      value: appliances[index].isOn,
      // 4.
      onChanged: (bool value) {
        setState(() {
          appliances[index].isOn = value;
        });
      },
    );
  },
)

代码块中的数字解释:

  1. 一个持有电器列表的变量
  2. 这显示ListTile的名称或标题
  3. 这决定了是否要打开或关闭开关
  4. 当你拨动开关时,这个被调用

输出

List items with toggle switch

管理ListTile主题

主题是开发前端应用程序的一个重要方面。主题传达了你的品牌,而且--如果不仔细管理--你可能会浪费很多时间让所有的UI元素都遵循同样的规则。

假设你想改变ListTile的外观和感觉以配合你的设计。你真的有以下两种选择:

  1. 在widget层面改变主题
  2. 在应用程序层面上改变主题

在widget层面上改变主题

你可以通过直接修改ListTile的属性来改变它的外观,比如颜色、形状和大小。

下面是你如何改变ListTile的背景颜色、文本颜色和图标颜色:

return ListTile(
  // 1.
  tileColor: Colors.redAccent,
  // 2.
  textColor: Colors.white,
  // 3.
  iconColor: Colors.white,
);

代码块中的数字解释:

  1. 这将改变ListTile的背景颜色
  2. 改变出现在ListTile上的所有文本的颜色
  3. 这将改变出现在ListTile上的所有图标的颜色

在应用程序层面上改变主题

你可能想在所有的应用程序页面上改变ListTile小组件的视觉美感。你可以通过定义listTileTheme ,然后添加ListTileThemeData 小组件来做到这一点。

ListTileThemeData widget里面,你可以指定你想为你项目中的所有ListTile widget改变的所有属性。

这里是代码的例子:

return MaterialApp(
  title: 'Flutter Demo',
  debugShowCheckedModeBanner: false,
  theme: ThemeData(
    primarySwatch: Colors.blue,
    listTileTheme: ListTileThemeData(
      tileColor: Colors.redAccent,
      textColor: Colors.white,
      iconColor: Colors.white,
    ),
  ),
  home: MyHomePage(),
);

第一种和第二种方法产生的结果都是一样的,如下图所示:

Background is now red

添加一个分隔符

添加一个分隔线可以帮助你清楚地区分项目,特别是当项目在中心部分用三条线显示时。

为了在ListTile项目之间添加分隔线,添加ListView widget。在ListView 里面,添加ListTile.divideTiles ,带有瓦片属性和ListTiles的列表。

代码示例:

ListView(
  children: ListTile.divideTiles(context: context, tiles: [
    ListTile(
      leading: Icon(Icons.car_rental),
      title: Text('Car'),
    ),
    ListTile(
      leading: Icon(Icons.flight),
      title: Text('Flight'),
    ),
    ListTile(
      leading: Icon(Icons.train),
      title: Text('Train'),
    ),
  ]).toList(),
)

输出:

List tile divider

如果你使用ListView.Builder ,你可以用ListView.separated 来代替它,并添加返回分隔符的separatorBuilder 参数。

代码示例:

ListView.separated( // <-- SEE HERE
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      leading: CircleAvatar(
        backgroundColor: const Color(0xff764abc),
        child: Text(items[index]),
      ),
      title: Text('Item ${items[index]}'),
      subtitle: Text('Item description'),
      trailing: Icon(Icons.more_vert),
    );
  },
  separatorBuilder: (context, index) { // <-- SEE HERE
    return Divider();
  },
)

输出:

添加扫一扫-撤消行为

扫除功能允许你用扫除的手势从列表中移除一个特定的ListTile。例如,你可以用这个功能从你的列表中删除一封邮件。

以下是步骤:

  1. 把你的ListTile widget包在Dismissiblewidget里面
  2. 在Dismissible小组件内,添加onDismissed 参数并注册一个回调。在这里你可以写出从列表中删除项目的逻辑。
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return Dismissible( // Step 1
      key: Key(items[index]),
      onDismissed: (direction) { // Step 2
        setState(() {
          items.removeAt(index);
        });
        ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('${items[index]} dismissed')));
      },
      child: ListTile(
        //visualDensity: VisualDensity(vertical: 4),
        leading: CircleAvatar(
          backgroundColor: const Color(0xff764abc),
          child: Text(items[index]),
        ),
        title: Text('Item ${items[index]}'),
        subtitle: Text('Item description'),
        trailing: Icon(Icons.more_vert),
      ),
    );
  },
)
)

(注意:上面的代码只是把ListTile从UI中移除,你必须自己写商业逻辑来从数据库中移除项目)

输出:

User swipes to remove list items

改变ListTile的高度

有时你可能想在某种程度上调整ListTile的高度。ListTile小组件并不直接支持高度属性,因为它的尺寸是按照Material设计规范限制的。所以,为了增加或减少ListTile的高度,你可以使用visualDensity属性。

visualDensity 设置为正数将增加ListTile的高度,而负数将减少其高度。

(注意:你可以设置的最大和最小值是4和**-4**)

下面是代码的例子:

ListTile(
  visualDensity: VisualDensity(vertical: 4), //<-- SEE HERE
  leading: CircleAvatar(
    backgroundColor: const Color(0xff764abc),
    child: Text(items[index]),
  ),
  title: Text('Item ${items[index]}'),
  subtitle: Text('Item description'),
  trailing: Icon(Icons.more_vert),
);

输出:

List item tiles have larger height

自定义

你可以通过利用可用的属性来为ListTile添加各种自定义功能。例如,你可以改变它的颜色(在不同的状态下,如悬停、按下等),形状,在标题和其他元素之间添加空间,以及改变它的高度等。

你可以通过导航到它的定义看到它支持的所有属性。要看到所有的属性,只需右键单击,然后转到>****定义使用

Seeing list of all properties

结论

添加ListTile小组件可以帮助你提高应用程序的开发速度。它遵循材料规范,涵盖了你所需要的一切,以有意义地展示一个项目。

在本教程中,我们首先看了如何添加ListTile,它的类型,以及如何管理主题,并涵盖了一些场景,如添加分隔线和扫码功能,以及改变ListTile的高度,我希望你会发现这些对你建立下一个列表有帮助。