diff --git a/animations/lib/main.dart b/animations/lib/main.dart index f02d459db..dceb55403 100644 --- a/animations/lib/main.dart +++ b/animations/lib/main.dart @@ -4,7 +4,12 @@ import 'package:flutter/material.dart'; -import 'src/basics/animation_controller_demo.dart'; +import 'src/basics/01_animated_container.dart'; +import 'src/basics/02_page_route_builder.dart'; +import 'src/basics/03_animation_controller.dart'; +import 'src/basics/04_tweens.dart'; +import 'src/basics/05_custom_tween.dart'; +import 'src/basics/06_animated_builder.dart'; import 'src/misc/card_swipe.dart'; import 'src/misc/carousel.dart'; import 'src/misc/expand_card.dart'; @@ -22,8 +27,17 @@ class Demo { } final basicDemos = [ + Demo('AnimatedContainer', AnimatedContainerDemo.routeName, + (context) => AnimatedContainerDemo()), + Demo('PageRouteBuilder', PageRouteBuilderDemo.routeName, + (context) => PageRouteBuilderDemo()), Demo('Animation Controller', AnimationControllerDemo.routeName, (context) => AnimationControllerDemo()), + Demo('Tweens', TweenDemo.routeName, (context) => TweenDemo()), + Demo('Custom Tween', CustomTweenDemo.routeName, + (context) => CustomTweenDemo()), + Demo('AnimatedBuilder', AnimatedBuilderDemo.routeName, + (context) => AnimatedBuilderDemo()), ]; final miscDemos = [ diff --git a/animations/lib/src/basics/01_animated_container.dart b/animations/lib/src/basics/01_animated_container.dart new file mode 100644 index 000000000..e09b8a46c --- /dev/null +++ b/animations/lib/src/basics/01_animated_container.dart @@ -0,0 +1,69 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +const _duration = Duration(milliseconds: 400); + +double generateBorderRadius() => Random().nextDouble() * 64; +double generateMargin() => Random().nextDouble() * 64; +Color generateColor() => Color(0xFFFFFFFF & Random().nextInt(0xFFFFFFFF)); + +class AnimatedContainerDemo extends StatefulWidget { + static String routeName = '/basics/01_animated_container'; + + _AnimatedContainerDemoState createState() => _AnimatedContainerDemoState(); +} + +class _AnimatedContainerDemoState extends State { + Color color; + double borderRadius; + double margin; + + void initState() { + super.initState(); + color = Colors.deepPurple; + borderRadius = generateBorderRadius(); + margin = generateMargin(); + } + + void change() { + setState(() { + color = generateColor(); + borderRadius = generateBorderRadius(); + margin = generateMargin(); + }); + } + + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Center( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: SizedBox( + width: 128, + height: 128, + child: AnimatedContainer( + margin: EdgeInsets.all(margin), + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(borderRadius), + ), + duration: _duration, + ), + ), + ), + RaisedButton( + child: Text( + 'change', + ), + onPressed: () => change(), + ), + ], + ), + ), + ); + } +} diff --git a/animations/lib/src/basics/02_page_route_builder.dart b/animations/lib/src/basics/02_page_route_builder.dart new file mode 100644 index 000000000..1ee16a16a --- /dev/null +++ b/animations/lib/src/basics/02_page_route_builder.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +class PageRouteBuilderDemo extends StatelessWidget { + static const String routeName = '/basics/page_route_builder'; + + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Center( + child: RaisedButton( + child: Text('Go!'), + color: Theme.of(context).primaryColor, + onPressed: () { + Navigator.of(context).push(_createRoute()); + }, + ), + ), + ); + } +} + +Route _createRoute() { + return PageRouteBuilder( + pageBuilder: (context, animation, secondaryAnimation) => _Page2(), + transitionsBuilder: (context, animation, secondaryAnimation, child) { + var tween = Tween(begin: Offset(0.0, 1.0), end: Offset.zero); + var curveTween = CurveTween(curve: Curves.ease); + + return SlideTransition( + position: animation.drive(curveTween).drive(tween), + child: child, + ); + }, + ); +} + +class _Page2 extends StatelessWidget { + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Center( + child: Text('Page 2!', style: Theme.of(context).textTheme.display1), + ), + ); + } +} diff --git a/animations/lib/src/basics/animation_controller_demo.dart b/animations/lib/src/basics/03_animation_controller.dart similarity index 100% rename from animations/lib/src/basics/animation_controller_demo.dart rename to animations/lib/src/basics/03_animation_controller.dart index a3c69e933..c7c84c75a 100644 --- a/animations/lib/src/basics/animation_controller_demo.dart +++ b/animations/lib/src/basics/03_animation_controller.dart @@ -30,8 +30,8 @@ class _AnimationControllerDemoState extends State @override void dispose() { - super.dispose(); controller.dispose(); + super.dispose(); } @override diff --git a/animations/lib/src/basics/04_tweens.dart b/animations/lib/src/basics/04_tweens.dart new file mode 100644 index 000000000..191515990 --- /dev/null +++ b/animations/lib/src/basics/04_tweens.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; + +class TweenDemo extends StatefulWidget { + static const String routeName = '/basics/tweens'; + + _TweenDemoState createState() => _TweenDemoState(); +} + +class _TweenDemoState extends State + with SingleTickerProviderStateMixin { + static const Duration _duration = Duration(seconds: 1); + static const double accountBalance = 1000000; + AnimationController controller; + Animation animation; + + void initState() { + super.initState(); + + controller = AnimationController(vsync: this, duration: _duration) + ..addListener(() { + // Marks the widget tree as dirty + setState(() {}); + }); + animation = Tween(begin: 0.0, end: accountBalance).animate(controller); + } + + void dispose() { + controller.dispose(); + super.dispose(); + } + + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ConstrainedBox( + constraints: BoxConstraints(maxWidth: 200), + child: Text('\$${animation.value.toStringAsFixed(2)}', + style: TextStyle(fontSize: 24)), + ), + RaisedButton( + child: Text( + controller.status == AnimationStatus.completed + ? 'Buy a Mansion' + : 'Win Lottery', + ), + onPressed: () { + if (controller.status == AnimationStatus.completed) { + controller.reverse(); + } else { + controller.forward(); + } + }, + ) + ], + ), + ), + ); + } +} diff --git a/animations/lib/src/basics/05_custom_tween.dart b/animations/lib/src/basics/05_custom_tween.dart new file mode 100644 index 000000000..4e40c2589 --- /dev/null +++ b/animations/lib/src/basics/05_custom_tween.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.dart'; + +class TypewriterTween extends Tween { + TypewriterTween({String begin = '', String end}) + : super(begin: begin, end: end); + + String lerp(double t) { + var cutoff = (end.length * t).round(); + return end.substring(0, cutoff); + } +} + +class CustomTweenDemo extends StatefulWidget { + static const String routeName = '/basics/custom_tweens'; + + _CustomTweenDemoState createState() => _CustomTweenDemoState(); +} + +class _CustomTweenDemoState extends State + with SingleTickerProviderStateMixin { + static const Duration _duration = Duration(seconds: 3); + static const String message = loremIpsum; + AnimationController controller; + Animation animation; + + void initState() { + super.initState(); + + controller = AnimationController(vsync: this, duration: _duration) + ..addListener(() { + // Marks the widget tree as dirty + setState(() {}); + }); + animation = TypewriterTween(end: message).animate(controller); + } + + void dispose() { + controller.dispose(); + super.dispose(); + } + + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + actions: [ + RaisedButton( + child: Text( + controller.status == AnimationStatus.completed + ? 'Delete Essay' + : 'Write Essay', + ), + onPressed: () { + if (controller.status == AnimationStatus.completed) { + controller.reverse(); + } else { + controller.forward(); + } + }, + ), + ], + ), + body: SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: EdgeInsets.all(8.0), + child: Card( + child: Container( + padding: EdgeInsets.all(8.0), + child: Text('${animation.value}', + style: + TextStyle(fontSize: 16, fontFamily: 'SpecialElite')), + ), + ), + ), + ], + ), + ), + ); + } +} + +const String loremIpsum = ''' +Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium +doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore +veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim +ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia +consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque +porro quisquam est, qui dolorem ipsum, quia dolor sit amet consectetur +adipisci[ng] velit, sed quia non-numquam [do] eius modi tempora inci[di]dunt, ut +labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, +quis nostrum[d] exercitationem ullam corporis suscipit laboriosam, nisi ut +aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in +ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui +dolorem eum fugiat, quo voluptas nulla pariatur? +'''; diff --git a/animations/lib/src/basics/06_animated_builder.dart b/animations/lib/src/basics/06_animated_builder.dart new file mode 100644 index 000000000..13ce24d88 --- /dev/null +++ b/animations/lib/src/basics/06_animated_builder.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; + +class AnimatedBuilderDemo extends StatefulWidget { + static const String routeName = '/basics/animated_builder'; + + _AnimatedBuilderDemoState createState() => _AnimatedBuilderDemoState(); +} + +class _AnimatedBuilderDemoState extends State + with SingleTickerProviderStateMixin { + static const Color beginColor = Colors.deepPurple; + static const Color endColor = Colors.deepOrange; + Duration duration = Duration(milliseconds: 800); + AnimationController controller; + Animation animation; + + void initState() { + super.initState(); + + controller = AnimationController(vsync: this, duration: duration); + animation = + ColorTween(begin: beginColor, end: endColor).animate(controller); + } + + void dispose() { + controller.dispose(); + super.dispose(); + } + + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Center( + child: AnimatedBuilder( + animation: this.animation, + builder: (context, child) { + return MaterialButton( + color: animation.value, + child: child, + onPressed: () { + if (controller.status == AnimationStatus.completed) { + controller.reverse(); + } else { + controller.forward(); + } + }, + ); + }, + child: Text( + 'Change Color', + style: TextStyle(color: Colors.white), + ), + ), + ), + ); + } +} diff --git a/animations/lib/src/misc/expand_card.dart b/animations/lib/src/misc/expand_card.dart index c9c19bb03..bdd5d3cba 100644 --- a/animations/lib/src/misc/expand_card.dart +++ b/animations/lib/src/misc/expand_card.dart @@ -60,7 +60,7 @@ class _ExpandCardState extends State layoutBuilder: (topChild, topChildKey, bottomChild, bottomChildKey) { return Stack( - children: [ + children: [ Positioned.fill( key: bottomChildKey, child: bottomChild,