chore(mobile): small visual fix and update (#17547)

* chore(mobile): small visual fix and update

* update

* update

* remove design placeholder
This commit is contained in:
Alex
2025-04-13 08:01:32 -05:00
committed by GitHub
parent 1f18fe31f0
commit ab2a7006f9
11 changed files with 168 additions and 39 deletions

View File

@@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/widgets/common/immich_logo.dart';
class ImmichLoadingIndicator extends StatelessWidget {
class ImmichLoadingIndicator extends HookWidget {
final double? borderRadius;
const ImmichLoadingIndicator({
@@ -11,18 +12,109 @@ class ImmichLoadingIndicator extends StatelessWidget {
@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: 60,
width: 60,
height: 80,
width: 80,
decoration: BoxDecoration(
color: context.primaryColor.withAlpha(200),
borderRadius: BorderRadius.circular(borderRadius ?? 10),
color: Colors.transparent,
borderRadius: BorderRadius.circular(borderRadius ?? 50),
backgroundBlendMode: BlendMode.luminosity,
),
padding: const EdgeInsets.all(15),
child: const CircularProgressIndicator(
color: Colors.white,
strokeWidth: 3,
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;
}