mirror of
https://github.com/immich-app/immich.git
synced 2025-05-24 01:12:58 -04:00
* chore(mobile): small visual fix and update * update * update * remove design placeholder
121 lines
3.3 KiB
Dart
121 lines
3.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
import 'package:immich_mobile/widgets/common/immich_logo.dart';
|
|
|
|
class ImmichLoadingIndicator extends HookWidget {
|
|
final double? borderRadius;
|
|
|
|
const ImmichLoadingIndicator({
|
|
super.key,
|
|
this.borderRadius,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final logoAnimationController = useAnimationController(
|
|
duration: const Duration(seconds: 6),
|
|
)
|
|
..reverse()
|
|
..repeat();
|
|
|
|
final borderAnimationController = useAnimationController(
|
|
duration: const Duration(seconds: 6),
|
|
)..repeat();
|
|
|
|
return Container(
|
|
height: 80,
|
|
width: 80,
|
|
decoration: BoxDecoration(
|
|
color: Colors.transparent,
|
|
borderRadius: BorderRadius.circular(borderRadius ?? 50),
|
|
backgroundBlendMode: BlendMode.luminosity,
|
|
),
|
|
child: AnimatedBuilder(
|
|
animation: borderAnimationController,
|
|
builder: (context, child) {
|
|
return CustomPaint(
|
|
painter: GradientBorderPainter(
|
|
animation: borderAnimationController.value,
|
|
strokeWidth: 3,
|
|
),
|
|
child: child,
|
|
);
|
|
},
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(15),
|
|
child: RotationTransition(
|
|
turns: logoAnimationController,
|
|
child: const ImmichLogo(
|
|
heroTag: 'logo',
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class GradientBorderPainter extends CustomPainter {
|
|
final double animation;
|
|
final double strokeWidth;
|
|
final double opacity = 0.7;
|
|
final colors = [
|
|
const Color(0xFFFA2921),
|
|
const Color(0xFFED79B5),
|
|
const Color(0xFFFFB400),
|
|
const Color(0xFF1E83F7),
|
|
const Color(0xFF18C249),
|
|
];
|
|
|
|
GradientBorderPainter({
|
|
required this.animation,
|
|
required this.strokeWidth,
|
|
});
|
|
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
final center = Offset(size.width / 2, size.height / 2);
|
|
final radius = min(size.width, size.height) / 2 - strokeWidth / 2;
|
|
|
|
// Create a sweep gradient that covers the entire circle
|
|
final Rect rect = Rect.fromCircle(center: center, radius: radius);
|
|
|
|
// Create a paint with the gradient
|
|
final paint = Paint()
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = strokeWidth;
|
|
|
|
// Create a gradient that smoothly transitions between colors
|
|
final shader = SweepGradient(
|
|
// Use a fixed starting point and let matrix transformation handle rotation
|
|
startAngle: 0,
|
|
endAngle: 2 * 3.14159,
|
|
colors: [
|
|
// Repeat colors to ensure smooth transitions
|
|
...colors.map((c) => c.withValues(alpha: opacity)),
|
|
colors.first.withValues(alpha: opacity),
|
|
],
|
|
// Add evenly distributed stops
|
|
stops: List.generate(
|
|
colors.length + 1,
|
|
(index) => index / colors.length,
|
|
),
|
|
tileMode: TileMode.clamp,
|
|
// Use transformations to rotate the gradient
|
|
transform: GradientRotation(-animation * 2 * 3.14159),
|
|
).createShader(rect);
|
|
|
|
paint.shader = shader;
|
|
|
|
// Draw the circular border
|
|
canvas.drawCircle(center, radius, paint);
|
|
}
|
|
|
|
@override
|
|
bool shouldRepaint(GradientBorderPainter oldDelegate) {
|
|
return animation != oldDelegate.animation;
|
|
}
|
|
|
|
double min(double a, double b) => a < b ? a : b;
|
|
}
|