Flutter SlideTransition begin with Offset OFF SCREEN

后端 未结 1 2081
日久生厌
日久生厌 2021-02-20 05:16

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 t

相关标签:
1条回答
  • 2021-02-20 06:04

    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 Docs Link

    OR you can also use a Future for this:

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

    Hope this helps.

    UPDATED

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

    It works this way:

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

    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. Set the beginning Offset for animation to Offset(0.0, 1.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.

    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. Animate the Column back to it's original position after a 1 second delay.

    SECOND UPDATE

    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,
              ),
            ),
          ],
        );
      }
    }
    
    0 讨论(0)
提交回复
热议问题