如何在 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,
),
)
],
),
),
);
}
}
我正在尝试在我的应用程序中实现启动画面页面。我需要在显示页面时自动启动动画,但实际实现不起作用。动画未在页面打开时执行。
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,
),
)
],
),
),
);
}
}