I’m building an App I have to build UI like below but I don’t have any idea that how to create UI like this. Kindly guide me through this.
3
var listImages = [ "https://source.unsplash.com/random/200x200?sig=1", "https://source.unsplash.com/random/200x200?sig=2", "https://source.unsplash.com/random/200x200?sig=3", "https://source.unsplash.com/random/200x200?sig=4", "https://source.unsplash.com/random/200x200?sig=5" ]; Padding( padding: const EdgeInsets.all(8.0), child: Stack( children: [ for (int i = 0; i < (listImages.length>=4?4:listImages.length); i++) Transform.translate( offset: Offset(i == 0 ? 0.0 : i * 44, 0), child: Container( height: 70, width: 70, alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(25), color: Colors.black87, ), clipBehavior: Clip.hardEdge, child: (i+1)>=4?Text("+ ${listImages.length-3}",style:const TextStyle(color: Colors.green),):Image.network(listImages[i]), ), ) ], ), )
The point is using Stack and Positioned to positiond widgets. (index to padding, maxPerson to set maximum widget to show)
Stack
Positioned
https://dartpad.dev/ example
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { final colors = const [ Colors.yellow, Colors.green, Colors.blue, Colors.orange, Colors.cyan, Colors.brown, ]; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: PersonStacker(colors: colors), ), ); } } class PersonStacker extends StatelessWidget { final List<Color> colors; final int maxPerson; const PersonStacker({required this.colors, this.maxPerson = 3}); @override Widget build(BuildContext context) { return Stack( children: [ ...colors .getRange(0, maxPerson) .toList() .asMap() .map((index, color) => MapEntry(index, Person(index, color: color))) .values .toList(), ...colors.length > maxPerson ? [Person(maxPerson, plus: colors.length - maxPerson)] : [] ], ); } } class Person extends StatelessWidget { final int index; final Color color; final Color colorBorder; final double size = 100; final double offset = 30; final int? plus; const Person(this.index, {this.plus, this.color = Colors.black, this.colorBorder = Colors.black}); Widget renderChild() => plus != null ? Center( child: Text( "+$plus", style: const TextStyle(color: Colors.white, fontSize: 20), ), ) : Container(); @override Widget build(BuildContext context) { return Positioned( left: index * offset, child: Container( width: size, height: size, decoration: BoxDecoration( color: color, border: Border.all(width: 2, color: colorBorder), borderRadius: BorderRadius.circular(size), ), child: renderChild(), ), ); } }
Result
Below are the methods used to prepare the layout
// this method return the layout as per your expectations, here images are the // list of items you want to use and max count are the list of max item you want // to show except the count tile. Widget getItems(List<String> images, int maxCount) => Stack( children: List.generate(images.length <= maxCount ? images.length : maxCount+1, (index) { if(index == maxCount){ return Positioned( left: index * 60, child: Container( padding: const EdgeInsets.all(2), // Border width decoration: BoxDecoration( border: Border.all( color: Colors.grey, width: 2, ), color: Colors.black, borderRadius: BorderRadius.circular(40)), child: SizedBox.fromSize( size: const Size.fromRadius(48), // Image radius child: Center( child: Text( style: const TextStyle( color: Color(0xff58D56D), fontSize: 30 ), "+${images.length-maxCount}" ), ), ), ), ); } else { return Positioned( left: index * 60, child: getItemWidget(images[index])); } }), ); // pass the image url you want to show. Widget getItemWidget(String imageUrl) => Stack( children: [ Container( padding: const EdgeInsets.all(2), // Border width decoration: BoxDecoration( color: Colors.black, borderRadius: BorderRadius.circular(40)), child: ClipRRect( borderRadius: BorderRadius.circular(40), child: SizedBox.fromSize( size: Size.fromRadius(48), // Image radius child: Image.network( imageUrl, fit: BoxFit.fill, ), ), ), ), Positioned( bottom: 0, left: 0, child: Container( padding: const EdgeInsets.all(2), // Border width decoration: const BoxDecoration( color: Colors.black, shape: BoxShape.circle, ), child: ClipRRect( borderRadius: BorderRadius.circular(100), child: SizedBox.fromSize( size: Size.fromRadius(20), // Image radius child: Image.network( "https://play-lh.googleusercontent.com/STIZ_iftiehDCSynHXQaLqiL-F4kbZwasXOB2nae5pXTOpNKz8XSd7_VCF1Zgc3Z8Q", fit: BoxFit.contain, ), ), ), ), ) ], );
Below code is used to show the items
getItems(["https://cdn.pixabay.com/photo/2013/07/13/10/07/man-156584__340.png", "https://static.vecteezy.com/system/resources/thumbnails/001/993/889/small/beautiful-latin-woman-avatar-character-icon-free-vector.jpg", "https://static.toiimg.com/thumb/resizemode-4,msid-76729536,width-1200,height-900/76729536.jpg", "https://www.nj.com/resizer/zovGSasCaR41h_yUGYHXbVTQW2A=/1280x0/smart/cloudfront-us-east-1.images.arcpublishing.com/advancelocal/SJGKVE5UNVESVCW7BBOHKQCZVE.jpg", "https://i.kinja-img.com/gawker-media/image/upload/t_original/ijsi5fzb1nbkbhxa2gc1.png"], 3), )
Output:
Click here to cancel reply.
3
Answers
The point is using
Stack
andPositioned
to positiond widgets.(index to padding, maxPerson to set maximum widget to show)
https://dartpad.dev/ example
Result
Below are the methods used to prepare the layout
Below code is used to show the items
Output: