Files
AYS-User/lib/component/flutter_image_stack_widget.dart
2026-02-22 10:47:09 +08:00

266 lines
7.5 KiB
Dart

library flutter_image_stack;
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:nb_utils/nb_utils.dart';
/// Creates a flutter image stack
class FlutterImageStack extends StatelessWidget {
final VoidCallback? onCallBack;
/// List of image urls
final List<String> imageList;
/// item radius for the circular image/widget
final double? itemRadius;
/// Count of the number of images/widgets to be shown
final int? itemCount;
/// Total count will be used to determine the number of circular images
/// to be shown along with showing the remaining count in an additional
/// circle
final int totalCount;
/// Optional field to set the circular image/widget border width
final double? itemBorderWidth;
/// Optional field to set the color of circular image/widget border
final Color? itemBorderColor;
/// The text style to apply if there is any extra count to be shown
final TextStyle extraCountTextStyle;
/// Set the background color of the circle
final Color backgroundColor;
/// Enum to define the image source.
///
/// Describes type of the image source being sent in [imageList]
///
/// Possible values:
/// * Asset
/// * Network
/// * File
final ImageSource? imageSource;
/// Custom widget list passed to render circular widgets
final List<Widget> children;
/// List of `ImageProvider` eg. AssetImage, FileImage, NetworkImage
final List<ImageProvider> providers;
/// To show the remaining count if the provided list size is less than [totalCount]
final bool showTotalCount;
/// Creates a flutter image stack widget.
///
/// The [imageList] and [totalCount] parameters are required.
FlutterImageStack({
Key? key,
required this.imageList,
this.itemRadius = 25,
this.itemCount = 3,
required this.totalCount,
this.itemBorderWidth = 2,
Color this.itemBorderColor = Colors.grey,
this.imageSource = ImageSource.network,
this.showTotalCount = true,
this.extraCountTextStyle = const TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
),
this.backgroundColor = Colors.white,
this.onCallBack,
}) : children = [],
providers = [],
super(key: key);
/// Creates a flutter image stack widget by passing list of custom widgets.
///
/// The [children] and [totalCount] parameters are required.
FlutterImageStack.widgets({
Key? key,
required this.children,
this.itemRadius = 25,
this.itemCount = 3,
required this.totalCount,
this.itemBorderWidth = 2,
Color this.itemBorderColor = Colors.grey,
this.showTotalCount = true,
this.extraCountTextStyle = const TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
),
this.backgroundColor = Colors.white,
this.onCallBack,
}) : imageList = [],
providers = [],
imageSource = null,
super(key: key);
/// Creates an flutter image stack by passing list of `ImageProvider`.
///
/// The [providers] and [totalCount] parameters are required.
FlutterImageStack.providers({
Key? key,
required this.providers,
this.itemRadius = 25,
this.itemCount = 3,
required this.totalCount,
this.itemBorderWidth = 2,
Color this.itemBorderColor = Colors.grey,
this.showTotalCount = true,
this.extraCountTextStyle = const TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
),
this.backgroundColor = Colors.white,
this.onCallBack,
}) : imageList = [],
children = [],
imageSource = null,
super(key: key);
@override
Widget build(BuildContext context) {
var items = List.from(imageList)
..addAll(children)
..addAll(providers);
int size = min(itemCount!, items.length);
var widgetList = items
.sublist(0, size)
.asMap()
.map((index, value) => MapEntry(
index,
Padding(
padding: EdgeInsets.only(left: 0.7 * itemRadius! * index),
child: circularItem(value),
)))
.values
.toList()
.reversed
.toList();
return FittedBox(
child: Row(
children: <Widget>[
widgetList.isNotEmpty
? Stack(
clipBehavior: Clip.none,
children: widgetList,
)
: const SizedBox.shrink(),
Container(
child: showTotalCount && totalCount - widgetList.length > 0
? Container(
constraints: BoxConstraints(minWidth: itemRadius! - itemBorderWidth!),
padding: const EdgeInsets.all(3),
height: (itemRadius! - itemBorderWidth!),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(itemRadius! - itemBorderWidth!), border: Border.all(color: itemBorderColor!, width: itemBorderWidth!), color: backgroundColor),
child: Center(
child: Text(
"+${totalCount - widgetList.length}",
textAlign: TextAlign.center,
style: extraCountTextStyle,
),
),
).onTap(() {
onCallBack?.call();
}, hoverColor: Colors.transparent, highlightColor: Colors.transparent, splashColor: Colors.transparent)
: const SizedBox()),
],
),
);
}
Widget circularItem(dynamic item) {
if (item is ImageProvider) {
return circularProviders(item);
} else if (item is Widget) {
return circularWidget(item);
} else if (item is String) {
return circularImage(item);
}
return Container();
}
circularWidget(Widget widget) {
return Container(
height: itemRadius,
width: itemRadius,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: backgroundColor,
border: Border.all(
color: itemBorderColor!,
width: itemBorderWidth!,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(itemRadius!),
child: widget,
),
);
}
Widget circularImage(String imageUrl) {
return Container(
height: itemRadius,
width: itemRadius,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.white,
width: itemBorderWidth!,
),
),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
image: DecorationImage(
image: imageProvider(imageUrl),
fit: BoxFit.cover,
),
),
),
);
}
Widget circularProviders(ImageProvider imageProvider) {
return Container(
height: itemRadius,
width: itemRadius,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.white,
width: itemBorderWidth!,
),
),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
);
}
imageProvider(imageUrl) {
if (imageSource == ImageSource.asset) {
return AssetImage(imageUrl);
} else if (imageSource == ImageSource.file) {
return FileImage(imageUrl);
}
return NetworkImage(imageUrl);
}
}
enum ImageSource { asset, network, file }