I'm working on a Flutter application and trying to implement a custom animation that simulates an hourglass effect. I've attached a GIF below to demonstrate the desired animation:
Here's the relevant portion of my code where I define my CustomTimePainter and the main screen setup:
import 'package:flutter/material.dart';
import 'dart:math' as math;
class CustomTimePainter extends CustomPainter {
CustomTimePainter({
required this.animation,
required this.backgroundColor,
required this.color,
}) : super(repaint: animation);
final Animation<double> animation;
final Color backgroundColor, color;
@override
void paint(Canvas canvas, Size size) {
var paint = Paint()
..color = const Color.fromARGB(255, 204, 255, 86);
canvas.drawPaint(paint);
var paintHourglassAnimation = Paint()
..color = Colors.blue;
canvas.drawPaint(paintHourglassAnimation);
}
@override
bool shouldRepaint(CustomTimePainter old) {
return animation.value != old.animation.value ||
color != old.color ||
backgroundColor != old.backgroundColor;
}
}
This is my main screen:
class MainScreen extends StatefulWidget {
const MainScreen({Key? key}) : super(key: key);
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> with TickerProviderStateMixin {
final CountDownController _countDownController = Get.find();
final ProjectsController _projectsController = Get.find();
final SettingsController _settingsController = Get.find();
@override
void initState() {
super.initState();
_countDownController.createAnimationController(this);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white10,
body: Center(
child: AnimatedBuilder(
animation: _countDownController.controller,
builder: (context, child) {
return Stack(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
flex: 50,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Align(
alignment: FractionalOffset.center,
child: AspectRatio(
aspectRatio: 1.0,
child: Stack(
children: <Widget>[
Positioned.fill(
child: CustomPaint(
painter:
_countDownController.painter),
),
Align(
alignment: FractionalOffset.center,
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
Obx(
() => Text(
_countDownController
.timerString.value,
style: const TextStyle(
fontSize: 80.0,
color: Colors.white),
),
),
],
),
),
],
),
),
),
),
),
]
I'm using a CustomPainter
to try to achieve the animation shown in the GIF. I want to start a timer and have the animation fade in and out like an hourglass timer. However, I'm struggling with implementing the dart:math
package to program the animation.
Specifically, I don't know how to animate the var paintHourglassAnimation
to achieve the hourglass transition shown in the video.
Can anyone help guide me on how to use the dart:math
package for this animation, or suggest a better approach to achieve the desired effect?
You can draw paint like
import 'dart:ui' as ui;
class CustomTimePainter extends CustomPainter {
CustomTimePainter({
required this.animation,
required this.backgroundColor,
required this.color,
}) : super(repaint: animation);
final Animation<double> animation;
final Color backgroundColor, color;
@override
void paint(Canvas canvas, Size size) {
var paint = Paint()..color = backgroundColor;
canvas.drawPaint(paint);
final top = ui.lerpDouble(0, size.height, animation.value)!;
Rect rect = Rect.fromLTRB(0, top, size.width, size.height);
Path path = Path()..addRect(rect);
canvas.drawPath(path, paint..color = color);
}
@override
bool shouldRepaint(CustomTimePainter old) {
return animation.value != old.animation.value ||
color != old.color ||
backgroundColor != old.backgroundColor;
}
}
And example output
class ANTest extends StatefulWidget {
const ANTest({super.key});
@override
State<ANTest> createState() => _ANTestState();
}
class _ANTestState extends State<ANTest> with SingleTickerProviderStateMixin {
late AnimationController container =
AnimationController(vsync: this, duration: Duration(seconds: 3))
..repeat();
late Animation<double> animation =
Tween<double>(begin: 0, end: 1).animate(container);
@override
Widget build(BuildContext context) {
return Scaffold(
body: LayoutBuilder(
builder: (_, constraints) => CustomPaint(
size: constraints.biggest,
painter: CustomTimePainter(
color: Colors.amber,
backgroundColor: Colors.black,
animation: animation,
),
),
),
);
}
}