我试图在Flutter中制作一个径向菜单,并希望我的菜单按钮每次按下时都有旋转动画。我遵循了TensorProgramming关于Youtube的Flutter动画基础知识的教程,但是由于某些原因,我的动画无法正常工作。下面,我包含了RadialMenuWidget的代码。
我已经正确配置了动画控制器,并使用SingleTickerProviderStateMixin扩展了小部件的状态。PS我正在模拟器上运行,热重载时有时会更改图标的旋转。
任何帮助将不胜感激!
import 'package:flutter/material.dart'; import 'package:savings/utils/colors.dart'; import 'package:savings/widgets/themed_radial_menu_item.dart'; class CustomThemedRadialMenu extends StatefulWidget { final List<CustomThemedRadialMenuItem> items; CustomThemedRadialMenu({@required this.items}); @override _CustomThemedRadialMenuState createState() => _CustomThemedRadialMenuState(); } class _CustomThemedRadialMenuState extends State<CustomThemedRadialMenu> with SingleTickerProviderStateMixin { Animation animationOpenClose; AnimationController animationControllerOpenClose; bool isOpen; @override void initState() { isOpen = false; animationControllerOpenClose = AnimationController(duration: new Duration(seconds: 5), vsync: this); animationOpenClose = Tween(begin: 0.0, end: 360.0).animate(animationControllerOpenClose) ..addListener(() { setState(() {}); }); animationControllerOpenClose.repeat(); super.initState(); } @override Widget build(BuildContext context) { ///A list of the items and the center menu button final List<Widget> menuContents = <Widget>[]; for (int i = 0; i < widget.items.length; i++) { ///Menu items menuContents.add(widget.items[1]); ///Menu Close/Open button menuContents.add(new InkWell( onTap: () {}, child: Container( padding: new EdgeInsets.all(10.0), decoration: new BoxDecoration( color: Colors.white, border: new Border.all(color: darkHeadingsTextColor), shape: BoxShape.circle, ), child: new Transform.rotate( angle: animationControllerOpenClose.value, child: isOpen ? new Icon(Icons.clear) : new Icon(Icons.menu)), ), )); } return new Stack( alignment: Alignment.center, children: menuContents, ); } @override void dispose() { animationControllerOpenClose.dispose(); super.dispose(); } closeMenu() { animationControllerOpenClose.forward(); setState(() { isOpen = false; }); print("RadialMenu Closed"); } openMenu() { animationControllerOpenClose.forward(); setState(() { isOpen = true; }); print("RadialMenu Opened"); } }
您的操作存在一些问题。但仅供参考- 如果您清理代码并提出一个封装的问题,人们很可能会为您提供帮助。这将需要删除所有未包含的类,并理想地发布一个可以粘贴到单个文件中并按原样运行的解决方案。
话虽如此,我已经实现了我认为您正在尝试做的事情。我已经删除了其中的一些内容,以便实际构建,因此您需要将它们重新添加。
您遇到的主要问题如下:
..addListener(() {
setState(() {}); });
这是不理想的,因为它将迫使整个小部件重新构建动画的每个滴答声。这将导致不是很基本的小部件出现严重的性能问题。使用AnimatedBuilder代替。或者,如果您正在寻找动画何时结束使用..addStateListener
AnimatedBuilder
..addStateListener
角度以弧度为单位波动。我没有为Pi常数导入Math的麻烦,但您可能应该这样做。
您有一个实际上没有调用openMenu或的点按closeMenu,因此它绝对不会执行任何操作= D。
openMenu
closeMenu
您可以将动画控制器设置为重复。这意味着它将永远持续下去。
您没有将任何内容传递给animationControllerOpenClose.forward通话。这意味着它将从当前位置动画到1.0状态,即使已经是1.0。我刚刚传入了0,但是您可能想对打开/关闭进行一些操作(如果用户在进行动画操作时点击了它或执行其他操作)。
animationControllerOpenClose.forward
(切线问题)您似乎在每个菜单小部件之后都添加了一个新的“菜单打开/关闭”按钮。那可能就是您想要做的,但我想您只是想添加一个。
无论如何,这是可行的示例。每次点击时,它都会更改图标,然后旋转。
import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: "Spinnig Menu", theme: ThemeData( primaryColor: Colors.red, ), home: new Scaffold( body: new SafeArea( child: new Column( children: <Widget>[new CustomThemedRadialMenu()], ), ), ), ); } } class CustomThemedRadialMenu extends StatefulWidget { @override _CustomThemedRadialMenuState createState() => _CustomThemedRadialMenuState(); } class _CustomThemedRadialMenuState extends State<CustomThemedRadialMenu> with SingleTickerProviderStateMixin { Animation animationOpenClose; AnimationController animationControllerOpenClose; bool isOpen; @override void initState() { isOpen = false; animationControllerOpenClose = AnimationController(duration: new Duration(seconds: 5), vsync: this); animationOpenClose = Tween(begin: 0.0, end: 3.14159 * 2).animate(animationControllerOpenClose); super.initState(); } @override Widget build(BuildContext context) { ///A list of the items and the center menu button final List<Widget> menuContents = <Widget>[]; ///Menu Close/Open button menuContents.add(new InkWell( onTap: () { if (isOpen) { closeMenu(); } else { openMenu(); } }, child: Container( padding: new EdgeInsets.all(10.0), decoration: new BoxDecoration( color: Colors.white, border: new Border.all(color: Colors.black38), shape: BoxShape.circle, ), child: new AnimatedBuilder( animation: animationControllerOpenClose, builder: (context, child) { return new Transform.rotate(angle: animationOpenClose.value, child: child); }, child: isOpen ? new Icon(Icons.clear) : new Icon(Icons.menu), )), )); return new Stack( alignment: Alignment.center, children: menuContents, ); } @override void dispose() { animationControllerOpenClose.dispose(); super.dispose(); } closeMenu() { animationControllerOpenClose.forward(from: 0.0); setState(() { isOpen = false; }); print("RadialMenu Closed"); } openMenu() { animationControllerOpenClose.forward(from: 0.0); setState(() { isOpen = true; }); print("RadialMenu Opened"); } }
值得注意的是,我在AnimatedBuilder的构建函数中构建了尽可能少的小部件。在那里构建的越少,就性能而言就越好。由于图标本身不会在旋转过程中发生变化,因此您可以简单地将其作为子项传递。
我猜测接下来可能会出现这种情况,因此仅供参考- 您可以使用动画的交叉淡入淡出小部件在图标之间进行过渡(只需将其放入AnimatedBuilder的子对象中即可):
new AnimatedCrossFade( firstChild: new Icon(Icons.clear), secondChild: new Icon(Icons.menu), crossFadeState: isOpen ? CrossFadeState.showFirst : CrossFadeState.showSecond, duration: Duration(milliseconds: 300)),