Skip to content

Instantly share code, notes, and snippets.

@mondoktamas
Created April 29, 2022 09:58
Show Gist options
  • Save mondoktamas/3f6f22a0d9dd6e96518afd67ca16d1d6 to your computer and use it in GitHub Desktop.
Save mondoktamas/3f6f22a0d9dd6e96518afd67ca16d1d6 to your computer and use it in GitHub Desktop.
import 'dart:math';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
class StackedAvatar extends StatelessWidget {
const StackedAvatar({
required this.avatarUrls,
this.size = 32.0,
this.maxItemCount = 3,
this.overlap = 10.0,
this.borderWidth = 3.0,
});
final List<String> avatarUrls;
final double size;
///the overlap in px of the avatars
final double overlap;
///maximum visible avatar items
final int maxItemCount;
///the outer border width around each avatar
final double borderWidth;
@override
Widget build(BuildContext context) {
final widgetWidth = (maxItemCount * size) - (overlap * (maxItemCount - 1));
return Container(
height: size,
width: widgetWidth,
child: Stack(
children: List.generate(
min(maxItemCount, avatarUrls.length),
(index) => Positioned(
top: 0,
left: index * (size - overlap),
bottom: 0,
child: _StackedAvatar(
shouldClip: index != 0,
avatarUrl: avatarUrls[index],
overlap: overlap + borderWidth,
size: size,
),
),
),
),
);
}
}
class _StackedAvatar extends StatelessWidget {
const _StackedAvatar({
required this.avatarUrl,
this.overlap = 10,
this.size = 32,
this.shouldClip = false,
});
final String avatarUrl;
final double size;
final double overlap;
final bool shouldClip;
@override
Widget build(BuildContext context) {
return SizedBox(
height: size,
width: size,
child: ClipPath(
clipper: _StackedAvatarClipper(shouldClip ? overlap : 0),
child: Image(
image: CachedNetworkImageProvider(avatarUrl),
),
),
);
}
}
class _StackedAvatarClipper extends CustomClipper<Path> {
final double shift;
_StackedAvatarClipper(this.shift);
@override
Path getClip(Size size) {
final path1 = Path()..addOval(Rect.fromLTRB(shift - size.width, 0, shift, size.height));
final path2 = Path()..addOval(Rect.fromLTRB(0, 0, size.width, size.height));
return Path.combine(PathOperation.reverseDifference, path1, path2);
}
@override
bool shouldReclip(covariant _StackedAvatarClipper oldClipper) => oldClipper.shift != shift;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment