Flutter滑动操作单元格插件flutter_swipe_action_cell_forked的使用

Flutter滑动操作单元格插件flutter_swipe_action_cell_forked的使用

A package that can give you a cell that can be swiped, effect is like iOS native

Package forked from flutter_swipe_action_cell


如果你喜欢这个包,可以给我一个星😀。这个项目获得的星星越多,我花在这个项目上的时间就会越多😀

捐赠:

  • MOMO

入门指南

3.0.0及以后版本适用于Flutter 3

pub home page点击这里: pub

安装:

flutter_swipe_action_cell_forked: ^3.1.4

示例代码

示例代码:

class ItemView extends StatefulWidget {
  const ItemView({Key? key, this.item, this.controller}) : super(key: key);
  final Model? item;
  final SwipeActionController? controller;
  [@override](/user/override)
  State<ItemView> createState() => _ItemViewState();
}

class _ItemViewState extends State<ItemView> with TickerProviderStateMixin {
  late Animation<double> animation;
  late AnimationController controller;

  [@override](/user/override)
  void initState() {
    super.initState();
    controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 500),
      value: 0.0,
    );
    animation = Tween<double>(begin: 0, end: 1).animate(controller);
  }

  Future<void> onDoneAnimation() async {
    await controller.forward();
  }

  Future<void> onResetAnimation() async {
    controller.reverse();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    double width = MediaQuery.of(context).size.width;
    return Stack(
      children: [
        Container(
          width: width,
          height: 80,
          decoration: BoxDecoration(color: Colors.red),
          child: Row(
            children: [
              Expanded(child: Text('Texxt nay hienj ra khi sur dung cu chi')),
              Spacer(),
              Expanded(child: Text('Texxt nay hienj ra khi sur dung cu chi')),
            ],
          ),
        ),
        FadeTransition(
            opacity: animation,
            child: Container(
              width: width,
              color: Colors.white,
              child: GestureDetector(
                onTap: () {
                  Navigator.push(context, CupertinoPageRoute(builder: (ctx) => const HomePage()));
                },
                child: Padding(
                  padding: const EdgeInsets.all(20.0),
                  child: Text("This is index of ${widget.item?.index}", style: const TextStyle(fontSize: 30)),
                ),
              ),
            )),
        SwipeActionCell(
          controller: widget.controller,
          index: widget.item?.index,
          backgroundColor: Colors.transparent,
          key: ValueKey(widget.item),
          doneAnimation: onDoneAnimation,
          afterResetAnimation: onResetAnimation,
          trailingActions: [SwipeAction(onTap: (v) {}, performsFirstActionWithFullSwipe: true)],
          leadingActions: [SwipeAction(onTap: (v) {}, performsFirstActionWithFullSwipe: true)],
          child: Container(
            color: Colors.white,
            child: GestureDetector(
              onTap: () {
                Navigator.push(context, CupertinoPageRoute(builder: (ctx) => const HomePage()));
              },
              child: Padding(
                padding: const EdgeInsets.all(20.0),
                child: Text("This is index of ${widget.item?.index}", style: const TextStyle(fontSize: 30)),
              ),
            ),
          ),
        ),
      ],
    );
  }
}
class _SwipeActionPageState extends State<SwipeActionPage> {
  List<Model> list = List.generate(30, (index) {
    return Model()..index = index;
  });

  late SwipeActionController controller;

  [@override](/user/override)
  void initState() {
    super.initState();
    controller = SwipeActionController(selectedIndexPathsChangeCallback: (changedIndexPaths, selected, currentCount) {
      print('cell at ${changedIndexPaths.toString()} is/are ${selected ? 'selected' : 'unselected'} ,current selected count is $currentCount');

      /// 我只是在这里简单地调用了setState()来更新UI。
      /// 但是整个页面会被重建。
      /// 所以当你开发时,最好只更新一小部分UI子树以获得最佳性能。

      setState(() {});
    });
  }

  Widget bottomBar() {
    return Container(
      color: Colors.grey[200],
      padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Row(
          children: [
            Expanded(
              child: CupertinoButton.filled(
                  padding: const EdgeInsets.only(),
                  child: const Text('open cell at 2'),
                  onPressed: () {
                    controller.openCellAt(index: 2, trailing: true, animated: true);
                  }),
            ),
            const SizedBox(
              width: 10,
            ),
            Expanded(
              child: CupertinoButton.filled(
                  padding: const EdgeInsets.only(),
                  child: const Text('switch edit mode'),
                  onPressed: () {
                    controller.toggleEditingMode();
                  }),
            ),
          ],
        ),
      ),
    );
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      bottomNavigationBar: bottomBar(),
      appBar: CupertinoNavigationBar(
        middle: CupertinoButton.filled(
            padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
            minSize: 0,
            child: const Text('deselect all', style: TextStyle(fontSize: 22)),
            onPressed: () {
              controller.deselectAll();
            }),
        leading: CupertinoButton.filled(
            padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 10),
            minSize: 0,
            child: Text('delete cells (${controller.getSelectedIndexPaths().length})', style: const TextStyle(color: Colors.white)),
            onPressed: () {
              /// 获取选取的索引集合
              List<int> selectedIndexes = controller.getSelectedIndexPaths();

              List<String> idList = [];
              for (var element in selectedIndexes) {
                idList.add(list[element].id);
              }

              /// 遍历id集合,并且在原来的list中删除这些id所对应的数据
              for (var itemId in idList) {
                list.removeWhere((element) {
                  return element.id == itemId;
                });
              }

              /// 更新内部数据,这句话一定要写哦
              controller.deleteCellAt(indexPaths: selectedIndexes);
              setState(() {});
            }),
        trailing: CupertinoButton.filled(
            minSize: 0,
            padding: const EdgeInsets.all(10),
            child: const Text('select all'),
            onPressed: () {
              controller.selectAll(dataLength: list.length);
            }),
      ),
      body: ListView.builder(
        physics: const BouncingScrollPhysics(),
        itemCount: list.length,
        itemBuilder: (context, index) {
          return _item(context, index);
        },
      ),
    );
  }

  Widget _item(BuildContext ctx, int index) {
    return ItemView(item: list[index],controller: controller,);
  }
}

示例代码:

import 'package:example/item_view.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_swipe_action_cell_forked/flutter_swipe_action_cell.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      /// 添加这个可以在路由切换的时候统一关闭打开的cell,全局有效
      navigatorObservers: [SwipeActionNavigatorObserver()],
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  [@override](/user/override)
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: CupertinoButton.filled(
            child: const Text('Enter new page'),
            onPressed: () {
              Navigator.push(context, CupertinoPageRoute(builder: (c) => const SwipeActionPage()));
            }),
      ),
    );
  }
}

class Model {
  String id = UniqueKey().toString();
  int index = 0;

  [@override](/user/override)
  String toString() {
    return index.toString();
  }
}

class SwipeActionPage extends StatefulWidget {
  const SwipeActionPage({Key? key}) : super(key: key);

  [@override](/user/override)
  _SwipeActionPageState createState() => _SwipeActionPageState();
}

class _SwipeActionPageState extends State<SwipeActionPage> {
  List<Model> list = List.generate(30, (index) {
    return Model()..index = index;
  });

  late SwipeActionController controller;

  [@override](/user/override)
  void initState() {
    super.initState();
    controller = SwipeActionController(selectedIndexPathsChangeCallback: (changedIndexPaths, selected, currentCount) {
      print('cell at ${changedIndexPaths.toString()} is/are ${selected ? 'selected' : 'unselected'} ,current selected count is $currentCount');

      /// 我只是在这里简单地调用了setState()来更新UI。
      /// 但是整个页面会被重建。
      /// 所以当你开发时,最好只更新一小部分UI子树以获得最佳性能。

      setState(() {});
    });
  }

  Widget bottomBar() {
    return Container(
      color: Colors.grey[200],
      padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Row(
          children: [
            Expanded(
              child: CupertinoButton.filled(
                  padding: const EdgeInsets.only(),
                  child: const Text('open cell at 2'),
                  onPressed: () {
                    controller.openCellAt(index: 2, trailing: true, animated: true);
                  }),
            ),
            const SizedBox(
              width: 10,
            ),
            Expanded(
              child: CupertinoButton.filled(
                  padding: const EdgeInsets.only(),
                  child: const Text('switch edit mode'),
                  onPressed: () {
                    controller.toggleEditingMode();
                  }),
            ),
          ],
        ),
      ),
    );
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      bottomNavigationBar: bottomBar(),
      appBar: CupertinoNavigationBar(
        middle: CupertinoButton.filled(
            padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
            minSize: 0,
            child: const Text('deselect all', style: TextStyle(fontSize: 22)),
            onPressed: () {
              controller.deselectAll();
            }),
        leading: CupertinoButton.filled(
            padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 10),
            minSize: 0,
            child: Text('delete cells (${controller.getSelectedIndexPaths().length})', style: const TextStyle(color: Colors.white)),
            onPressed: () {
              /// 获取选取的索引集合
              List<int> selectedIndexes = controller.getSelectedIndexPaths();

              List<String> idList = [];
              for (var element in selectedIndexes) {
                idList.add(list[element].id);
              }

              /// 遍历id集合,并且在原来的list中删除这些id所对应的数据
              for (var itemId in idList) {
                list.removeWhere((element) {
                  return element.id == itemId;
                });
              }

              /// 更新内部数据,这句话一定要写哦
              controller.deleteCellAt(indexPaths: selectedIndexes);
              setState(() {});
            }),
        trailing: CupertinoButton.filled(
            minSize: 0,
            padding: const EdgeInsets.all(10),
            child: const Text('select all'),
            onPressed: () {
              controller.selectAll(dataLength: list.length);
            }),
      ),
      body: ListView.builder(
        physics: const BouncingScrollPhysics(),
        itemCount: list.length,
        itemBuilder: (context, index) {
          return _item(context, index);
        },
      ),
    );
  }

  Widget _item(BuildContext ctx, int index) {
    return ItemView(item: list[index],controller: controller,);
  }
}

更多关于Flutter滑动操作单元格插件flutter_swipe_action_cell_forked的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter滑动操作单元格插件flutter_swipe_action_cell_forked的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


flutter_swipe_action_cell_forked 是一个用于在 Flutter 应用中实现滑动操作单元格的插件。它允许你在列表项或表格单元格上添加滑动操作,类似于 iOS 的邮件应用中的滑动删除功能。

以下是如何使用 flutter_swipe_action_cell_forked 插件的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 flutter_swipe_action_cell_forked 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_swipe_action_cell_forked: ^1.0.0

然后运行 flutter pub get 来获取依赖。

2. 导入插件

在你的 Dart 文件中导入插件:

import 'package:flutter_swipe_action_cell_forked/flutter_swipe_action_cell_forked.dart';

3. 创建滑动操作单元格

你可以使用 SwipeActionCell 来包裹你的列表项或表格单元格,并定义滑动操作。

class MyListView extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: 10,
      itemBuilder: (context, index) {
        return SwipeActionCell(
          key: ValueKey(index), // 为每个单元格设置唯一的 key
          trailingActions: <SwipeAction>[
            SwipeAction(
              title: "Delete",
              onTap: (CompletionHandler handler) async {
                // 删除操作
                print("Delete item $index");
                await handler(true); // 确认操作完成
              },
              color: Colors.red,
            ),
          ],
          child: ListTile(
            title: Text("Item $index"),
          ),
        );
      },
    );
  }
}

4. 定义滑动操作

SwipeActionCell 中,你可以定义 leadingActionstrailingActions,分别表示向左滑动和向右滑动时显示的操作。

每个 SwipeAction 包含以下属性:

  • title: 操作的标题。
  • onTap: 操作被点击时的回调函数。
  • color: 操作按钮的背景颜色。

5. 处理滑动操作

onTap 回调中,你可以执行删除、编辑等操作。onTap 回调函数接收一个 CompletionHandler 参数,用于确认操作是否完成。

onTap: (CompletionHandler handler) async {
  // 执行删除操作
  print("Delete item $index");
  await handler(true); // 确认操作完成
},

6. 完整示例

以下是一个完整的示例,展示如何在 ListView 中使用 SwipeActionCell

import 'package:flutter/material.dart';
import 'package:flutter_swipe_action_cell_forked/flutter_swipe_action_cell_forked.dart';

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Swipe Action Cell Example')),
        body: MyListView(),
      ),
    );
  }
}

class MyListView extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: 10,
      itemBuilder: (context, index) {
        return SwipeActionCell(
          key: ValueKey(index),
          trailingActions: <SwipeAction>[
            SwipeAction(
              title: "Delete",
              onTap: (CompletionHandler handler) async {
                print("Delete item $index");
                await handler(true);
              },
              color: Colors.red,
            ),
          ],
          child: ListTile(
            title: Text("Item $index"),
          ),
        );
      },
    );
  }
}
回到顶部