在 Flutter 中为不受约束的小部件设置动画

问题描述 投票:0回答:1

基本上,我有一个顶部导航栏(菜单),并希望它在鼠标悬停时展开。当

onHover
属性变为
true
时,它应该展开并向下动画,而当
onHover
属性变为
false
时,它应该折叠并向上动画。

但是,当它展开时,它的高度应该受到其子级 + 填充的大小的限制。 我不想为容器设置特定的高度。

仅供参考,这是我的代码:

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),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

这部分有效,我只能在展开时使其具有动画效果,当它折叠时,它就会消失而没有动画。

flutter flutter-animation
1个回答
0
投票

这是@pskink 答案的解释和演示

之前的代码:

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),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.