我的应用程序是一个简单的列,其中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()); }); }
Flutter Api文件链接
或者, 您也可以为此使用 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, ), ), ], ); } }