如何在Flutter中实现Material 3规范的卡片/图片多选功能?
Flutter实现Material Design 3「大选项」多选功能
完全可以在Flutter里实现Material Design 3规范的「大选项」多选功能,比如Google Photos那种进入选择模式后,选项左上角显示圆形复选框的效果,用原生组件加状态管理就能搞定,不用依赖额外复杂库。
核心实现思路
- 维护两个核心状态:
isSelecting(标记是否处于选择模式)、selectedItemIds(存储已选项目的ID集合) - 每个选项组件根据
isSelecting状态,决定是否显示左上角的圆形复选框 - 交互逻辑:非选择模式下点击执行原功能(比如打开详情),长按进入选择模式;选择模式下点击切换选中状态
代码示例
先定义一个简单的数据模型:
class MediaItem { final String id; final String imageUrl; final String title; MediaItem({required this.id, required this.imageUrl, required this.title}); }
然后是完整的页面实现:
class LargeSelectionScreen extends StatefulWidget { const LargeSelectionScreen({super.key}); @override State<LargeSelectionScreen> createState() => _LargeSelectionScreenState(); } class _LargeSelectionScreenState extends State<LargeSelectionScreen> { bool isSelecting = false; final Set<String> selectedItemIds = {}; final List<MediaItem> items = [ MediaItem(id: '1', imageUrl: 'https://picsum.photos/200', title: '照片1'), MediaItem(id: '2', imageUrl: 'https://picsum.photos/201', title: '照片2'), MediaItem(id: '3', imageUrl: 'https://picsum.photos/202', title: '照片3'), ]; // 切换单项选中状态 void toggleSelection(String itemId) { setState(() { if (selectedItemIds.contains(itemId)) { selectedItemIds.remove(itemId); // 已选为空时自动退出选择模式 if (selectedItemIds.isEmpty) isSelecting = false; } else { selectedItemIds.add(itemId); isSelecting = true; } }); } // 长按进入选择模式并选中当前项 void enterSelectingMode(String itemId) { setState(() { isSelecting = true; selectedItemIds.add(itemId); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: isSelecting ? Text('已选 ${selectedItemIds.length} 项') : const Text('相册'), leading: isSelecting ? IconButton( icon: const Icon(Icons.close), onPressed: () { setState(() { isSelecting = false; selectedItemIds.clear(); }); }, ) : null, ), body: GridView.builder( padding: const EdgeInsets.all(8), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 8, ), itemCount: items.length, itemBuilder: (context, index) { final item = items[index]; final isSelected = selectedItemIds.contains(item.id); return GestureDetector( onTap: () { if (isSelecting) { toggleSelection(item.id); } else { // 非选择模式下的点击逻辑:比如打开详情页 // Navigator.push(context, MaterialPageRoute(builder: (_) => DetailScreen(item: item))); } }, onLongPress: () => enterSelectingMode(item.id), child: Stack( children: [ // 核心内容:图片/卡片 ClipRRect( borderRadius: BorderRadius.circular(12), child: Image.network( item.imageUrl, fit: BoxFit.cover, height: double.infinity, width: double.infinity, ), ), // 选择模式下显示圆形复选框 if (isSelecting) Positioned( top: 8, left: 8, child: Container( decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface.withOpacity(0.8), shape: BoxShape.circle, ), child: Icon( isSelected ? Icons.check_circle_rounded : Icons.circle_outlined, color: isSelected ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onSurface, size: 28, ), ), ), ], ), ); }, ), ); } }
额外优化提示
- 复选框的样式可以通过
Theme.of(context).colorScheme匹配Material Design 3的主题色彩,确保风格统一 - 如果需要全选、批量删除等功能,可以在AppBar添加对应的操作按钮
- 若是卡片式的大选项,只需把示例中的
Image.network替换为Card组件,交互逻辑完全通用
内容的提问来源于stack exchange,提问作者paul
相关产品推荐
相关产品推荐

