import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'dart:async'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp(home: Scaffold(body: App())); } } class App extends StatefulWidget { @override AppState createState() => AppState(); } class AppState extends State with SingleTickerProviderStateMixin { AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController( duration: Duration(seconds: 10), vsync: this, ); } @override void dispose() { _controller?.dispose(); super.dispose(); } Future _startAnim() async { try { await _controller.forward().orCancel; // await _controller.reverse().orCancel; } on TickerCanceled { print('welp we fucked'); } } @override Widget build(BuildContext context) { return Material( child: GestureDetector( onTap: () { _startAnim(); }, child: Stack( fit: StackFit.expand, children: [ Image.network( 'https://wallpaperplay.com/walls/full/e/5/3/13586.jpg', fit: BoxFit.cover, ), ColorFiltered( colorFilter: ColorFilter.mode(Colors.black.withOpacity(0.8), BlendMode.srcOut), // This one will create the magic child: Stack( fit: StackFit.expand, children: [ Container( decoration: BoxDecoration( color: Colors.black, backgroundBlendMode: BlendMode.dstOut), // This one will handle background + difference out ), MovingHole(controller: _controller) ], ), ), ], ), )); } } class MovingHole extends StatelessWidget { // MovingHole({Key key, this.controller}) // : width = TweenSequence( // >[ // TweenSequenceItem( // tween: Tween(begin: 100, end: 150) // .chain(CurveTween(curve: Curves.ease)), // weight: 33.0, // ), // TweenSequenceItem( // tween: Tween(begin: 150, end: 200), // weight: 33.0, // ), // TweenSequenceItem( // tween: Tween(begin: 200, end: 300) // .chain(CurveTween(curve: Curves.ease)), // weight: 33.0, // ), // ], // ).animate(CurvedAnimation( // parent: controller, curve: Interval(0.0, 1, curve: Curves.linear))), // height = TweenSequence( // >[ // TweenSequenceItem( // tween: Tween(begin: 100, end: 150) // .chain(CurveTween(curve: Curves.ease)), // weight: 40.0, // ), // TweenSequenceItem( // tween: Tween(begin: 150, end: 200), // weight: 20.0, // ), // TweenSequenceItem( // tween: Tween(begin: 200, end: 300) // .chain(CurveTween(curve: Curves.ease)), // weight: 40.0, // ), // ], // ).animate(CurvedAnimation( // parent: controller, // curve: Interval(0.0, 1, curve: Curves.fastOutSlowIn))), // radius = BorderRadiusTween( // begin: BorderRadius.circular(0.0), // end: BorderRadius.circular(100.0)) // .animate(CurvedAnimation( // parent: controller, // curve: Interval( // 0.0, // 1, // curve: Curves.ease, // ))), // alignment = AlignmentTween( // begin: Alignment.bottomCenter, end: Alignment.topCenter) // .animate(CurvedAnimation( // parent: controller, // curve: Interval(0.0, 1, curve: Curves.ease))), MovingHole({Key key, this.controller}) : scale1 = Tween(begin: 1.0, end: 0.5).animate( CurvedAnimation( parent: controller, curve: const Interval(0.05, 0.075, curve: Curves.easeOut), ), ), scale2 = Tween(begin: 0.5, end: 1.2).animate( CurvedAnimation( parent: controller, curve: const Interval( 0.5, 0.55, curve: Curves.ease, ), ), ), move1 = Tween(begin: Offset(0, 20), end: Offset(-20, 100)).animate(CurvedAnimation( parent: controller, curve: const Interval( 0.0, 0.1, curve: Curves.ease, ), )), move2 = Tween(begin: Offset(50, 100), end: Offset(30, 20)).animate(CurvedAnimation( parent: controller, curve: const Interval( 0.9, 1.0, curve: Curves.ease, ), )), super(key: key); final AnimationController controller; final Animation scale1; final Animation scale2; final Animation move1; final Animation move2; final Color color = Colors.black; // final Size size; @override Widget build(BuildContext context) { return AnimatedBuilder( animation: controller, builder: (BuildContext context, Widget child) { return Container( child: Transform( alignment: Alignment.center, transform: Matrix4.identity() ..scale(scale1.value) ..scale(scale2.value) ..translate( move1.value.dx, move1.value.dy, ) ..translate( move2.value.dx, move2.value.dy, ), // child: Opacity( // opacity: opacityAnimation.value, child: Container( width: 60.0, height: 60.0, decoration: BoxDecoration( shape: BoxShape.circle, color: color, border: Border.all(color: color, style: BorderStyle.solid, width: 4.0 - (2 * controller.value))), )), // ), ); }); } // Widget build(BuildContext context) { // return AnimatedBuilder( // animation: controller, // builder: (BuildContext context, Widget child) { // return Container( // // alignment: alignment.value, // child: Container( // width: size.value.width, // height: size.value.height, // decoration: BoxDecoration( // color: Colors.red, borderRadius: BorderRadius.all(Radius.circular(20))), // )); // }); // } }