Last active
January 24, 2024 04:04
-
-
Save arpit/2474f7704984353c39dc6dbdfe034d9b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'dart:math' as math; | |
class DotPainter extends CustomPainter { | |
final int dotCount; | |
double outerDotsPositionAngle = 51.42; | |
final Color color1; | |
final Color color2; | |
final Color color3; | |
final Color color4; | |
double centerX = 0.0; | |
double centerY = 0.0; | |
final List<Paint> circlePaints = List<Paint>.filled( | |
4, Paint() | |
); | |
double maxOuterDotsRadius = 0.0; | |
double maxInnerDotsRadius = 0.0; | |
double maxDotSize; | |
final currentProgress; | |
double currentRadius1 = 0.0; | |
double currentDotSize1 = 0.0; | |
double currentDotSize2 = 0.0; | |
double currentRadius2 = 0.0; | |
bool isFirst = true; | |
DotPainter({ | |
required this.currentProgress, | |
this.dotCount = 7, | |
this.maxDotSize = 1, | |
this.color1 = const Color(0xFFFFC107), | |
this.color2 = const Color(0xFFFF9800), | |
this.color3 = const Color(0xFFFF5722), | |
this.color4 = const Color(0xFFF44336), | |
}) { | |
outerDotsPositionAngle = 360.0 / dotCount; | |
for (int i = 0; i < circlePaints.length; i++) { | |
circlePaints[i] = Paint()..style = PaintingStyle.fill; | |
} | |
} | |
@override | |
void paint(Canvas canvas, Size size) { | |
if (isFirst) { | |
centerX = size.width * 0.5; | |
centerY = size.height * 0.5; | |
maxDotSize = size.width * 0.05; | |
maxOuterDotsRadius = size.width * 0.5 - maxDotSize * 2; | |
maxInnerDotsRadius = 0.8 * maxOuterDotsRadius; | |
isFirst = false; | |
} | |
_updateOuterDotsPosition(); | |
_updateInnerDotsPosition(); | |
_updateDotsPaints(); | |
_drawOuterDotsFrame(canvas); | |
_drawInnerDotsFrame(canvas); | |
} | |
void _drawOuterDotsFrame(Canvas canvas) { | |
for (int i = 0; i < dotCount; i++) { | |
double cX = centerX + | |
currentRadius1 * math.cos(i * degToRad(outerDotsPositionAngle)); | |
double cY = centerY + | |
currentRadius1 * math.sin(i * degToRad(outerDotsPositionAngle)); | |
canvas.drawCircle(Offset(cX, cY), currentDotSize1, | |
circlePaints[i % circlePaints.length]); | |
} | |
} | |
void _drawInnerDotsFrame(Canvas canvas) { | |
for (int i = 0; i < dotCount; i++) { | |
double cX = centerX + | |
currentRadius2 * | |
math.cos((i * degToRad(outerDotsPositionAngle - 10))); | |
double cY = centerY + | |
currentRadius2 * | |
math.sin((i * degToRad(outerDotsPositionAngle - 10))); | |
canvas.drawCircle(Offset(cX, cY), currentDotSize2, | |
circlePaints[(i + 1) % circlePaints.length]); | |
} | |
} | |
void _updateOuterDotsPosition() { | |
if (currentProgress < 0.3) { | |
currentRadius1 = mapValueFromRangeToRange( | |
currentProgress, 0.0, 0.3, 0.0, maxOuterDotsRadius * 0.8); | |
} else { | |
currentRadius1 = mapValueFromRangeToRange(currentProgress, 0.3, 1.0, | |
0.8 * maxOuterDotsRadius, maxOuterDotsRadius); | |
} | |
if (currentProgress == 0) { | |
currentDotSize1 = 0; | |
} else if (currentProgress < 0.7) { | |
currentDotSize1 = maxDotSize; | |
} else { | |
currentDotSize1 = | |
mapValueFromRangeToRange(currentProgress, 0.7, 1.0, maxDotSize, 0.0); | |
} | |
} | |
void _updateInnerDotsPosition() { | |
if (currentProgress < 0.3) { | |
currentRadius2 = mapValueFromRangeToRange( | |
currentProgress, 0.0, 0.3, 0.0, maxInnerDotsRadius); | |
} else { | |
currentRadius2 = maxInnerDotsRadius; | |
} | |
if (currentProgress == 0) { | |
currentDotSize2 = 0; | |
} else if (currentProgress < 0.2) { | |
currentDotSize2 = maxDotSize; | |
} else if (currentProgress < 0.5) { | |
currentDotSize2 = mapValueFromRangeToRange( | |
currentProgress, 0.2, 0.5, maxDotSize, 0.3 * maxDotSize); | |
} else { | |
currentDotSize2 = mapValueFromRangeToRange( | |
currentProgress, 0.5, 1.0, maxDotSize * 0.3, 0.0); | |
} | |
} | |
void _updateDotsPaints() { | |
double progress = clamp(currentProgress, 0.6, 1.0); | |
int alpha = | |
mapValueFromRangeToRange(progress, 0.6, 1.0, 255.0, 0.0).toInt(); | |
if (currentProgress < 0.5) { | |
double progress = | |
mapValueFromRangeToRange(currentProgress, 0.0, 0.5, 0.0, 1.0); | |
circlePaints[0] | |
.color = Color.lerp(color1, color2, progress)!.withAlpha(alpha); | |
circlePaints[1] | |
.color = Color.lerp(color2, color3, progress)!.withAlpha(alpha); | |
circlePaints[2] | |
.color = Color.lerp(color3, color4, progress)!.withAlpha(alpha); | |
circlePaints[3] | |
.color = Color.lerp(color4, color1, progress)!.withAlpha(alpha); | |
} else { | |
double progress = | |
mapValueFromRangeToRange(currentProgress, 0.5, 1.0, 0.0, 1.0); | |
circlePaints[0] | |
.color = Color.lerp(color2, color3, progress)!.withAlpha(alpha); | |
circlePaints[1] | |
.color = Color.lerp(color3, color4, progress)!.withAlpha(alpha); | |
circlePaints[2] | |
.color = Color.lerp(color4, color1, progress)!.withAlpha(alpha); | |
circlePaints[3] | |
.color = Color.lerp(color1, color2, progress)!.withAlpha(alpha); | |
} | |
} | |
@override | |
bool shouldRepaint(CustomPainter oldDelegate) { | |
return true; | |
} | |
} | |
num degToRad(num deg) => deg * (math.pi / 180.0); | |
num radToDeg(num rad) => rad * (180.0 / math.pi); | |
double mapValueFromRangeToRange(double value, double fromLow, double fromHigh, double toLow, double toHigh) { | |
return toLow + ((value - fromLow) / (fromHigh - fromLow) * (toHigh - toLow)); | |
} | |
double clamp(double value, double low, double high) { | |
return math.min(math.max(value, low), high); | |
} | |
class CirclePainter extends CustomPainter { | |
Paint circlePaint = Paint(); | |
Paint maskPaint = Paint(); | |
final double outerCircleRadiusProgress; | |
final double innerCircleRadiusProgress; | |
final Color startColor; | |
final Color endColor; | |
CirclePainter({ | |
required this.outerCircleRadiusProgress, | |
required this.innerCircleRadiusProgress, | |
this.startColor = const Color(0xFFFF5722), | |
this.endColor = const Color(0xFFFFC107), | |
}) { | |
circlePaint.style = PaintingStyle.fill; | |
maskPaint.blendMode = BlendMode.clear; | |
} | |
@override | |
void paint(Canvas canvas, Size size) { | |
double center = size.width * 0.5; | |
_updateCircleColor(); | |
canvas.saveLayer(Offset.zero & size, Paint()); | |
canvas.drawCircle(Offset(center, center), | |
outerCircleRadiusProgress * center, circlePaint); | |
canvas.drawCircle(Offset(center, center), | |
innerCircleRadiusProgress * center + 1, maskPaint); | |
canvas.restore(); | |
} | |
void _updateCircleColor() { | |
double colorProgress = clamp(outerCircleRadiusProgress, 0.5, 1.0); | |
colorProgress = mapValueFromRangeToRange(colorProgress, 0.5, 1.0, 0.0, 1.0); | |
circlePaint.color = | |
Color.lerp(startColor, endColor, colorProgress)!; | |
} | |
@override | |
bool shouldRepaint(CustomPainter oldDelegate) { | |
return true; | |
} | |
} | |
const Color darkBlue = Color.fromARGB(255, 18, 32, 47); | |
void main() { | |
runApp(MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
theme: ThemeData.dark().copyWith( | |
scaffoldBackgroundColor: darkBlue, | |
), | |
debugShowCheckedModeBanner: false, | |
home: Scaffold( | |
body: Container( | |
child: Center( | |
child: CustomPaint( | |
size: Size(200, 200), | |
painter: DotPainter(currentProgress: .5, dotCount: 3, color1: Colors.red, color2: Colors.blue, color3: Colors.green, color4: Colors.yellow), | |
//painter: CirclePainter(innerCircleRadiusProgress: 0.9, outerCircleRadiusProgress: 1, startColor: Colors.red, endColor: Colors.blue), | |
) | |
), | |
), | |
), | |
); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment