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; String param; double tm; @override void initState() { super.initState(); param = "beginning"; _controller = AnimationController( duration: Duration(milliseconds: 10000), vsync: this, )..addListener(() => contrVal()); } @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'); } } contrVal() { setState(() { tm = _controller.value; if (_controller.value >= 0.2) param = 'two seconds'; if (_controller.value >= 0.5) param = "five seconds"; if (_controller.value >= 1) param = "ten seconds"; }); } @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), ], ), ), Container( alignment: Alignment.bottomCenter, child: Text( "$param - $tm", style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold), )) ], ), )); } } class MovingHole extends StatelessWidget { double timeM(double scene) { int time = 10; return scene / time; } MovingHole({Key key, this.controller}) : scale1 = Tween(begin: 1.0, end: 0.5).animate( CurvedAnimation( parent: controller, curve: 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))), )), ); }); } } // создать переменную, листенером слить текущее значение, на поляне в стеке вывести. // сколько составляет 0.05 от 10 секунд