“滑动清除”在许多移动应用中都很常见。比如,我们在写一个邮件应用,我们会想让用户能够滑动删除列表中的邮件消息。用户操作时,我们可能需要把这封邮件从收件箱移动到垃圾箱。

Flutter 提供了DismissibleWidget 来轻松地实现这个需求。

步骤

  • 创建项目列表

  • 把每一项打包成一个 Dismissible Widget

  • 提供“滞留”提示

1. 创建项目列表

首先,我们创建一个列表,列表项是能够滑动清除的。至于如何创建列表的更多细节,请参考长列表的处理文档。

创建一个数据源

在我们的例子中,我们需要 20 个样本项来实现列表。为简单起见,我们会生成一个字符串列表。

  1. final items = List<String>.generate(20, (i) => "Item ${i + 1}");

将数据源转换成一个 List

首先,我们简单地在屏幕上展示列表中的每一项。用户现在还无法滑动清除它们!

  1. ListView.builder(
  2. itemCount: items.length,
  3. itemBuilder: (context, index) {
  4. return ListTile(title: Text('${items[index]}'));
  5. },
  6. );

2. 把每一项打包一个 Dismissible Widget

既然我们已经展示了一个列表,我们还要让用户能够将每一项滑出列表!

在用户将某一项滑出屏幕后,我们需要运行一些代码,将那一项从列表中删除并显示一个 Snackbar。在真实的应用中,你可能需要执行更复杂的逻辑,比如从网页服务或数据库中删除此项。

此时,就轮到DismissibleWidget 登场了!在我们的例子中,还要更新itemBuilder函数并返回一个Dismissible Widget。

  1. Dismissible(
  2. // 每个Dismissible实例都必须包含一个Key。Key让Flutter能够对Widgets做唯一标识。(Each Dismissible must contain a Key. Keys allow Flutter to uniquely identify Widgets.)
  3. key: Key(item),
  4. // 我们还需要提供一个函数,告诉应用,在项目被移出后,要做什么。(We also need to provide a function that will tell our app what to do after an item has been swiped away.)
  5. onDismissed: (direction) {
  6. // 从数据源中移除项目(Remove the item from our data source.)
  7. setState(() {
  8. items.removeAt(index);
  9. });
  10. // 展示一个 snackbar!这个snackbar也可以包含“撤销”动作。(Show a snackbar! This snackbar could also contain "Undo" actions.)
  11. Scaffold
  12. .of(context)
  13. .showSnackBar(SnackBar(content: Text("$item dismissed")));
  14. },
  15. child: ListTile(title: Text('$item')),
  16. );

3. 提供“滞留”提示

顾名思义,我们的应用允许用户将列表项滑出列表,但是应用可能没有向用户给出视觉提示,告诉他们操作时发生了什么。要给出提示,表明我们正在删除列表项,就需要在他们将列表项滑出屏幕的时候,展示一个“滞留”提示。这个例子中,我们使用了一个红色背景!

出于这个目的,我们为Dismissible设置了一个background参数。

  1. Dismissible(
  2. // 列表项被滑出时,显示一个红色背景(Show a red background as the item is swiped away)
  3. background: Container(color: Colors.red),
  4. key: Key(item),
  5. onDismissed: (direction) {
  6. setState(() {
  7. items.removeAt(index);
  8. });
  9. Scaffold
  10. .of(context)
  11. .showSnackBar(SnackBar(content: Text("$item dismissed")));
  12. },
  13. child: ListTile(title: Text('$item')),
  14. );

完整代码

  1. import 'package:flutter/foundation.dart';
  2. import 'package:flutter/material.dart';
  3. void main() {
  4. runApp(MyApp());
  5. }
  6. // MyApp是一个StatefulWidget。这样,我们就能够在列表项被移除的时候,更新Widget的状态。(MyApp is a StatefulWidget. This allows us to update the state of the Widget whenever an item is removed.)
  7. class MyApp extends StatefulWidget {
  8. MyApp({Key key}) : super(key: key);
  9. @override
  10. MyAppState createState() {
  11. return MyAppState();
  12. }
  13. }
  14. class MyAppState extends State<MyApp> {
  15. final items = List<String>.generate(3, (i) => "Item ${i + 1}");
  16. @override
  17. Widget build(BuildContext context) {
  18. final title = 'Dismissing Items';
  19. return MaterialApp(
  20. title: title,
  21. theme: ThemeData(
  22. primarySwatch: Colors.blue,
  23. ),
  24. home: Scaffold(
  25. appBar: AppBar(
  26. title: Text(title),
  27. ),
  28. body: ListView.builder(
  29. itemCount: items.length,
  30. itemBuilder: (context, index) {
  31. final item = items[index];
  32. return Dismissible(
  33. // 每个Dismissible实例都必须包含一个Key。Key让Flutter能够对Widgets做唯一标识。(Each Dismissible must contain a Key. Keys allow Flutter to uniquely identify Widgets.)
  34. key: Key(item),
  35. // 我们还需要提供一个函数,告诉应用,在项目被移出后,要做什么。(We also need to provide a function that tells our app what to do after an item has been swiped away.)
  36. onDismissed: (direction) {
  37. // 从数据源中移除项目(Remove the item from our data source.)
  38. setState(() {
  39. items.removeAt(index);
  40. });
  41. // 展示一个 snackbar!(Then show a snackbar!)
  42. Scaffold.of(context)
  43. .showSnackBar(SnackBar(content: Text("$item dismissed")));
  44. },
  45. // 列表项被滑出时,显示一个红色背景(Show a red background as the item is swiped away)
  46. background: Container(color: Colors.red),
  47. child: ListTile(title: Text('$item')),
  48. );
  49. },
  50. ),
  51. ),
  52. );
  53. }
  54. }

Dismissible Demo