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

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

✨ 核心功能概览

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

img_introduce.gif

🛠️ 如何使用?

1. 添加依赖

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


yaml

体验AI代码助手

代码解读

复制代码

dependencies: flutter_drag_selector: ^latest

2. 包装你的列表

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


dart

体验AI代码助手

代码解读

复制代码

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

体验AI代码助手

代码解读

复制代码

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(...), )

原文:https://juejin.cn/post/7573332163387506742

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐