Flutter SlideTransition 以 Offset OFF SCREEN 开始

13 dart flutter

我的应用程序是一个简单的列,其中包含 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')
     ),
   ]
);
Run Code Online (Sandbox Code Playgroud)

问题是在定义动画时,您必须指定它们的初始偏移量。

@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)));
    ...
}  
Run Code Online (Sandbox Code Playgroud)

在这里你可以看到 beginOffset 被指定为 Offset(0.0, 20.0) ,它成功地将小部件定位在屏幕外。但是如果我突然在平板电脑上运行它会发生什么?由于 Offset 被定义为小部件高度的单位,那么这显然不是将小部件放置在屏幕外的好方法。

由于动画是在“initState()”中定义的……我看不到像 MediaQuery.of(context) 这样的东西……因为我们在那里没有上下文。

在原生 iOS 中,这很简单。在“viewDidLoad”中,您将从 self.view 获取框架。您将明确地将每个文本字段定位在框架的边缘。然后,您可以为约束更改设置动画,将它们放置在您想要的位置。为什么在 Flutter 中这必须如此困难?

我觉得特别奇怪的是,我能找到的任何示例都完全没有涵盖这种确切的场景(在屏幕外启动动画)。例如:

https://github.com/flutter/website/blob/master/examples/_animation/basic_staggered_animation/main.dart

似乎几乎所有类型的动画都在这里展示,除了加载时显式的屏幕外动画......也许我只是错过了一些东西。

编辑:ThePeanut 已经解决了!检查他的“第二次更新”。

The*_*nut 14

您可能需要使用尝试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());
      });
    }
Run Code Online (Sandbox Code Playgroud)

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());
      });
    }
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助。

更新

这样做的方式有点不寻常,但它确实可以满足您的需求。

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,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

它是这样工作的:

1.我们创建了一个带有选项的项目堆栈,可以扩展其中的任何子项。

2.将您要显示的每张幻灯片包装到一个子项居中对齐的中。Column 将占据 Stack 大小的 100%。

3.将动画的开始偏移设置为Offset(0.0, 1.0)

请记住,Offset 中的dxdy不是像素或类似的东西,而是Widget 的宽度或高度的比率。例如:如果您的小部件的宽度为 100.0并且您将0.25 作为 dx - 这将导致您的孩子向右移动 25.0 点

因此,将 offset 设置为 (0.0, 1.0) 会将 Column 离屏移动到底部 100% 的高度(这是 Flutter 中工作的页面转换数量)。

4 . 延迟 1 秒后,将 Column 动画返回到其原始位置。

第二次更新

此代码根据屏幕大小和小部件大小计算偏移量。 附注。可能有我不知道的更好的方法来做到这一点。

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,
          ),
        ),
      ],
    );
  }
}
Run Code Online (Sandbox Code Playgroud)