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

求助:如何在Flutter中复现视频所示的showMenu交互行为?

实现思路与代码示例

可以用MouseRegion配合AnimatedContainer轻松实现这种悬停展开的交互效果,核心是通过鼠标进入/离开事件切换状态,触发容器的动画变化。以下是完整的可运行示例:

import 'package:flutter/material.dart';

void main() => runApp(const HoverCardDemo());

class HoverCardDemo extends StatelessWidget {
  const HoverCardDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.grey[100],
        body: Center(
          child: SizedBox(
            width: 300,
            child: HoverExpandCard(
              title: "Sample Card",
              subtitle: "Hover to see more details",
              expandedContent: const Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  SizedBox(height: 12),
                  Text(
                    "Additional Information",
                    style: TextStyle(fontWeight: FontWeight.bold),
                  ),
                  SizedBox(height: 8),
                  Text(
                    "这里是悬停后显示的额外内容,可以是文本、图片或者任何Widget。"
                    "你可以根据需求自定义这部分的布局和样式。",
                    style: TextStyle(color: Colors.grey[700]),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class HoverExpandCard extends StatefulWidget {
  final String title;
  final String subtitle;
  final Widget expandedContent;

  const HoverExpandCard({
    super.key,
    required this.title,
    required this.subtitle,
    required this.expandedContent,
  });

  @override
  State<HoverExpandCard> createState() => _HoverExpandCardState();
}

class _HoverExpandCardState extends State<HoverExpandCard> {
  bool _isHovered = false;

  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      onEnter: (_) => setState(() => _isHovered = true),
      onExit: (_) => setState(() => _isHovered = false),
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeInOut,
        padding: const EdgeInsets.all(16),
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(_isHovered ? 16 : 8),
          boxShadow: [
            BoxShadow(
              color: Colors.black.withOpacity(_isHovered ? 0.12 : 0.05),
              blurRadius: _isHovered ? 12 : 4,
              offset: const Offset(0, 2),
            ),
          ],
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(
              widget.title,
              style: const TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 4),
            Text(
              widget.subtitle,
              style: TextStyle(color: Colors.grey[600]),
            ),
            // 根据悬停状态显示/隐藏额外内容
            if (_isHovered) widget.expandedContent,
          ],
        ),
      ),
    );
  }
}

关键部分说明

  • MouseRegion:通过onEnter和onExit回调更新_isHovered状态,这是触发交互的核心。
  • AnimatedContainer:利用它的动画特性,在状态变化时自动过渡卡片的圆角、阴影等样式,实现平滑的视觉变化。
  • 条件渲染:通过if (_isHovered)控制额外内容的显示,配合容器动画完成展开效果。

额外优化建议

如果展开内容高度不确定,可以用AnimatedSize替代AnimatedContainer自动适配高度:

// 替换AnimatedContainer为AnimatedSize
AnimatedSize(
  duration: const Duration(milliseconds: 300),
  curve: Curves.easeInOut,
  child: Container(
    // 保留原有的decoration、padding配置
    child: Column(/* ... */),
  ),
)

若内容过长,可在展开部分嵌套SingleChildScrollView避免布局溢出。


内容的提问来源于stack exchange,提问作者Danilo Souza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 17:33:22