🎯 Flutter 拖拽选择组件:flutter_drag_selector —— 像选文件一样选择列表项

在 Flutter 开发桌面端应用中,我们经常需要在一个滚动列表(如 ListViewGridViewWrap)中让用户批量选择多个项目。传统的做法是点击每个项目逐一选中,或使用"全选"按钮,但这在项目数量多、布局密集的场景下体验不佳。

flutter_drag_selector 正是为解决这一痛点而生的轻量级 Flutter 插件。它让你像在桌面系统中用鼠标框选文件一样,通过拖拽鼠标(或手指)划出一个区域,自动选中该区域内的所有子组件,大幅提升交互效率。

✨ 核心功能概览

  • 拖拽框选:按住鼠标左键(或触摸屏长按)并拖动,实时绘制一个半透明选择框。
  • 自动识别 :自动检测拖拽区域内所有被 SelectableItem 包裹的子组件,并动态选中/取消选中。
  • 可定制:支持自定义选择区域的样式(颜色、圆角、边框)、选中状态回调、滚动容器控制等。

🛠️ 如何使用?

1. 添加依赖

在你的 pubspec.yaml 文件中添加:

yaml 复制代码
dependencies:
  flutter_drag_selector: ^latest

2. 包装你的列表

将你的滚动容器(如 SingleChildScrollView + Wrap)用 CursorSelectorWidget 包裹,并用 SelectableItem 包裹每一个可选项目。

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_drag_selector/flutter_drag_selector.dart';

class MyApp extends StatefulWidget {
  const MyApp({super.key});
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _list = List.generate(50, (i) => i);
  final _controller = StreamController<(Key?, bool)>.broadcast(); // 用于更新 UI 状态
  final scrollController = ScrollController();

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    _controller.close();
    scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    Widget buildBox(int index) {
      final id = ValueKey<int>(index);
      return StreamBuilder<(Key?, bool)>(
        stream: _controller.stream.where((e) => e.$1 == id),
        builder: (ctx, snapshot) {
          return SelectableItem(
            key: id,
            child: GestureDetector(
              onTap: () {
                debugPrint('tap -> $index');
              },
              child: Container(
                width: 200,
                height: 200,
                decoration: BoxDecoration(
                  color: index % 2 == 0 ? Colors.yellow : Colors.lightBlueAccent,
                ),
                alignment: Alignment.center,
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text('$index', style: const TextStyle(fontSize: 20)),
                    const SizedBox(width: 20),
                    Icon(
                      (snapshot.data?.$2 ?? false)
                          ? Icons.check_box
                          : Icons.check_box_outline_blank,
                      size: 40,
                      color: Colors.red,
                    )
                  ],
                ),
              ),
            ),
          );
        },
      );
    }

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('拖拽选择示例')),
        body: CursorSelectorTheme(
          data: CursorSelectorThemeData(
            selectedAreaDecoration: BoxDecoration(
              color: Colors.blue.withOpacity(0.4),
              borderRadius: BorderRadius.circular(10),
            ),
          ),
          child: CursorSelectorWidget(
            scrollController: scrollController, // 控制滚动
            selectedChangedCallback: (selection) {
              // 选中状态变更回调,用于更新 UI
              _controller.add(selection);
            },
            child: SingleChildScrollView(
              controller: scrollController,
              child: Wrap(
                spacing: 10,
                runSpacing: 10,
                children: _list.map<Widget>(buildBox).toList(),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

3. 效果说明

  • 用户按住鼠标左键(或在移动端长按)并拖动,会看到一个蓝色半透明矩形框跟随鼠标移动。
  • 每次SelectableItem的选中状态变化,都会通过 selectedChangedCallback 回调返回 (Key, isSelected),开发者可据此更新数据模型或 UI。

🎨 自定义主题

你可以通过 CursorSelectorThemeCursorSelectorThemeData 自定义选择框的视觉样式:

dart 复制代码
CursorSelectorTheme(
  data: CursorSelectorThemeData(
    selectedAreaDecoration: BoxDecoration(
      color: Colors.green.withOpacity(0.3),
      border: Border.all(color: Colors.green, width: 2),
      borderRadius: BorderRadius.circular(8),
    )
  ),
  child: CursorSelectorWidget(...),
)

结语

如有设计不佳的点,欢迎指出。

👉 GitHub 地址github.com/bladeofgod/...

👉 Pub 地址pub.dev/packages/fl...

相关推荐
一只大侠的侠1 小时前
Flutter开源鸿蒙跨平台训练营 Day 10特惠推荐数据的获取与渲染
flutter·开源·harmonyos
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
renke33645 小时前
Flutter for OpenHarmony:色彩捕手——基于HSL色轮与感知色差的交互式色觉训练系统
flutter
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端