基本上,我有一个顶部导航栏(菜单),并希望它在鼠标悬停时扩展.当onHover属性变成true时,它应该向下扩展并动画,当onHover属性变成false时,它应该向下折叠并动画.

然而,当它展开时,它的高度应该受到其子+填充的大小的限制.I don't want to set a specific height to the container.

作为参考,这是我的代码:

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool isTopBarHovered = false;

  void handleTopBarHover(bool isHovered) {
    setState(() {
      isTopBarHovered = isHovered;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: MediaQuery.of(context).size.width < 800
          ? AppBar()
          : PreferredSize(
              preferredSize: Size(MediaQuery.of(context).size.width, 48.0),
              child: TopNavigationBar(onHover: handleTopBarHover),
            ),
      body: Stack(
        children: [
          Container(),
          Positioned(
            top: 0,
            left: 0,
            right: 0,
            child: AnimatedSize(
              duration: const Duration(milliseconds: 250),
              curve: Curves.easeInOut,
              child: isTopBarHovered
                  ? Container(
                    color: Colors.black,
                    child: const Padding(
                      padding: EdgeInsets.symmetric(vertical: 88.0),
                      child: Center(
                        child: Text('Additional Widget'),
                      ),
                    ),
                  )
                  : const SizedBox(height: 0),
            ),
          ),
        ],
      ),
    );
  }
}
class TopNavigationBar extends StatefulWidget {
  final Function(bool) onHover;

  const TopNavigationBar({super.key, required this.onHover});

  @override
  State<TopNavigationBar> createState() => _TopNavigationBarState();
}

class _TopNavigationBarState extends State<TopNavigationBar> {
  @override
  Widget build(BuildContext context) {
    bool isHover = false;

    return Container(
      color: Colors.black,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          InkWell(
            onTap:(){},
            onHover: (val) {
              setState(() {
                isHover = val;
              });
              widget.onHover(isHover);
            },
            child: const Padding(
              padding: EdgeInsets.symmetric(vertical: 16.0, horizontal: 8.0),
              child: Text(
                "Item 01",
                style: TextStyle(color: Colors.white),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

这部分有效,我只能在扩展时让它充满活力,当它折叠时,它就会消失而没有动画.

推荐答案

This is the explanation and the demo for the @pskink answer

之前的代码:

Positioned(
  top: 0,
  left: 0,
  right: 0,
  child: AnimatedSize(
    duration: const Duration(milliseconds: 250),
    curve: Curves.easeInOut,
    child: isTopBarHovered
        ? Container(
          color: Colors.black,
          child: const Padding(
            padding: EdgeInsets.symmetric(vertical: 88.0),
            child: Center(
              child: Text('Additional Widget'),
            ),
          ),
        )
        : const SizedBox(height: 0),
  ),
),

步骤:

  1. AnimatedSize替换为AnimatedAlign并将对齐设置为底部,因为您将向下动画化小部件,因此您必须将其粘在底部
  2. 动画随heightFactor变化,就像调整布局边界框比例,1或0

后面的代码:

Positioned(
  top: 0,
  left: 0,
  right: 0,
  child: AnimatedAlign(
    duration: const Duration(milliseconds: 250),
    alignment: Alignment.bottomCenter,
    heightFactor: isTopBarHovered ? 1 : 0,
    curve: Curves.easeInOut,
    child: Container(
      color: Colors.black,
      child: const Padding(
        padding: EdgeInsets.symmetric(vertical: 88.0),
        child: Center(
          child: Text(
            'Additional Widget',
            style: TextStyle(color: Colors.white),
          ),
        ),
      ),
    ),
  ),
),

这就是结果:

enter image description here

感谢@pskink

这是最终的代码:

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool isTopBarHovered = false;

  void handleTopBarHover(bool isHovered) {
    setState(() {
      isTopBarHovered = isHovered;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: MediaQuery.of(context).size.width < 800
          ? AppBar()
          : PreferredSize(
              preferredSize: Size(MediaQuery.of(context).size.width, 48.0),
              child: TopNavigationBar(onHover: handleTopBarHover),
            ),
      body: Stack(
        children: [
          Container(),
          Positioned(
            top: 0,
            left: 0,
            right: 0,
            child: AnimatedAlign(
              duration: const Duration(milliseconds: 250),
              alignment: Alignment.bottomCenter,
              heightFactor: isTopBarHovered ? 1 : 0,
              curve: Curves.easeInOut,
              child: Container(
                color: Colors.black,
                child: const Padding(
                  padding: EdgeInsets.symmetric(vertical: 88.0),
                  child: Center(
                    child: Text(
                      'Additional Widget',
                      style: TextStyle(color: Colors.white),
                    ),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class TopNavigationBar extends StatefulWidget {
  final Function(bool) onHover;

  const TopNavigationBar({super.key, required this.onHover});

  @override
  State<TopNavigationBar> createState() => _TopNavigationBarState();
}

class _TopNavigationBarState extends State<TopNavigationBar> {
  @override
  Widget build(BuildContext context) {
    bool isHover = false;

    return Container(
      color: Colors.black,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          InkWell(
            onTap: () {},
            onHover: (val) {
              setState(() {
                isHover = val;
              });
              widget.onHover(isHover);
            },
            child: const Padding(
              padding: EdgeInsets.symmetric(vertical: 16.0, horizontal: 8.0),
              child: Text(
                "Item 01",
                style: TextStyle(color: Colors.white),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Flutter相关问答推荐

飘动的文本用/字符绕转

如何更改ElevatedButton的 colored颜色 以按下它?

如何更新文本字段的基础上的选定下拉菜单?

创建后检索FiRestore文档ID

底部导航栏项目在抖动中不更改 colored颜色

video_player在Android上返回401 Unauthorized with Bunny CDN

悬停时打开Flutter 下拉按钮

如何确定Flutter 中的文本 colored颜色 ?

如何在 Dart/Flutter 中克隆 Future

SliverAppbar 呈蓝色

Dart:未捕获错误:RangeError(索引):索引超出范围:索引应小于 7:7

Flutter:使用 GlobalKey 访问 BlocProvider

无状态小部件被奇怪地重建

如何在 Flutter 中的 BackdropFilter.blur 中制作一个 100x100 的清晰孔

是否有可能减少代码的编写,例如:ref.read(countProvider.notifier) - 在构建方法中?

statefulWidget 无法进行所需更改的问题

无法在Flutter 图像小部件中打开大尺寸(500MB)图像

使用 onPressed 切换到另一个屏幕无法正常工作

函数中的变量在 Dart 中应该有什么关键字?

Flutter:整个判断整个应用生命周期的通用类