mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-03-24 16:04:43 +01:00
163 lines
4.1 KiB
Dart
163 lines
4.1 KiB
Dart
// https://docs.flutter.dev/cookbook/effects/shimmer-loading
|
|
import 'package:flutter/material.dart';
|
|
|
|
class Shimmer extends StatefulWidget {
|
|
const Shimmer({
|
|
super.key,
|
|
required this.linearGradient,
|
|
this.child,
|
|
});
|
|
|
|
static _ShimmerState? _of(BuildContext context) {
|
|
return context.findAncestorStateOfType<_ShimmerState>();
|
|
}
|
|
|
|
final LinearGradient linearGradient;
|
|
final Widget? child;
|
|
|
|
@override
|
|
State<Shimmer> createState() => _ShimmerState();
|
|
}
|
|
|
|
class _ShimmerState extends State<Shimmer> with SingleTickerProviderStateMixin {
|
|
late AnimationController _shimmerController;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_shimmerController = AnimationController.unbounded(vsync: this)
|
|
..repeat(min: -0.5, max: 1.5, period: const Duration(milliseconds: 1500));
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_shimmerController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
LinearGradient get gradient => LinearGradient(
|
|
colors: widget.linearGradient.colors,
|
|
stops: widget.linearGradient.stops,
|
|
begin: widget.linearGradient.begin,
|
|
end: widget.linearGradient.end,
|
|
transform:
|
|
_SlidingGradientTransform(slidePercent: _shimmerController.value),
|
|
);
|
|
|
|
bool get isSized =>
|
|
(context.findRenderObject() as RenderBox?)?.hasSize ?? false;
|
|
|
|
Size get size => (context.findRenderObject() as RenderBox).size;
|
|
|
|
Offset getDescendantOffset({
|
|
required RenderBox descendant,
|
|
Offset offset = Offset.zero,
|
|
}) {
|
|
final shimmerBox = context.findRenderObject() as RenderBox;
|
|
return descendant.localToGlobal(offset, ancestor: shimmerBox);
|
|
}
|
|
|
|
Listenable get shimmerChanges => _shimmerController;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return widget.child ?? const SizedBox();
|
|
}
|
|
}
|
|
|
|
class ShimmerLoading extends StatefulWidget {
|
|
const ShimmerLoading({
|
|
super.key,
|
|
required this.isLoading,
|
|
required this.child,
|
|
});
|
|
|
|
final bool isLoading;
|
|
final Widget child;
|
|
|
|
@override
|
|
State<ShimmerLoading> createState() => _ShimmerLoadingState();
|
|
}
|
|
|
|
class _ShimmerLoadingState extends State<ShimmerLoading> {
|
|
Listenable? _shimmerChanges;
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
super.didChangeDependencies();
|
|
if (_shimmerChanges != null) {
|
|
_shimmerChanges!.removeListener(_onShimmerChange);
|
|
}
|
|
_shimmerChanges = Shimmer._of(context)?.shimmerChanges;
|
|
if (_shimmerChanges != null) {
|
|
_shimmerChanges!.addListener(_onShimmerChange);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_shimmerChanges?.removeListener(_onShimmerChange);
|
|
super.dispose();
|
|
}
|
|
|
|
void _onShimmerChange() {
|
|
if (widget.isLoading) {
|
|
setState(() {
|
|
// update the shimmer painting.
|
|
});
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (!widget.isLoading) {
|
|
return widget.child;
|
|
}
|
|
|
|
// Collect ancestor shimmer info.
|
|
final shimmer = Shimmer._of(context)!;
|
|
if (!shimmer.isSized) {
|
|
// The ancestor Shimmer widget has not laid
|
|
// itself out yet. Return an empty box.
|
|
return const SizedBox();
|
|
}
|
|
final shimmerSize = shimmer.size;
|
|
final gradient = shimmer.gradient;
|
|
// https://github.com/flutter/website/issues/9372
|
|
Offset offsetWithinShimmer = Offset.zero;
|
|
if (context.findRenderObject() != null) {
|
|
final box = context.findRenderObject() as RenderBox;
|
|
offsetWithinShimmer = shimmer.getDescendantOffset(
|
|
descendant: box,
|
|
);
|
|
}
|
|
|
|
return ShaderMask(
|
|
blendMode: BlendMode.srcATop,
|
|
shaderCallback: (bounds) {
|
|
return gradient.createShader(
|
|
Rect.fromLTWH(
|
|
-offsetWithinShimmer.dx,
|
|
-offsetWithinShimmer.dy,
|
|
shimmerSize.width,
|
|
shimmerSize.height,
|
|
),
|
|
);
|
|
},
|
|
child: widget.child,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _SlidingGradientTransform extends GradientTransform {
|
|
const _SlidingGradientTransform({
|
|
required this.slidePercent,
|
|
});
|
|
|
|
final double slidePercent;
|
|
|
|
@override
|
|
Matrix4? transform(Rect bounds, {TextDirection? textDirection}) {
|
|
return Matrix4.translationValues(bounds.width * slidePercent, 0.0, 0.0);
|
|
}
|
|
}
|