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 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 children; /// List of `ImageProvider` eg. AssetImage, FileImage, NetworkImage final List 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: [ 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 }