如何在 Flutter 页面加载时自动启动动画?

How to start automatically animation at page load on Flutter?

我正在尝试在我的应用程序中实现启动画面页面。我需要在显示页面时自动启动动画,但实际实现不起作用。动画未在页面打开时执行。

import 'package:flutter/material.dart';
import 'package:coin_flip_app/home_page.dart';
import 'package:flutter_svg/flutter_svg.dart';

class SplashScreenPage extends StatefulWidget {
  SplashScreenPage({Key key}) : super(key: key);

  @override
  _SplashScreenPageState createState() => _SplashScreenPageState();
}

class _SplashScreenPageState extends State<SplashScreenPage>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
        vsync: this, duration: Duration(milliseconds: 250), value: 1);

    Future.delayed(Duration(seconds: 4), () {
      Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => HomePage(),
          ));
    });

    _controller.forward().then((f) {
      _controller.reverse();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        color: const Color.fromARGB(255, 48, 48, 48),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RotationTransition(
              turns: Tween(begin: 0.0, end: 1.0).animate(_controller),
              child: SizedBox(
                child: SvgPicture.asset('assets/app_logo.svg'),
                height: 150,
              ),
            ),
            const Padding(
              padding: const EdgeInsets.only(top: 20.0),
              child: Text(
                "My App",
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 32,
                ),
                textAlign: TextAlign.center,
              ),
            )
          ],
        ),
      ),
    );
  }
}

如果我尝试使用带有 GestureDetector 的 onTap() 事件来调用动画,它会起作用。但我需要它在没有用户输入的情况下自动启动和完成。

更新 #1

这是更新后的代码。它也不起作用。它只能取消对第一个 // await Future.delayed(Duration(seconds: 1)); 的注释,但我认为这只是一种解决方法,而不是最终解决方案

import 'package:flutter/material.dart';
import 'package:coin_flip_app/home_page.dart';
import 'package:flutter_svg/flutter_svg.dart';

class SplashScreenPage extends StatefulWidget {
  SplashScreenPage({Key key}) : super(key: key);

  @override
  _SplashScreenPageState createState() => _SplashScreenPageState();
}

class _SplashScreenPageState extends State<SplashScreenPage>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
        vsync: this, duration: Duration(milliseconds: 250), value: 1);

    WidgetsBinding.instance.addPostFrameCallback(
        (_) => loopOnce(context)); //i add this to access the context safely.
  }

  Future<void> loopOnce(BuildContext context) async {
    // await Future.delayed(Duration(seconds: 1));
    await _controller.forward();
    await _controller.reverse();
    //we can add duration here
    await Future.delayed(Duration(seconds: 1));
    Navigator.of(context).push(MaterialPageRoute(
      // since this triggers when the animation is done, no duration is needed
      builder: (context) => HomePage(),
    ));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        color: const Color.fromARGB(255, 48, 48, 48),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RotationTransition(
              turns: _controller,
              child: SizedBox(
                child: SvgPicture.asset('assets/app_logo.svg'),
                height: 150,
              ),
            ),
            const Padding(
              padding: const EdgeInsets.only(top: 20.0),
              child: Text(
                "My App",
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 32,
                ),
                textAlign: TextAlign.center,
              ),
            )
          ],
        ),
      ),
    );
  }
}

奇怪,也许可以尝试使用

_controller.repeat(reverse: true);

而不是

_controller.forward().then((f) {
      _controller.reverse();
    });

哦,别忘了处理

@override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

你可以直接用这个

RotationTransition(
              turns:_controller,
              child: SizedBox(
                child: SvgPicture.asset('assets/app_logo.svg'),
                height: 150,
              ),
            ),

编辑:

所以如果我理解正确的话你想这样做

向前循环然后向后循环一次, 当循环结束时,导航到屏幕。

我的方法就是这个

@override
  void initState() {
    super.initState();
    _controller = AnimationController(
        vsync: this, duration: Duration(milliseconds: 250), value: 1);
    WidgetsBinding.instance.addPostFrameCallback((_)=>loopOnce(context));//i add this to access the context safely.
  }

  Future<void> loopOnce(BuildContext context)async{
    await _controller.forward();
    await _controller.reverse();
    //we can add duration here 
    //await Future.delayed(Duration(seconds: 4));
    Navigator.of(context).push(MaterialPageRoute( // since this triggers when the animation is done, no duration is needed
            builder: (context) => HomePage(),
          ));
  }

编辑 2:

class Loader extends StatefulWidget {
  const Loader();
  @override
  _LoaderState createState() => _LoaderState();
}

class _LoaderState extends State<Loader> with SingleTickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
        vsync: this, duration: Duration(milliseconds: 1500), value: 0);

    WidgetsBinding.instance.addPostFrameCallback(
        (_) => loopOnce(context)); //i add this to access the context safely.
  }

  Future<void> loopOnce(BuildContext context) async {
    // await Future.delayed(Duration(seconds: 1));
    await _controller.forward();
    await _controller.reverse();
    //we can add duration here
    await Future.delayed(Duration(seconds: 1));
    Navigator.of(context).pushNamed('');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        color: const Color.fromARGB(255, 48, 48, 48),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RotationTransition(
              turns: _controller,
              child: Container(
                width: 150,
                color: Colors.red,
                height: 150,
              ),
            ),
            const Padding(
              padding: const EdgeInsets.only(top: 20.0),
              child: Text(
                "Coin Flip",
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 32,
                ),
                textAlign: TextAlign.center,
              ),
            )
          ],
        ),
      ),
    );
}
}