You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 23:35:01