Flutter SlideTransition从Offset OFF SCREEN开始 [英] Flutter SlideTransition begin with Offset OFF SCREEN

查看:123
本文介绍了Flutter SlideTransition从Offset OFF SCREEN开始的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用是一个简单的列,其中3个文本小部件包裹在SlideTransitions中。我的目标是使应用程序在屏幕上不加载任何内容,然后将这些Text小部件从底部(屏幕外)动画到屏幕上(在中间居中)。

My app is a simple Column with 3 Text widgets wrapped in SlideTransitions. My goal is for the app to load with nothing on the screen, and then animate these Text widgets from the bottom (off screen) up into the screen (settling in the middle).

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')
     ),
   ]
);

问题是定义动画时,必须指定其初始偏移量。

The problem is that when defining the animation, you have to specify their initial Offset.

@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)确实可以将小部件成功置于屏幕之外。但是,如果我突然在平板电脑上运行该怎么办?由于将偏移定义为小部件高度的单位,因此,显然这不是将小部件置于屏幕外的好方法。

Here you can see that beginningOffset is specified as Offset(0.0, 20.0) which does successfully position the widgets off screen. But what happens if I suddenly run this on a tablet? Since Offset is defined as units of the height of the widget, then this is obviously not a good way to position a widget off screen.

由于动画是在 initState() ...我看不到使用MediaQuery.of(context)之类的方法的方法,因为那里没有上下文。

Since the animation is defined in "initState()" ... I see no way to use something like MediaQuery.of(context) ... since we don't have context there.

在本地iOS,这很简单。在 viewDidLoad中,您将从self.view获取框架。您可以将每个文本字段明确地放置在框架的边缘。然后,您可以为约束更改设置动画,将其放置在所需的位置。为什么在Flutter中必须这么难?

In native iOS this would be simple. Within "viewDidLoad" you would get the frame from self.view. You'd position each text field at the edge of the frame explicitly. You'd then animate a constraint change, positioning them where you want them. Why must this be so hard in Flutter?

我特别感到奇怪的是,在所有示例中都没有涉及这种确切的场景(在屏幕外开始播放动画)我能找到。例如:

I find it especially curious that this exact scenario (starting an animation just off screen) is totally not covered in any of the examples I could find. For instance:

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

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

Seems like almost every type of animation is featured here EXCEPT an explicit off screen animation on load... Perhaps I'm just missing something.

编辑:ThePeanut解决了它!检查他的第二次更新。

ThePeanut has solved it! Check his "Second Update".

推荐答案

您可能希望尝试在自己的计算机中使用 addPostFrameCallback 方法 initState

You might want to try using the addPostFrameCallback method in your 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文档链接

OR 一个未来

    @override
    void initState() {
      super.initState();
      new Future.delayed(Duration.zero, () {
          // Schedule a zero-delay future to be executed
          print(MediaQuery.of(context).size.toString());
      });
    }

希望这会有所帮助。

更新

一些不寻常方式,但这确实可以满足您的要求

A bit of a unusual way to do it, but it really does the thing you need.

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。。我们创建一个项目项,并带有选项以扩展其中的任何子项。

1. We create a Stack of items with options to expand any child inside it.

2。。将要显示在其中的每个幻灯片包裹到一个以子级居中对齐的中。列将占用堆栈的100%。

2. Wrap each slide you want to display in to a Column with center alignment of children. The Column will take 100% of the size of the Stack.

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

3. Set the beginning Offset for animation to Offset(0.0, 1.0).

请记住 dx dy 偏移中的像素不是像素之类的,而是小部件的宽度或高度的比率。例如:如果您的小部件的宽度为100.0 ,并且您将 0.25作为dx -它会导致您的孩子向右移动25.0点

Keep in mind that dx and dy in Offset are not pixels or something like that, but the ratio of Widget's width or height. For example: if your widget's width is 100.0 and you put 0.25 as dx - it will result in moving your child to the right by 25.0 points.

因此,将offset设置为(0.0,1.0)会将列在屏幕外移至底部,其高度为100%(这是Flutter中有多少个页面过渡效果)。

So setting offset to (0.0, 1.0) will move the Column offscreen to the bottom by it's 100% height (this is how many page transitions work in Flutter).

4 。延迟1秒后将列动画设置为原始位置。

4. Animate the Column back to it's original position after a 1 second delay.

第二次更新

此代码根据屏幕尺寸和小部件尺寸计算偏移量。
PS。也许有一种我不知道的更好的方法。

This code calculates the offset based on the screen size and widget size. PS. There might be a better way of doing this that I don't know of.

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,
          ),
        ),
      ],
    );
  }
}

这篇关于Flutter SlideTransition从Offset OFF SCREEN开始的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆