首页感谢大家的支持,发出去的文章被点赞、转发、收藏,说明对大家有用,这样的写作我会坚持下去,坚持分享Flutter项目给大家。
朋友养的鱼死了,悲伤不已。
他不想给鱼土葬,说想给它火葬,然后再把鱼
的骨灰撤回大海,好让它再回到母亲的怀抱。
谁知道那玩意儿越烤越香,后来就买了两瓶啤
酒......
很多事情,人们走着走着,就忘了初心。
——生活就是这样,变化太快,我们连自己的心情都无法控制。
本头条核心宗旨
欢迎来到「技术刚刚好」作者,「技术刚刚好」是个人维护,每天至少更新一篇Flutter技术文章,实时为大家播报Flutter最新消息。如果你刚好也在关注Flutter这门技术,那就跟我一起学习进步吧,你的赞,收藏,转发是对我个人最大的支持,维护不易,欢迎关注。
技术刚刚好经历
近几年,移动端跨平台开发技术层出不穷,从Facebook家的ReactNative,到阿里家WEEX,前端技术在移动端跨平台开发中大展身手,技术刚刚好作为一名Android开发,经历了从Reactjs到Vuejs的不断学习。而在2018年,我们的主角变成了Flutter,这是Goolge开源的一个移动端跨平台解决方案,可以快速开发精美的移动App。希望跟大家一起学习,一起进步!
本文核心要点
今天分享一个漂亮的登录页面,打开会有一个正在加载中动画,成功后就进入登录页面,一个动态加载文字动画,图片也跟着移动起来。
pubspec.yaml文件
这个文件都很正常,就是一个图片加载配置。
main.dart文件
import 'package:flutter/material.dart'; import 'package:travel/splash_page.dart'; import 'package:travel/landing_page.dart'; class TravelApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: "Travel App", onGenerateRoute: (RouteSettings settings) { switch (settings.name) { case '/landing': return new SplashPageToLandingPageRoute( builder: (_) => new LandingPage(), settings: settings ); } }, home: new SplashPage(), ); } } void main() => runApp(new TravelApp()); class SplashPageToLandingPageRoute<T> extends MaterialPageRoute<T> { SplashPageToLandingPageRoute( { WidgetBuilder builder, RouteSettings settings }) : super(builder: builder, settings: settings); @override Duration get transitionDuration => new Duration(milliseconds: 1500); @override Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) { if (settings.isInitialRoute) return child; final curve = new CurvedAnimation( parent: animation, curve: Curves.easeInOut); return new FadeTransition(opacity: curve, child: child); } }
LandingPage文件
import 'package:flutter/material.dart'; import 'package:travel/animated_background.dart'; import 'package:travel/animated_locations_text.dart'; import 'package:travel/animated_text.dart'; import 'package:travel/spread_circles.dart'; import 'package:travel/stacked_circles.dart'; class LandingPage extends StatefulWidget { @override State createState() => new LandingPageState(); } class LandingPageState extends State<LandingPage> with TickerProviderStateMixin { AnimationController buttonAnimationController; Animation<AlignmentGeometry> buttonAlignment; Animation<double> buttonOpacity; @override void initState() { super.initState(); buttonAnimationController = new AnimationController( vsync: this, duration: new Duration(milliseconds: 1000)); buttonAlignment = new AlignmentTween( begin: new Alignment(0.0, 1.0), end: new Alignment(0.0, 0.95), ).animate(new CurvedAnimation( parent: buttonAnimationController, curve: new Interval(0.3, 0.9, curve: Curves.easeInOut),)); buttonOpacity = new Tween<double>( begin: 0.0, end: 1.0, ).animate(new CurvedAnimation( parent: buttonAnimationController, curve: new Interval(0.3, 1.0, curve: Curves.easeInOut),)); buttonAlignment.addListener(() { setState(() {}); }); buttonOpacity.addListener(() { setState(() {}); }); buttonAnimationController.forward(); } @override void dispose() { buttonAnimationController.dispose(); } @override Widget build(BuildContext context) { return new Scaffold( body: new Stack( children: <Widget>[ new AnimatedBackground(), _buildStackedCircles(), new SpreadCircles(), _buildButtomButtons(), _buildAnimatedText(), ], ), ); } Widget _buildAnimatedText() { final animatedTextDelay = 800; return new Align( alignment: new Alignment(-1.0, -0.75), child: new Padding( padding: const EdgeInsets.only(left: 15.0), child: new Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ new AnimatedText( "Find your perfect \nholiday home in", animatedTextDelay, durationInMilliseconds: 2500), new AnimatedLocationsText(animatedTextDelay + 2500), ], ), ) ); } Widget _buildStackedCircles() { final circleDiameter = 25.0; return new Align( alignment: new Alignment(0.0, -0.9), child: new Hero( tag: "CircleHeroTag", child: new StackedCircles(circleDiameter), ), ); } Widget _buildButtomButtons() { return new AnimatedBuilder( animation: buttonAnimationController, child: new Container( padding: const EdgeInsets.only(left: 15.0, right: 15.0), child: new Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ _createAccountButton(), new Padding(padding: const EdgeInsets.only(bottom: 10.0)), _signInButton(), new Padding(padding: const EdgeInsets.only(bottom: 10.0)), _termsAndConditions(), ], ), ), builder: (BuildContext context, Widget child) { return new Align( alignment: buttonAlignment.value, child: new Opacity( opacity: buttonOpacity.value, child: child, ), ); } ); } Widget _createAccountButton() { return new GestureDetector( child: new Material( child: new Container( padding: const EdgeInsets.only(top: 10.0, bottom: 10.0), decoration: new BoxDecoration( borderRadius: new BorderRadius.circular(5.0), gradient: new LinearGradient( colors: <Color>[ Colors.green, Colors.greenAccent, ] ), ), alignment: Alignment.center, child: new Text("Create account", style: new TextStyle(color: Colors.white, fontSize: 16.0, fontWeight: FontWeight.w500),), ), ), onTap: () {}, ); } Widget _signInButton() { return new GestureDetector( child: new Material( child: new Container( padding: const EdgeInsets.only(top: 10.0, bottom: 10.0), decoration: new BoxDecoration( borderRadius: new BorderRadius.circular(5.0), gradient: new LinearGradient( colors: <Color>[ Colors.grey.withAlpha(150), Colors.grey.withAlpha(100), ] ), ), alignment: Alignment.center, child: new Text("Sign in", style: new TextStyle(color: Colors.black54, fontSize: 16.0, fontWeight: FontWeight.w500),), ), ), onTap: () {}, ); } Widget _termsAndConditions() { final textStyle = new TextStyle(fontSize: 13.0, color: Colors.black38); return new RichText( textAlign: TextAlign.center, text: new TextSpan( text: "By signing up to our services you indicate that you have read and agree to our ", style: textStyle, children: <TextSpan>[ new TextSpan(text: "terms and conditions", style: new TextStyle(decoration: TextDecoration.underline)), ], ), ); } }
Circle文件
import 'package:flutter/material.dart'; import 'package:meta/meta.dart'; class Circle extends StatelessWidget { final Color color; final double diameter; final Offset center; Circle({@required this.color, @required this.diameter, this.center}); @override Widget build(BuildContext context) { return new CustomPaint( size: new Size(diameter, diameter), painter: new CirclePainter(color, center: center), ); } } class CirclePainter extends CustomPainter { final Color color; final Offset center; CirclePainter(this.color, {this.center}); @override void paint(Canvas canvas, Size size) { canvas.drawCircle( center ?? new Offset(size.width / 2, size.height / 2), size.width / 2, new Paint()..color = this.color); } @override bool shouldRepaint(CirclePainter oldDelegate) => true; }
AnimatedText文件
import 'dart:async'; import 'package:flutter/material.dart'; class AnimatedText extends StatefulWidget { final String text; final int delayInMilliseconds; final int durationInMilliseconds; final TextStyle textStyle; AnimatedText(this.text, this.delayInMilliseconds, {this.durationInMilliseconds: 2500, this.textStyle}); @override createState() => new AnimatedTextState(text); } class AnimatedTextState extends State<AnimatedText> with SingleTickerProviderStateMixin { String currentText = ""; final String text; AnimationController animationController; List<int> textRunes; int curIndex = 0; AnimatedTextState(this.text) { textRunes = text.runes.toList(); } @override Future initState() async { animationController = new AnimationController( vsync: this, value: 0.0, lowerBound: 0.0, upperBound: textRunes.length.toDouble(), duration: new Duration( milliseconds: widget.durationInMilliseconds)); animationController.addListener(() { if (animationController.value.toInt() == 0) { setState(() { currentText = new String.fromCharCode(textRunes[0]); }); } else if (animationController.value.toInt() > curIndex && animationController.value.toInt() < textRunes.length) { setState(() { curIndex = animationController.value.toInt(); currentText += new String.fromCharCode(textRunes[curIndex]); }); } }); await new Future.delayed(new Duration(milliseconds: widget.delayInMilliseconds)); animationController.forward(); } @override void dispose() { animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return new Text(currentText, textAlign: TextAlign.left, style: widget.textStyle ?? new TextStyle(fontWeight: FontWeight.w600, fontSize: 28.0),); } }
AnimatedLocationsText文件
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:travel/animated_text.dart'; enum AnimationType { Character, SlideDown } class AnimatedLocationsText extends StatefulWidget { final int delayInMilliseconds; AnimatedLocationsText(this.delayInMilliseconds); @override State createState() => new _AnimationState(); } class _AnimationState extends State<AnimatedLocationsText> with SingleTickerProviderStateMixin { AnimationController animationController; Animation<Alignment> londonSlideOut; Animation<double> londonFadeOut; Animation<Alignment> newYorkSlideIn; Animation<double> newYorkFadeIn; Animation<Alignment> newYorkSlideOut; Animation<double> newYorkFadeOut; Animation<Alignment> losAngelesSlideIn; Animation<double> losAngelesFadeIn; String firstLocation = "London"; String secondLocation = "New York"; String thirdLocation = "Los Angeles"; @override void initState() { super.initState(); animationController = new AnimationController(vsync: this, duration: new Duration(seconds: 6)); londonSlideOut = new AlignmentTween( begin: new Alignment(-1.0, 0.0), end: new Alignment(-1.0, 1.0)) .animate(new CurvedAnimation(parent: animationController, curve: new Interval(0.4, 0.45, curve: Curves.easeIn))); londonFadeOut = new Tween<double>(begin: 1.0, end: 0.0) .animate(new CurvedAnimation(parent: animationController, curve: new Interval(0.42, 0.45, curve: Curves.easeIn))); newYorkSlideIn = new AlignmentTween( begin: new Alignment(-1.0, -1.0), end: new Alignment(-1.0, 0.0), ).animate(new CurvedAnimation(parent: animationController, curve: new Interval(0.42, 0.45, curve: Curves.easeIn))); newYorkFadeIn = new Tween<double>(begin: 0.0, end: 1.0) .animate(new CurvedAnimation(parent: animationController, curve: new Interval(0.42, 0.45))); newYorkSlideOut = new AlignmentTween( begin: new Alignment(-1.0, 0.0), end: new Alignment(-1.0, 1.0)) .animate(new CurvedAnimation(parent: animationController, curve: new Interval(0.8, 0.85, curve: Curves.easeIn))); newYorkFadeOut = new Tween<double>(begin: 1.0, end: 0.0) .animate(new CurvedAnimation(parent: animationController, curve: new Interval(0.82, 0.85))); losAngelesSlideIn = new AlignmentTween( begin: new Alignment(-1.0, -1.0), end: new Alignment(-1.0, 0.0)) .animate(new CurvedAnimation(parent: animationController, curve: new Interval(0.8, 0.85, curve: Curves.easeIn))); losAngelesFadeIn = new Tween<double>(begin: 0.0, end: 1.0) .animate(new CurvedAnimation(parent: animationController, curve: new Interval(0.82, 0.85))); londonSlideOut.addListener(() { setState(() {}); }); londonFadeOut.addListener(() { setState(() {}); }); newYorkSlideIn.addListener(() { setState(() {}); }); newYorkFadeIn.addListener(() { setState(() {}); }); newYorkSlideOut.addListener(() { setState(() {}); }); newYorkFadeOut.addListener(() { setState(() {}); }); losAngelesSlideIn.addListener(() { setState(() {}); }); losAngelesFadeIn.addListener(() { setState(() {}); }); new Future.delayed( new Duration(milliseconds: widget.delayInMilliseconds + 500)) .then((_) { animationController.forward(); }); } @override void dispose() { animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return new SizedBox( height: 40.0, child: new Stack( children: <Widget>[ // London new Align( alignment: londonSlideOut.value, child: new Opacity( opacity: londonFadeOut.value, child: new AnimatedText("London", widget.delayInMilliseconds, durationInMilliseconds: 500, textStyle: new TextStyle(color: Colors.green, fontSize: 24.0, fontWeight: FontWeight.w500),), ), ), // New York new AlignTransition( alignment: !(newYorkSlideIn.value.y == 0.0) ? newYorkSlideIn : newYorkSlideOut, child: new Opacity( opacity: !(newYorkFadeIn.value == 1.0) ? newYorkFadeIn.value : newYorkFadeOut.value, child: new Text(secondLocation, style: new TextStyle( color: Colors.lightBlue.withOpacity(0.7), fontSize: 24.0, fontWeight: FontWeight.w500), ), ), ), // Los Angeles new Align( alignment: losAngelesSlideIn.value, child: new Opacity( opacity: losAngelesFadeIn.value, child: new Text(thirdLocation, style: new TextStyle( color: Colors.purpleAccent, fontSize: 24.0, fontWeight: FontWeight.w500),), ), ), ], ), ); } }
AnimatedBackground文件
import 'package:flutter/material.dart'; class AnimatedBackground extends StatefulWidget { @override State createState() => new AnimatedBackgroundState(); } class AnimatedBackgroundState extends State<AnimatedBackground> with SingleTickerProviderStateMixin { AnimationController animationController; Animation<double> imageSizeAnimation; Animation<double> imageSlideAnimation; @override void initState() { super.initState(); animationController = new AnimationController( vsync: this, duration: new Duration(milliseconds: 6000)); imageSizeAnimation = new Tween<double>(begin: 0.0, end: 1.0).animate( new CurvedAnimation( parent: animationController, curve: new Interval(0.0, 0.1, curve: Curves.bounceInOut))); imageSlideAnimation = new Tween<double>(begin: 0.0, end: 0.5).animate( new CurvedAnimation(parent: animationController, curve: new Interval(0.1, 1.0, curve: Curves.linear))); imageSlideAnimation.addListener(() { setState(() {}); }); animationController.forward(); } @override void dispose() { animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return new Container( child: new Align( alignment: new Alignment(1.0, -0.3), child: new ClipPath( child: new Image( alignment: new Alignment(imageSlideAnimation.value, 0.0), width: 250.0 * imageSizeAnimation.value, height: 350.0 * imageSizeAnimation.value, image: new AssetImage("assets/bar.jpg"), fit: BoxFit.cover), clipper: new _AnimatedBackgroundImageClipper(), ), ), ); } } class _AnimatedBackgroundImageClipper extends CustomClipper<Path> { @override Path getClip(Size size) { Offset ctrl; Offset end; Path path = new Path(); path.moveTo(size.width, 0.0); path.lineTo(0.2 * size.width, size.height * 0.5 - 20); ctrl = new Offset(-0.35 * size.width, size.height); end = new Offset(0.6 * size.width, size.height * 0.95); path.quadraticBezierTo(ctrl.dx, ctrl.dy, end.dx, end.dy); path.lineTo(0.6 * size.width, size.height * 0.95); ctrl = new Offset(0.7 * size.width, size.height - 20); end = new Offset(0.8 * size.width, size.height * 0.9); path.quadraticBezierTo(ctrl.dx, ctrl.dy, end.dx, end.dy); path.lineTo(0.8 * size.width, size.height * 0.9); ctrl = new Offset(0.9 * size.width, size.height * 0.9 - 15); end = new Offset(size.width, size.height * 0.8); path.quadraticBezierTo(ctrl.dx, ctrl.dy, end.dx, end.dy); path.lineTo(size.width, size.height * 0.8); path.close(); return path; } @override bool shouldReclip(CustomClipper<Path> oldClipper) => true; }
SplashPage文件
import 'dart:async'; import 'package:flutter/scheduler.dart' show timeDilation; import 'package:flutter/material.dart'; import 'package:travel/circles.dart'; class SplashPage extends StatefulWidget { @override State createState() => new SplashPageState(); } class SplashPageState extends State<SplashPage> with SingleTickerProviderStateMixin { final circleDiameter = 50.0; AnimationController controller1; Animation<double> translation1; Animation<double> scale1; Animation<double> translation1Reverse; Animation<double> scale1Reverse; Animation<double> translation2; Animation<double> scale2; Animation<double> translation2Reverse; Animation<double> scale2Reverse; Animation<double> translation3; Animation<double> scale3; Animation<double> translation3Reverse; Animation<double> scale3Reverse; Animation<double> translation4; double currentTranslation = 0.0; double currentScale = 1.0; @override void initState() { super.initState(); controller1 = new AnimationController(vsync: this, duration: const Duration(seconds: 7)); translation1 = new Tween<double>(begin: 50.0, end: -20.0).animate( new CurvedAnimation(parent: controller1, curve: new Interval(0.0, 0.14, curve: Curves.easeInOut))); scale1 = new Tween<double>(begin: 0.8, end: 0.4).animate( new CurvedAnimation(parent: controller1, curve: new Interval(0.0, 0.14, curve: Curves.linear))); translation1Reverse = new Tween<double>(begin: -20.0, end: 55.0).animate( new CurvedAnimation(parent: controller1, curve: new Interval(0.14, 0.28, curve: Curves.easeInOut))); scale1Reverse = new Tween<double>(begin: 0.4, end: 0.85).animate( new CurvedAnimation(parent: controller1, curve: new Interval(0.14, 0.28, curve: Curves.linear))); translation2 = new Tween<double>(begin: 55.0, end: -20.0).animate( new CurvedAnimation(parent: controller1, curve: new Interval(0.28, 0.42, curve: Curves.easeInOut))); scale2 = new Tween<double>(begin: 0.85, end: 0.4).animate( new CurvedAnimation(parent: controller1, curve: new Interval(0.28, 0.42, curve: Curves.linear))); translation2Reverse = new Tween<double>(begin: -20.0, end: 60.0).animate( new CurvedAnimation(parent: controller1, curve: new Interval(0.42, 0.57, curve: Curves.easeInOut))); scale2Reverse = new Tween<double>(begin: 0.4, end: 0.9).animate( new CurvedAnimation(parent: controller1, curve: new Interval(0.42, 0.57, curve: Curves.linear))); translation3 = new Tween<double>(begin: 60.0, end: -20.0).animate( new CurvedAnimation(parent: controller1, curve: new Interval(0.57, 0.71, curve: Curves.easeInOut))); scale3 = new Tween<double>(begin: 0.9, end: 0.4).animate( new CurvedAnimation(parent: controller1, curve: new Interval(0.57, 0.71, curve: Curves.linear))); translation3Reverse = new Tween<double>(begin: -20.0, end: 65.0).animate( new CurvedAnimation(parent: controller1, curve: new Interval(0.71, 0.85, curve: Curves.easeInOut))); scale3Reverse = new Tween<double>(begin: 0.4, end: 1.0).animate( new CurvedAnimation(parent: controller1, curve: new Interval(0.71, 0.85, curve: Curves.linear))); translation4 = new Tween<double>(begin: 65.0, end: 15.0).animate( new CurvedAnimation(parent: controller1, curve: new Interval(0.85, 1.0, curve: Curves.linear))); translation1.addListener(() { setCurrentTranslation(translation1); }); scale1.addListener(() { setCurrentScale(scale1); }); translation1Reverse.addListener(() { if (translation1.value == -20) { setCurrentTranslation(translation1Reverse); } }); scale1Reverse.addListener(() { if (scale1.value == 0.4) { setCurrentScale(scale1Reverse); } }); translation2.addListener(() { if (translation1Reverse.value == 55) { setCurrentTranslation(translation2); } }); scale2.addListener(() { if (scale1Reverse.value == 0.85) { setCurrentScale(scale2); } }); translation2Reverse.addListener(() { if (translation2.value == -20) { setCurrentTranslation(translation2Reverse); } }); scale2Reverse.addListener(() { if (scale2.value == 0.4) { setCurrentScale(scale2Reverse); } }); translation3.addListener(() { if (translation2Reverse.value == 60) { setCurrentTranslation(translation3); } }); scale3.addListener(() { if (scale2Reverse.value == 0.9) { setCurrentScale(scale3); } }); translation3Reverse.addListener(() { if (translation3.value == -20) { setCurrentTranslation(translation3Reverse); } }); scale3Reverse.addListener(() { if (scale3.value == 0.4) { setCurrentScale(scale3Reverse); } }); translation4.addListener(() { if (translation3Reverse.value == 65) { setCurrentTranslation(translation4); } }); controller1.addStatusListener((status) { if (status == AnimationStatus.completed) { showLandingPage(); } }); controller1.forward(); } void setCurrentTranslation(Animation<double> animation) { setState(() { currentTranslation = animation.value; }); } void setCurrentScale(Animation<double> scaleAnimation) { setState(() { currentScale = scaleAnimation.value; }); } @override void dispose() { controller1.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final matrix1 = new Matrix4.translationValues( -1 * currentTranslation, 0.0, 0.0); matrix1.scale(currentScale, currentScale); final matrix2 = new Matrix4.translationValues(currentTranslation, 0.0, 0.0); matrix2.scale(currentScale, currentScale); return new Scaffold( body: new Center( child: new Hero( tag: "CircleHeroTag", child: new Stack( alignment: Alignment.center, children: <Widget>[ new Transform( transform: matrix1, child: new Circle( color: Colors.purple, diameter: circleDiameter), ), new Transform( transform: matrix2, child: new Opacity( opacity: 0.8, child: new Circle( color: Colors.yellow, diameter: circleDiameter), ), ) ], ), ), ), ); } void showLandingPage() { new Future.delayed(new Duration(milliseconds: 500)).then((_) => Navigator.of(context).pushNamed("/landing")); } }
SpreadCircles文件
import 'package:flutter/material.dart'; import 'package:travel/circles.dart'; class SpreadCircles extends StatefulWidget { @override State createState() => new SpreadCirclesState(); } class SpreadCirclesState extends State<SpreadCircles> with SingleTickerProviderStateMixin { AnimationController animationController; Animation<double> purpleCircleDiameter; Animation<double> yellowCircleDiameter; Animation<double> greenCircleDiameter; @override void initState() { super.initState(); animationController = new AnimationController( vsync: this, duration: new Duration(milliseconds: 800)); purpleCircleDiameter = new Tween<double>(begin: 0.0, end: 130.0).animate( new CurvedAnimation(parent: animationController, curve: new Interval(0.0, 0.6, curve: Curves.bounceInOut))); yellowCircleDiameter = new Tween<double>(begin: 0.0, end: 80.0).animate( new CurvedAnimation(parent: animationController, curve: new Interval(0.3, 0.7, curve: Curves.bounceInOut))); greenCircleDiameter = new Tween<double>(begin: 0.0, end: 60.0).animate( new CurvedAnimation(parent: animationController, curve: new Interval(0.6, 1.0, curve: Curves.easeIn))); purpleCircleDiameter.addListener(() { setState(() {}); }); greenCircleDiameter.addListener(() { setState(() {}); }); yellowCircleDiameter.addListener(() { setState(() {}); }); animationController.forward(); } @override void dispose() { animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return new Container( color: Colors.transparent, child: new Stack( children: <Widget>[ new Align( // light green circle on the left border alignment: new Alignment(-1.0, -0.05), child: new Circle( color: Colors.grey.withGreen(190).withOpacity(0.5), diameter: greenCircleDiameter.value, center: new Offset(10.0, 25.0), ), ), new Align( // purple circle on the right border alignment: new Alignment(1.0, 0.24), child: new Circle( color: Colors.purple.withOpacity(0.8), diameter: purpleCircleDiameter.value, center: new Offset(95.0, 75.0), ), ), new Align( // yellow circle at the top alignment: new Alignment(0.6, -0.85), child: new Circle( color: Colors.yellow.withOpacity(0.8), diameter: yellowCircleDiameter.value ), ), ], ), ); } }
StackedCircles文件
import 'package:flutter/material.dart'; import 'package:travel/circles.dart'; class StackedCircles extends StatelessWidget { final double circleDiameter; final Matrix4 circle1Transform; final Matrix4 circle2Transform; StackedCircles(this.circleDiameter, {this.circle1Transform, this.circle2Transform}); @override Widget build(BuildContext context) { return new SizedBox( width: circleDiameter * 2, height: circleDiameter, child: new Stack( children: <Widget>[ new Align( alignment: new Alignment(-0.5, 0.0), child: new Circle(color: Colors.purple, diameter: circleDiameter), ), new Align( alignment: new Alignment(0.5, 0.0), child: new Opacity( opacity: 0.8, child: new Circle(color: Colors.yellow, diameter: circleDiameter), ), ), ], ), ); } } class StackedCirclesAnimation extends StatelessWidget { final Animation<double> controller; final Animation<double> diameter; final Animation<Alignment> alignment, alignment1; StackedCirclesAnimation({Key key, this.controller}) : diameter = new Tween<double>( end: 25.0, begin: 30.0 ).animate(new CurvedAnimation(parent: controller, curve: new Interval(0.0, 1.0, curve: Curves.ease))), alignment = new AlignmentTween( begin: new Alignment(-0.5, 0.0), end: new Alignment(0.2, 0.0), ).animate(new CurvedAnimation(parent: controller, curve: new Interval(0.0, 1.0, curve: Curves.ease))), alignment1 = new AlignmentTween( begin: new Alignment(0.5, 0.0), end: new Alignment(-0.2, 0.0), ).animate(new CurvedAnimation(parent: controller, curve: new Interval(0.0, 1.0, curve: Curves.ease))), super(key: key); Widget _buildAnimation(BuildContext context, Widget child) { return new Stack( children: <Widget>[ new Align( alignment: alignment.value, child: new Circle(color: Colors.purple, diameter: diameter.value), ), new Align( alignment: alignment1.value, child: new Opacity( opacity: 0.8, child: new Circle(color: Colors.yellow, diameter: diameter.value), ), ), ], ); } @override Widget build(BuildContext context) { return new AnimatedBuilder( builder: _buildAnimation, animation: controller, ); } }
总结
今天就分享到这里,代码大家可以直接新建一个项目,然后复制进去运行。
谢谢观看技术刚刚好的文章,技术刚刚好是个人维护,每天至少更新一篇Flutter技术文章,实时为大家播报Flutter最新消息。如果你刚好也在关注Flutter这门技术,那就跟我一起学习进步吧,你的赞,收藏,转发是对我个人最大的支持,维护不易,欢迎关注。
本文暂时没有评论,来添加一个吧(●'◡'●)