在 Row 布局容器中从当前位置滑动 child 小部件
Slide child widget in Row layout container from its current position
我有一行包含 x 个按钮小部件。因此,当用户单击其中一个按钮时,我需要淡出其他按钮并将单击的按钮滑动到行布局的起始位置 (0,0)。我设法使淡出动画起作用。
final _opacityTween = Tween<double>(begin: 1, end: 0);
_opacityAnimation = _opacityTween.animate(CurvedAnimation(
parent: _controller,
curve: new Interval(0.00, 0.50, curve: Curves.linear)
// curve: Curves.ease
));
// And using the opacityAnimation value in Opacity widget.
return Opacity(
opacity: _opacityAnimation.value,
child: child,
);
然而,我一直未能成功使用动画 类 和小部件将 child 小部件滑动到行的起始位置。基本上我不确定什么样的 类 可以用来实现这样的动画。我可以使用 Tween 并将结束位置设置为 (0, 0) 并使用 Translation 来实现它,但是当我将动画初始化为 [=16= 的位置时,我不确定如何设置动画的开始位置] 未知。
试试这个解决方案。我认为这几乎就是您所需要的。您只需添加 SizeTransition 小部件。
问你有什么问题。
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
int _pressedButton;
Animation<double> _animation;
AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_animation = Tween<double>(begin: 1.0, end: 0.0)
.animate(_controller)
..addListener(() {
setState(() { });
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Test"),
),
body: Column(
children: <Widget>[
Row(
children: <Widget>[
_animatedButton(1),
_animatedButton(2),
_animatedButton(3),
_animatedButton(4)
]
),
Text(_pressedButton != null ? "$_pressedButton button pressed" : "No button pressed"),
RaisedButton(
child: Text("Reset"),
onPressed: () {
_controller.reset();
}
)
]
)
);
}
Widget _animatedButton(int button) {
if (button != _pressedButton) {
return SizeTransition(
sizeFactor: _animation,
axis: Axis.horizontal,
child: Opacity(
opacity: _animation.value,
child: _button(button)
)
);
} else {
return _button(button);
}
}
Widget _button(int button) {
return RaisedButton(
onPressed: () => onButtonClick(button),
child: Text("Button $button")
);
}
void onButtonClick(int button) {
setState(() {
_pressedButton = button;
});
_controller.forward();
}
}
更新 1
检查另一种方法来完成您需要的操作。下面的代码中有一些不同的地方。
- 将 SingleTickerProviderStateMixin 更改为 TickerProviderStateMixin。
- 使用两个动画控制器和动画(用于淡出未选中的按钮和向左滑动选中的按钮)。
- 运行 个动画顺序(请参阅动画补间侦听器)。
- 添加 bool _buttonSelected 以跟踪动画的完成情况。
- 使用 _buttonSelected 构建正确的小部件 (Widget _buttonsWidget())
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
int _pressedButton = -1;
bool _buttonSelected = false;
Animation<double> _animationFadeOut;
AnimationController _controllerFadeOut;
Animation<double> _animationSlideLeft;
AnimationController _controllerSlideLeft;
@override
void initState() {
super.initState();
_initFadeOutAnimation();
_initSlideLeftAnimation();
}
void _initFadeOutAnimation() {
_controllerFadeOut = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_animationFadeOut = Tween<double>(begin: 1.0, end: 0.0)
.animate(_controllerFadeOut)
..addListener(() {
setState(() {
if (_controllerFadeOut.isCompleted && !_controllerSlideLeft.isAnimating) {
_controllerSlideLeft.forward();
}
});
});
}
void _initSlideLeftAnimation() {
_controllerSlideLeft = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_animationSlideLeft = Tween<double>(begin: 1.0, end: 0.0)
.animate(_controllerSlideLeft)
..addListener(() {
setState(() {
if (_controllerSlideLeft.isCompleted) {
_buttonSelected = true;
}
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Test"),
),
body: Column(
children: <Widget>[
_buttonsWidget(),
Text(_pressedButton != null ? "$_pressedButton button pressed" : "No button pressed"),
RaisedButton(
child: Text("Reset"),
onPressed: () {
_controllerFadeOut.reset();
_controllerSlideLeft.reset();
_pressedButton = -1;
_buttonSelected = false;
}
)
]
)
);
}
Widget _buttonsWidget() {
if (_buttonSelected) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[_buttonWidget(_pressedButton)]
);
} else {
return Row(
children: <Widget>[
_animatedButtonWidget(1),
_animatedButtonWidget(2),
_animatedButtonWidget(3),
_animatedButtonWidget(4)
]
);
}
}
Widget _animatedButtonWidget(int button) {
if (button == _pressedButton) {
return _buttonWidget(button);
} else {
return SizeTransition(
sizeFactor: _animationSlideLeft,
axis: Axis.horizontal,
child: Opacity(
opacity: _animationFadeOut.value,
child: _buttonWidget(button)
)
);
}
}
Widget _buttonWidget(int button) {
return RaisedButton(
onPressed: () => _onButtonClick(button),
child: Text("Button $button")
);
}
void _onButtonClick(int button) {
setState(() {
_pressedButton = button;
});
_controllerFadeOut.forward();
}
}
我有一行包含 x 个按钮小部件。因此,当用户单击其中一个按钮时,我需要淡出其他按钮并将单击的按钮滑动到行布局的起始位置 (0,0)。我设法使淡出动画起作用。
final _opacityTween = Tween<double>(begin: 1, end: 0);
_opacityAnimation = _opacityTween.animate(CurvedAnimation(
parent: _controller,
curve: new Interval(0.00, 0.50, curve: Curves.linear)
// curve: Curves.ease
));
// And using the opacityAnimation value in Opacity widget.
return Opacity(
opacity: _opacityAnimation.value,
child: child,
);
然而,我一直未能成功使用动画 类 和小部件将 child 小部件滑动到行的起始位置。基本上我不确定什么样的 类 可以用来实现这样的动画。我可以使用 Tween 并将结束位置设置为 (0, 0) 并使用 Translation 来实现它,但是当我将动画初始化为 [=16= 的位置时,我不确定如何设置动画的开始位置] 未知。
试试这个解决方案。我认为这几乎就是您所需要的。您只需添加 SizeTransition 小部件。 问你有什么问题。
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
int _pressedButton;
Animation<double> _animation;
AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_animation = Tween<double>(begin: 1.0, end: 0.0)
.animate(_controller)
..addListener(() {
setState(() { });
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Test"),
),
body: Column(
children: <Widget>[
Row(
children: <Widget>[
_animatedButton(1),
_animatedButton(2),
_animatedButton(3),
_animatedButton(4)
]
),
Text(_pressedButton != null ? "$_pressedButton button pressed" : "No button pressed"),
RaisedButton(
child: Text("Reset"),
onPressed: () {
_controller.reset();
}
)
]
)
);
}
Widget _animatedButton(int button) {
if (button != _pressedButton) {
return SizeTransition(
sizeFactor: _animation,
axis: Axis.horizontal,
child: Opacity(
opacity: _animation.value,
child: _button(button)
)
);
} else {
return _button(button);
}
}
Widget _button(int button) {
return RaisedButton(
onPressed: () => onButtonClick(button),
child: Text("Button $button")
);
}
void onButtonClick(int button) {
setState(() {
_pressedButton = button;
});
_controller.forward();
}
}
更新 1
检查另一种方法来完成您需要的操作。下面的代码中有一些不同的地方。
- 将 SingleTickerProviderStateMixin 更改为 TickerProviderStateMixin。
- 使用两个动画控制器和动画(用于淡出未选中的按钮和向左滑动选中的按钮)。
- 运行 个动画顺序(请参阅动画补间侦听器)。
- 添加 bool _buttonSelected 以跟踪动画的完成情况。
- 使用 _buttonSelected 构建正确的小部件 (Widget _buttonsWidget())
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
int _pressedButton = -1;
bool _buttonSelected = false;
Animation<double> _animationFadeOut;
AnimationController _controllerFadeOut;
Animation<double> _animationSlideLeft;
AnimationController _controllerSlideLeft;
@override
void initState() {
super.initState();
_initFadeOutAnimation();
_initSlideLeftAnimation();
}
void _initFadeOutAnimation() {
_controllerFadeOut = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_animationFadeOut = Tween<double>(begin: 1.0, end: 0.0)
.animate(_controllerFadeOut)
..addListener(() {
setState(() {
if (_controllerFadeOut.isCompleted && !_controllerSlideLeft.isAnimating) {
_controllerSlideLeft.forward();
}
});
});
}
void _initSlideLeftAnimation() {
_controllerSlideLeft = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_animationSlideLeft = Tween<double>(begin: 1.0, end: 0.0)
.animate(_controllerSlideLeft)
..addListener(() {
setState(() {
if (_controllerSlideLeft.isCompleted) {
_buttonSelected = true;
}
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Test"),
),
body: Column(
children: <Widget>[
_buttonsWidget(),
Text(_pressedButton != null ? "$_pressedButton button pressed" : "No button pressed"),
RaisedButton(
child: Text("Reset"),
onPressed: () {
_controllerFadeOut.reset();
_controllerSlideLeft.reset();
_pressedButton = -1;
_buttonSelected = false;
}
)
]
)
);
}
Widget _buttonsWidget() {
if (_buttonSelected) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[_buttonWidget(_pressedButton)]
);
} else {
return Row(
children: <Widget>[
_animatedButtonWidget(1),
_animatedButtonWidget(2),
_animatedButtonWidget(3),
_animatedButtonWidget(4)
]
);
}
}
Widget _animatedButtonWidget(int button) {
if (button == _pressedButton) {
return _buttonWidget(button);
} else {
return SizeTransition(
sizeFactor: _animationSlideLeft,
axis: Axis.horizontal,
child: Opacity(
opacity: _animationFadeOut.value,
child: _buttonWidget(button)
)
);
}
}
Widget _buttonWidget(int button) {
return RaisedButton(
onPressed: () => _onButtonClick(button),
child: Text("Button $button")
);
}
void _onButtonClick(int button) {
setState(() {
_pressedButton = button;
});
_controllerFadeOut.forward();
}
}