Animation 颤栗:英雄过渡+;同时制作小部件动画?
所以,我在一个特定的动画案例中遇到了一些关于颤振的问题 基本上,我要做的是同时在相邻的小部件上运行一个用于路线更改的英雄转换和一个自定义动画 分解后,我在根目录下有一个自定义的InheritedWidget,它从StatefulWidget的父级馈送一个应用程序状态。嵌套在我的InheritedWidget中,我有一个WidgetsApp和一个相邻的兄弟用于自定义选项卡导航。这棵树看起来像这样:Animation 颤栗:英雄过渡+;同时制作小部件动画?,animation,dart,flutter,Animation,Dart,Flutter,所以,我在一个特定的动画案例中遇到了一些关于颤振的问题 基本上,我要做的是同时在相邻的小部件上运行一个用于路线更改的英雄转换和一个自定义动画 分解后,我在根目录下有一个自定义的InheritedWidget,它从StatefulWidget的父级馈送一个应用程序状态。嵌套在我的InheritedWidget中,我有一个WidgetsApp和一个相邻的兄弟用于自定义选项卡导航。这棵树看起来像这样: Root Widget (Stateful) | |__Inheri
Root Widget (Stateful)
|
|__InheritedWidget
|
|__WidgetsApp (Handles routing)
|
|__Navigation Bar (Overlay)
class NavigationFaderState extends State<NavigationFader> with NavigatorObserver {
Animation _animation;
// whatever else you need, maybe starting/finishing opacity or position etc.
@override
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
setState(() {
_animation = route.animation;
}
route.animation.addStatusListener((status) {
if (status = AnimationStatus.completed) {
setState(() {
_animation = null;
});
}
});
}
....
}
当我在WidgetsApp上执行使用英雄转换的路由更改时,我的问题就出现了。当这种情况发生时,我还试图根据用户所处的视图设置导航栏的动画,使其显示或隐藏。但是,由于我在我的应用程序状态中使用bool变量通过动画显示或隐藏导航栏,因此这里的SetState调用“覆盖”了英雄转换,因为在这个过程中树是重建的(这就是我的想法)
我最初的想法是,InheritedWidget将捕获应用程序状态的更改,并仅通过updateShouldNotify重建导航栏,但遗憾的是,这并不是我所期望的效果:(
那么-有没有人尝试过类似的东西,或者对如何优雅地处理这个问题有什么想法?:)我做过类似的事情,但不幸的是,我的代码还包含了一大堆其他东西&这是相对复杂的事情,所以我必须把事情分开来做一个比我现在能做的更多的例子。我将解释我所做的事情的一般概念。也许还有更好的方法可以做到这一点 您希望编写一个StatefulWidget,其状态也扩展了NavigatorObserver(您可以使用无状态widget,但我不这么认为)。我个人将其放在树中导航器的上方(即,它在其“构建”函数中构建导航器),但您也可以将其放在导航器的“旁边” 从NavigatorObserver重写didPush、didRemove、didPop等方法。在每个参数中,调用setState并保存动画和其他参数,如下所示:
Root Widget (Stateful)
|
|__InheritedWidget
|
|__WidgetsApp (Handles routing)
|
|__Navigation Bar (Overlay)
class NavigationFaderState extends State<NavigationFader> with NavigatorObserver {
Animation _animation;
// whatever else you need, maybe starting/finishing opacity or position etc.
@override
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
setState(() {
_animation = route.animation;
}
route.animation.addStatusListener((status) {
if (status = AnimationStatus.completed) {
setState(() {
_animation = null;
});
}
});
}
....
}
class NavigationFaderState使用NavigatorObserver扩展状态{
动画(动画),;
//无论你需要什么,可能是开始/结束不透明度或位置等。
@凌驾
void didPush(路由、路由前一路由){
设置状态(){
_动画=route.animation;
}
route.animation.addStatusListener((状态){
如果(状态=AnimationStatus.completed){
设置状态(){
_动画=空;
});
}
});
}
....
}
在构建函数中,您需要检查_动画,并根据它是否存在以及您可能需要设置的任何其他参数设置动画(即,是否设置动画的标志,以及“向前”还是“向后”可能会有所帮助-我相信“弹出”动画从0开始,到1,与“推”动画相同,但我可能错了)。
然后,您可以将此动画连接到您想要设置导航栏动画的任何位置,可能使用AnimatedBuilder或直接连接动画或其他方式。如果对这一切如何工作有任何具体问题,请发表评论,我将添加一些评论等
希望这有帮助()
编辑:以完整的代码为例。作为记录,我不认为这段代码很好,也不认为这是你应该做的事情。但这是一种解决问题的方法。在实际应用程序中使用它之前,值得对其进行测试,可能还需要添加一些断言来检查状态等
进口“包装:颤振/材料.省道”
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
PushListener listener = new PushListener();
@override
Widget build(BuildContext context) {
return new WidgetsApp(
locale: new Locale("en"),
navigatorObservers: [listener],
builder: (context, child) {
// this is here rather than outside the WidgetsApp so that it
// gets access to directionality, text styles, etc
return new Scaffold(
body: child,
bottomNavigationBar:
new ColorChangingNavigationBar(key: listener.navBarKey),
);
},
onGenerateRoute: (settings) {
switch (settings.name) {
case '/':
return new MaterialPageRoute(
settings: settings,
builder: (context) => Column(
children: <Widget>[
new Text(
"I have a green nav bar when you open me and blue when you come back"),
new RaisedButton(
onPressed: () {
Navigator.pushNamed(context, "/red");
},
child: new Text("Next"),
),
],
),
);
case '/red':
return new MaterialPageRoute(
settings: settings,
builder: (context) => Column(
children: <Widget>[
new Text("I have a red nav bar"),
new RaisedButton(
onPressed: () {
Navigator.pop(context);
},
)
],
),
);
}
},
color: Colors.blue,
);
}
}
class PushListener extends NavigatorObserver {
GlobalKey<ColorChangingNavigationBarState> navBarKey = new GlobalKey();
@override
void didPop(Route route, Route previousRoute) {
if (route is ModalRoute && navBarKey.currentState != null) {
var name = route.settings.name;
var color = name == "/" ? Colors.red.shade500 : Colors.blue.shade500;
var animation = new ReverseAnimation(route.animation);
print("Popping & changing color to: ${name == "/" ? "red" : "blue"}");
navBarKey.currentState.setAnimating(animation, color);
}
}
@override
void didPush(Route route, Route previousRoute) {
if (route is ModalRoute && navBarKey.currentState != null) {
var name = route.settings.name;
var color = name == "/" ? Colors.blue.shade500 : Colors.red.shade500;
print("Pushing & changing color to: ${name == "/" ? "red" : "blue"}");
var animation = route.animation;
navBarKey.currentState.setAnimating(animation, color);
}
}
@override
void didRemove(Route route, Route previousRoute) {
// probably don't need
}
@override
void didStartUserGesture() {
// might want to do if gestures are supported with whichever type of
// route you're using.
}
@override
void didStopUserGesture() {
// if you implement didStartUserGesture
}
}
class ColorChangingNavigationBar extends StatefulWidget {
final Color startColor;
ColorChangingNavigationBar(
{Key key, this.startColor = const Color.fromRGBO(0, 255, 0, 1.0)})
: super(key: key);
@override
State<StatefulWidget> createState() => new ColorChangingNavigationBarState();
}
class _ColorAnimationInfo {
final Animation animation;
final Tween<Color> colorTween;
final AnimationStatusListener statusListener;
_ColorAnimationInfo(this.animation, this.colorTween, this.statusListener);
}
class ColorChangingNavigationBarState
extends State<ColorChangingNavigationBar> {
@override
void initState() {
_toColor = widget.startColor;
super.initState();
}
Color _toColor;
_ColorAnimationInfo _colorAnimationInfo;
void setAnimating(Animation animation, Color to) {
var fromColor;
if (_colorAnimationInfo != null) {
fromColor = _colorAnimationInfo.colorTween
.lerp(_colorAnimationInfo.animation.value);
_colorAnimationInfo.animation
.removeStatusListener(_colorAnimationInfo.statusListener);
} else {
fromColor = _toColor;
}
var statusListener = (state) {
if (state == AnimationStatus.completed ||
state == AnimationStatus.dismissed) {
setState(() {
_colorAnimationInfo = null;
});
}
};
animation.addStatusListener(statusListener);
setState(() {
_toColor = to;
Tween<Color> colorTween = new ColorTween(begin: fromColor, end: to);
_colorAnimationInfo =
new _ColorAnimationInfo(animation, colorTween, statusListener);
});
}
@override
Widget build(BuildContext context) {
if (_colorAnimationInfo != null) {
return new AnimatedBuilder(
animation: _colorAnimationInfo.animation,
builder: (context, child) {
return new Container(
color: _colorAnimationInfo.colorTween
.lerp(_colorAnimationInfo.animation.value),
height: 30.0,
);
});
} else {
return new Container(
color: _toColor,
height: 30.0,
);
}
}
@override
void dispose() {
if (_colorAnimationInfo != null) {
_colorAnimationInfo.animation.removeStatusListener(_colorAnimationInfo.statusListener);
}
_colorAnimationInfo = null;
super.dispose();
}
}
void main()=>runApp(新的MyApp());
类MyApp扩展了无状态小部件{
PushListener=新的PushListener();
@凌驾
小部件构建(构建上下文){
返回新的WidgetsApp(
语言环境:新语言环境(“en”),
NavigatorObserver:[侦听器],
生成器:(上下文,子对象){
//这是在这里,而不是在WidgetsApp之外,因此
//获取对方向性、文本样式等的访问权限
归还新脚手架(
身体:孩子,
底部导航栏:
新颜色更改导航栏(键:listener.navBarKey),
);
},
onGenerateRoute:(设置){
开关(设置.名称){
案例“/”:
返回新材料路线(
设置:设置,
生成器:(上下文)=>列(
儿童:[
新文本(
“当你打开我时,我有一个绿色导航条,当你回来时,我有一个蓝色导航条”),
新升起的按钮(
已按下:(){
Navigator.pushNamed(上下文“/red”);
},
子项:新文本(“下一个”),
),
],
),
);
案例“/红色”:
返回新材料路线(
设置:设置,
生成器:(上下文)=>列(
儿童:[
新文本(“我有一个红色导航条”),
新升起的按钮(
已按下:(){
Navigator.pop(上下文);
},
)
],
),
);
}
},
颜色:颜色,蓝色,
);
}
}
类PushListener扩展了NavigatorObserver{
GlobalKey navBarKey=新的GlobalKey();
@凌驾
void didPop(路由、路由前一路由){
if(路由为ModalRoute&&navBarKey.currentState!=null){
var name=route.settings.name;
var color=name==“/”?Colors.red.shade500:Colors.blue.shade500;
var animation=新反向动画(route.animation);
打印(“弹出并将颜色更改为:${name==”/“?”r