Flutter SlideTransition从Offset OFF SCREEN开始
问题内容:
我的应用程序是一个简单的列,其中3个文本小部件包裹在SlideTransitions中。我的目标是使应用程序在屏幕上不加载任何内容,然后从底部(屏幕外)到屏幕(中间居中)对这些Text小部件进行动画处理。
return Column(
children: <Widget>[
SlideTransition(
position:_curve1,
child: Text('Hello 1')
),
SlideTransition(
position:_curve1,
child: Text('Hello 2')
),
SlideTransition(
position:_curve1,
child: Text('Hello 3')
),
]
);
问题是定义动画时,必须指定其初始偏移。
@override
void initState(){
...
Offset beginningOffset = Offset(0.0,20.0);
_curve1 = Tween<Offset>(begin: beginningOffset, end: Offset.zero).animate(
CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut)));
...
}
在这里您可以看到,beginningOffset被指定为Offset(0.0,20.0),它确实将小部件成功地置于屏幕之外。但是,如果我突然在平板电脑上运行该怎么办?由于“偏移”定义为小部件高度的单位,因此,显然这不是将小部件置于屏幕外的好方法。
由于动画是在“ initState()”中定义的,因此…我看不到使用MediaQuery.of(context)之类的方法的方法,因为那里没有上下文。
在本机iOS中,这很简单。在“
viewDidLoad”中,您将从self.view获取框架。您可以将每个文本字段明确地放置在框架的边缘。然后,您可以为约束更改设置动画,将它们放置在所需的位置。为什么在Flutter中这么难?
我感到特别奇怪的是,我找不到的任何示例都完全没有涵盖这种确切的场景(在屏幕外启动动画)。例如:
https://github.com/flutter/website/blob/master/examples/_animation/basic_staggered_animation/main.dart
似乎几乎所有类型的动画都具有这种特征,除了在加载时有显式的屏幕外动画外……也许我只是缺少一些东西。
编辑:ThePeanut解决了它!检查他的“第二次更新”。
问题答案:
您可能需要使用尝试 addPostFrameCallback 在你的方法 INITSTATE 。
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){
// Schedule code execution once after the frame has rendered
print(MediaQuery.of(context).size.toString());
});
}
或者, 您也可以为此使用 Future :
@override
void initState() {
super.initState();
new Future.delayed(Duration.zero, () {
// Schedule a zero-delay future to be executed
print(MediaQuery.of(context).size.toString());
});
}
希望这可以帮助。
更新
这样做有些 不寻常 ,但确实可以满足您的需要。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
Animation<Offset> animation;
AnimationController animationController;
@override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
);
animation = Tween<Offset>(
begin: Offset(0.0, 1.0),
end: Offset(0.0, 0.0),
).animate(CurvedAnimation(
parent: animationController,
curve: Curves.fastLinearToSlowEaseIn,
));
Future<void>.delayed(Duration(seconds: 1), () {
animationController.forward();
});
}
@override
void dispose() {
// Don't forget to dispose the animation controller on class destruction
animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Stack(
alignment: Alignment.center,
fit: StackFit.expand,
children: <Widget>[
SlideTransition(
position: animation,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircleAvatar(
backgroundImage: NetworkImage(
'https://pbs.twimg.com/media/DpeOMc3XgAIMyx_.jpg',
),
radius: 50.0,
),
],
),
),
],
),
);
}
}
它是这样工作的:
1. 我们创建一个带有选项的项目 堆栈 ,以展开其中的任何子项。
2. 将要显示的每张 幻灯片 包装到一个子中心对齐的 列 中。列将占用堆栈大小的100%。
3. 将动画的开始 偏移 设置为 Offset(0.0,1.0) 。
请记住,“ 偏移”中的 dx 和 dy 不是像素或类似的东西,而是 Widget的 宽度或高度的比率。例如:如果
小部件的宽度为100.0, 而您将 dx 设置 为0.25, 则将使 您的孩子向右移动25.0点 。
因此,将offset设置为(0.0,1.0)会将Column屏幕外移到底部100%的高度(这是Flutter中工作的页面过渡数量)。
4 。延迟1秒钟后,使Column动画返回其原始位置。
第二次更新
此代码根据屏幕尺寸和小部件尺寸计算偏移量。 PS。 可能有一种我不知道的更好的方法。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Page(),
);
}
}
class Page extends StatefulWidget {
@override
State<StatefulWidget> createState() => _PageState();
}
class _PageState extends State<Page> with SingleTickerProviderStateMixin {
// Set the initial position to something that will be offscreen for sure
Tween<Offset> tween = Tween<Offset>(
begin: Offset(0.0, 10000.0),
end: Offset(0.0, 0.0),
);
Animation<Offset> animation;
AnimationController animationController;
GlobalKey _widgetKey = GlobalKey();
@override
void initState() {
super.initState();
// initialize animation controller and the animation itself
animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
);
animation = tween.animate(animationController);
Future<void>.delayed(Duration(seconds: 1), () {
// Get the screen size
final Size screenSize = MediaQuery.of(context).size;
// Get render box of the widget
final RenderBox widgetRenderBox =
_widgetKey.currentContext.findRenderObject();
// Get widget's size
final Size widgetSize = widgetRenderBox.size;
// Calculate the dy offset.
// We divide the screen height by 2 because the initial position of the widget is centered.
// Ceil the value, so we get a position that is a bit lower the bottom edge of the screen.
final double offset = (screenSize.height / 2 / widgetSize.height).ceilToDouble();
// Re-set the tween and animation
tween = Tween<Offset>(
begin: Offset(0.0, offset),
end: Offset(0.0, 0.0),
);
animation = tween.animate(animationController);
// Call set state to re-render the widget with the new position.
this.setState((){
// Animate it.
animationController.forward();
});
});
}
@override
void dispose() {
// Don't forget to dispose the animation controller on class destruction
animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
fit: StackFit.loose,
children: <Widget>[
SlideTransition(
position: animation,
child: CircleAvatar(
key: _widgetKey,
backgroundImage: NetworkImage(
'https://pbs.twimg.com/media/DpeOMc3XgAIMyx_.jpg',
),
radius: 50.0,
),
),
],
);
}
}