Skip to content
HyperUI for Flutter requires the Style package — this site itself ships no pub package.

Product Collections

Ported from HyperUI’s Product Collections — see Attribution.

const _photoUrl =
'https://images.unsplash.com/photo-1523381210434-271e8be1f52b?auto=format&fit=crop&q=80&w=1160';
class ProductCollections1Example extends StatelessWidget {
const ProductCollections1Example({super.key});
@override
Widget build(BuildContext context) {
return Style(
'mx-auto max-w-7xl px-4 py-8 sm:px-6 sm:py-12 lg:px-8',
children: [
const Style(
'text-xl font-bold text-gray-900 sm:text-3xl',
child: Text('Product Collection'),
),
const Style(
'mt-4 max-w-md text-gray-500',
child: Text(
'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Itaque '
'praesentium cumque iure dicta incidunt est ipsam, officia dolor '
'fugit natus?',
),
),
Style(
'mt-8 grid gap-4 sm:grid-cols-2 lg:grid-cols-4',
children: [
Style(
'group block overflow-hidden',
cursor: SystemMouseCursors.click,
children: [
Style(
'h-87.5 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-112.5',
child: Image.network(_photoUrl, fit: BoxFit.cover),
),
Style(
'relative bg-white pt-3',
children: [
const Style(
'text-xs text-gray-700 group-hover:underline group-hover:underline-offset-4',
child: Text('Basic Tee'),
),
Style(
'mt-2',
children: const [
Style('sr-only', child: Text('Regular Price')),
Style(
'tracking-wider text-gray-900',
child: Text('£24.00 GBP'),
),
],
),
],
),
],
),
Style(
'group block overflow-hidden',
cursor: SystemMouseCursors.click,
children: [
Style(
'h-87.5 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-112.5',
child: Image.network(_photoUrl, fit: BoxFit.cover),
),
Style(
'relative bg-white pt-3',
children: [
const Style(
'text-xs text-gray-700 group-hover:underline group-hover:underline-offset-4',
child: Text('Basic Tee'),
),
Style(
'mt-2',
children: const [
Style('sr-only', child: Text('Regular Price')),
Style(
'tracking-wider text-gray-900',
child: Text('£24.00 GBP'),
),
],
),
],
),
],
),
Style(
'group block overflow-hidden',
cursor: SystemMouseCursors.click,
children: [
Style(
'h-87.5 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-112.5',
child: Image.network(_photoUrl, fit: BoxFit.cover),
),
Style(
'relative bg-white pt-3',
children: [
const Style(
'text-xs text-gray-700 group-hover:underline group-hover:underline-offset-4',
child: Text('Basic Tee'),
),
Style(
'mt-2',
children: const [
Style('sr-only', child: Text('Regular Price')),
Style(
'tracking-wider text-gray-900',
child: Text('£24.00 GBP'),
),
],
),
],
),
],
),
Style(
'group block overflow-hidden',
cursor: SystemMouseCursors.click,
children: [
Style(
'h-87.5 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-112.5',
child: Image.network(_photoUrl, fit: BoxFit.cover),
),
Style(
'relative bg-white pt-3',
children: [
const Style(
'text-xs text-gray-700 group-hover:underline group-hover:underline-offset-4',
child: Text('Basic Tee'),
),
Style(
'mt-2',
children: const [
Style('sr-only', child: Text('Regular Price')),
Style(
'tracking-wider text-gray-900',
child: Text('£24.00 GBP'),
),
],
),
],
),
],
),
],
),
],
);
}
}
const _photoUrl =
'https://images.unsplash.com/photo-1523381210434-271e8be1f52b?auto=format&fit=crop&q=80&w=1160';
class ProductCollections2Example extends StatelessWidget {
const ProductCollections2Example({super.key});
@override
Widget build(BuildContext context) {
return Style(
'mx-auto max-w-7xl px-4 py-8 sm:px-6 sm:py-12 lg:px-8',
children: [
Style(
'text-center',
children: [
const Style(
'text-xl font-bold text-gray-900 sm:text-3xl',
child: Text('Product Collection'),
),
const Style(
'mx-auto mt-4 max-w-md text-gray-500',
child: Text(
'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Itaque '
'praesentium cumque iure dicta incidunt est ipsam, officia dolor '
'fugit natus?',
),
),
],
),
Style(
'mt-8 grid gap-4 sm:grid-cols-2 lg:grid-cols-4',
children: [
Style(
'group block overflow-hidden',
cursor: SystemMouseCursors.click,
children: [
Style(
'h-87.5 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-112.5',
child: Image.network(_photoUrl, fit: BoxFit.cover),
),
Style(
'relative bg-white pt-3',
children: [
const Style(
'text-xs text-gray-700 group-hover:underline group-hover:underline-offset-4',
child: Text('Basic Tee'),
),
Style(
'mt-2',
children: const [
Style('sr-only', child: Text('Regular Price')),
Style(
'tracking-wider text-gray-900',
child: Text('£24.00 GBP'),
),
],
),
],
),
],
),
Style(
'group block overflow-hidden',
cursor: SystemMouseCursors.click,
children: [
Style(
'h-87.5 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-112.5',
child: Image.network(_photoUrl, fit: BoxFit.cover),
),
Style(
'relative bg-white pt-3',
children: [
const Style(
'text-xs text-gray-700 group-hover:underline group-hover:underline-offset-4',
child: Text('Basic Tee'),
),
Style(
'mt-2',
children: const [
Style('sr-only', child: Text('Regular Price')),
Style(
'tracking-wider text-gray-900',
child: Text('£24.00 GBP'),
),
],
),
],
),
],
),
Style(
'group block overflow-hidden',
cursor: SystemMouseCursors.click,
children: [
Style(
'h-87.5 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-112.5',
child: Image.network(_photoUrl, fit: BoxFit.cover),
),
Style(
'relative bg-white pt-3',
children: [
const Style(
'text-xs text-gray-700 group-hover:underline group-hover:underline-offset-4',
child: Text('Basic Tee'),
),
Style(
'mt-2',
children: const [
Style('sr-only', child: Text('Regular Price')),
Style(
'tracking-wider text-gray-900',
child: Text('£24.00 GBP'),
),
],
),
],
),
],
),
Style(
'group block overflow-hidden',
cursor: SystemMouseCursors.click,
children: [
Style(
'h-87.5 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-112.5',
child: Image.network(_photoUrl, fit: BoxFit.cover),
),
Style(
'relative bg-white pt-3',
children: [
const Style(
'text-xs text-gray-700 group-hover:underline group-hover:underline-offset-4',
child: Text('Basic Tee'),
),
Style(
'mt-2',
children: const [
Style('sr-only', child: Text('Regular Price')),
Style(
'tracking-wider text-gray-900',
child: Text('£24.00 GBP'),
),
],
),
],
),
],
),
],
),
],
);
}
}
class CurrentColorIcon extends StatelessWidget
implements StyleReplacedElementWrapper {
const CurrentColorIcon(this.svg, {super.key});
final String svg;
@override
Widget build(BuildContext context) {
final color = IconTheme.of(context).color ?? const Color(0xFF000000);
return SvgPicture.string(svg, theme: SvgTheme(currentColor: color));
}
}
const _photoUrl =
'https://images.unsplash.com/photo-1523381210434-271e8be1f52b?auto=format&fit=crop&q=80&w=1160';
const _chevronDownSvg =
'''<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"/></svg>''';
const _filterToggleSvg =
'''<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"/></svg>''';
Widget _buildPriceField(BuildContext context, {required String hintText}) {
final isSmUp = MediaQuery.sizeOf(context).width >= 640;
final fontSize = isSmUp ? 14.0 : 16.0;
final lineHeight = isSmUp ? 20.0 : 24.0;
final baseStyle = Theme.of(context).textTheme.bodyLarge ?? const TextStyle();
return DecoratedBox(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6),
border: Border.all(color: const Color(0xFFE5E7EB)),
boxShadow: const [
BoxShadow(color: Color(0x0D000000), offset: Offset(0, 1), blurRadius: 2),
],
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Text(
hintText,
style: baseStyle.copyWith(
fontSize: fontSize,
height: lineHeight / fontSize,
color: tailwindColors['gray']![400],
letterSpacing: 0,
),
),
),
);
}
class ProductCollections3Example extends StatefulWidget {
const ProductCollections3Example({super.key});
@override
State<ProductCollections3Example> createState() =>
_ProductCollections3ExampleState();
}
class _ProductCollections3ExampleState
extends State<ProductCollections3Example> {
var _availabilityOpen = false;
var _priceOpen = false;
@override
Widget build(BuildContext context) {
return Style(
'mx-auto max-w-7xl px-4 py-8 sm:px-6 sm:py-12 lg:px-8',
children: [
const Style(
'text-xl font-bold text-gray-900 sm:text-3xl',
child: Text('Product Collection'),
),
const Style(
'mt-4 max-w-md text-gray-500',
child: Text(
'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Itaque '
'praesentium cumque iure dicta incidunt est ipsam, officia dolor '
'fugit natus?',
),
),
Style(
'mt-8 sm:flex sm:items-center sm:justify-between',
children: [
Style(
'block sm:hidden',
child: Style(
'flex cursor-pointer items-center gap-2 border-b border-gray-400 pb-1 text-gray-900 transition hover:border-gray-600',
cursor: SystemMouseCursors.click,
children: [
const Style(
'text-sm font-medium',
display: DisplayType.inline,
child: Text('Filters & Sorting'),
),
const Style(
'size-4 rtl:rotate-180',
child: CurrentColorIcon(_filterToggleSvg),
),
],
),
),
Style(
'hidden sm:flex sm:gap-4',
children: [
Style(
'relative',
child: Style(
'group [&_summary::-webkit-details-marker]:hidden',
open: _availabilityOpen,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => setState(
() => _availabilityOpen = !_availabilityOpen,
),
child: Style(
'flex cursor-pointer items-center gap-2 border-b border-gray-400 pb-1 text-gray-900 transition hover:border-gray-600',
cursor: SystemMouseCursors.click,
children: [
const Style(
'text-sm font-medium',
display: DisplayType.inline,
child: Text('Availability'),
),
Style(
'transition group-open:-rotate-180',
child: const Style(
'size-4',
child: CurrentColorIcon(_chevronDownSvg),
),
),
],
),
),
if (_availabilityOpen)
Style(
'z-50 group-open:absolute group-open:top-auto group-open:mt-2 ltr:group-open:start-0',
child: Style(
'w-96 rounded-sm border border-gray-200 bg-white',
children: [
Style(
'flex items-center justify-between p-4',
children: const [
Style(
'text-sm text-gray-700',
display: DisplayType.inline,
child: Text('0 Selected'),
),
Style(
'text-sm text-gray-900 underline underline-offset-4',
child: Text('Reset'),
),
],
),
Style(
'space-y-1 border-t border-gray-200 p-4',
children: const [
Padding(
padding: EdgeInsets.only(bottom: 7),
child: Style(
'inline-flex items-center gap-2',
children: [
Style(
'size-5 rounded-sm border-gray-300 shadow-sm',
formControl: true,
),
Style(
'text-sm font-medium text-gray-700',
display: DisplayType.inline,
child: Text('In Stock (5+)'),
),
],
),
),
Padding(
padding: EdgeInsets.only(bottom: 7),
child: Style(
'inline-flex items-center gap-2',
children: [
Style(
'size-5 rounded-sm border-gray-300 shadow-sm',
formControl: true,
),
Style(
'text-sm font-medium text-gray-700',
display: DisplayType.inline,
child: Text('Pre Order (3+)'),
),
],
),
),
Padding(
padding: EdgeInsets.only(bottom: 7),
child: Style(
'inline-flex items-center gap-2',
children: [
Style(
'size-5 rounded-sm border-gray-300 shadow-sm',
formControl: true,
),
Style(
'text-sm font-medium text-gray-700',
display: DisplayType.inline,
child: Text('Out of Stock (10+)'),
),
],
),
),
],
),
],
),
),
],
),
),
Style(
'relative',
child: Style(
'group [&_summary::-webkit-details-marker]:hidden',
open: _priceOpen,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => setState(() => _priceOpen = !_priceOpen),
child: Style(
'flex cursor-pointer items-center gap-2 border-b border-gray-400 pb-1 text-gray-900 transition hover:border-gray-600',
cursor: SystemMouseCursors.click,
children: [
const Style(
'text-sm font-medium',
display: DisplayType.inline,
child: Text('Price'),
),
Style(
'transition group-open:-rotate-180',
child: const Style(
'size-4',
child: CurrentColorIcon(_chevronDownSvg),
),
),
],
),
),
if (_priceOpen)
Style(
'z-50 group-open:absolute group-open:top-auto group-open:mt-2 ltr:group-open:start-0',
child: Style(
'w-96 rounded-sm border border-gray-200 bg-white',
children: [
const Style(
'flex items-center justify-between p-4',
children: [
Style(
'text-sm text-gray-700',
display: DisplayType.inline,
child: Text('The highest price is \$600'),
),
Style(
'text-sm text-gray-900 underline underline-offset-4',
child: Text('Reset'),
),
],
),
Style(
'border-t border-gray-200 p-4',
child: Style(
'flex justify-between gap-4',
children: [
Style(
'flex items-center gap-2',
children: [
const Style(
'text-sm text-gray-600',
display: DisplayType.inline,
child: Text('\$'),
),
Style(
'w-full rounded-md border-gray-200 shadow-xs sm:text-sm',
child: _buildPriceField(
context,
hintText: 'From',
),
),
],
),
Style(
'flex items-center gap-2',
children: [
const Style(
'text-sm text-gray-600',
display: DisplayType.inline,
child: Text('\$'),
),
Style(
'w-full rounded-md border-gray-200 shadow-xs sm:text-sm',
child: _buildPriceField(
context,
hintText: 'To',
),
),
],
),
],
),
),
],
),
),
],
),
),
],
),
Style(
'hidden sm:block',
children: [
const Style('sr-only', child: Text('SortBy')),
Style(
'h-10 rounded-sm border-gray-300 text-sm',
child: Builder(
builder: (context) {
final baseStyle =
Theme.of(context).textTheme.bodyLarge ??
const TextStyle();
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(2),
border: Border.all(
color: const Color(0xFFD1D5DB),
),
),
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Sort By',
style: baseStyle.copyWith(
fontSize: 14,
height: 20 / 14,
color: const Color(0xFF111827),
letterSpacing: 0,
),
),
Padding(
padding: const EdgeInsets.only(left: 8),
child: SvgPicture.string(
_selectChevronSvg,
width: 21,
height: 21,
),
),
],
),
);
},
),
),
],
),
],
),
Style(
'mt-4 grid gap-4 sm:grid-cols-2 lg:grid-cols-4',
children: [
Style(
'group block overflow-hidden',
cursor: SystemMouseCursors.click,
children: [
Style(
'h-87.5 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-112.5',
child: Image.network(_photoUrl, fit: BoxFit.cover),
),
Style(
'relative bg-white pt-3',
children: [
const Style(
'text-xs text-gray-700 group-hover:underline group-hover:underline-offset-4',
child: Text('Basic Tee'),
),
Style(
'mt-2',
children: const [
Style('sr-only', child: Text('Regular Price')),
Style(
'tracking-wider text-gray-900',
child: Text('£24.00 GBP'),
),
],
),
],
),
],
),
Style(
'group block overflow-hidden',
cursor: SystemMouseCursors.click,
children: [
Style(
'h-87.5 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-112.5',
child: Image.network(_photoUrl, fit: BoxFit.cover),
),
Style(
'relative bg-white pt-3',
children: [
const Style(
'text-xs text-gray-700 group-hover:underline group-hover:underline-offset-4',
child: Text('Basic Tee'),
),
Style(
'mt-2',
children: const [
Style('sr-only', child: Text('Regular Price')),
Style(
'tracking-wider text-gray-900',
child: Text('£24.00 GBP'),
),
],
),
],
),
],
),
Style(
'group block overflow-hidden',
cursor: SystemMouseCursors.click,
children: [
Style(
'h-87.5 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-112.5',
child: Image.network(_photoUrl, fit: BoxFit.cover),
),
Style(
'relative bg-white pt-3',
children: [
const Style(
'text-xs text-gray-700 group-hover:underline group-hover:underline-offset-4',
child: Text('Basic Tee'),
),
Style(
'mt-2',
children: const [
Style('sr-only', child: Text('Regular Price')),
Style(
'tracking-wider text-gray-900',
child: Text('£24.00 GBP'),
),
],
),
],
),
],
),
Style(
'group block overflow-hidden',
cursor: SystemMouseCursors.click,
children: [
Style(
'h-87.5 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-112.5',
child: Image.network(_photoUrl, fit: BoxFit.cover),
),
Style(
'relative bg-white pt-3',
children: [
const Style(
'text-xs text-gray-700 group-hover:underline group-hover:underline-offset-4',
child: Text('Basic Tee'),
),
Style(
'mt-2',
children: const [
Style('sr-only', child: Text('Regular Price')),
Style(
'tracking-wider text-gray-900',
child: Text('£24.00 GBP'),
),
],
),
],
),
],
),
],
),
],
);
}
}
const _selectChevronSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20"><path stroke="#6b7280" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6 8l4 4 4-4"/></svg>''';
class CurrentColorIcon extends StatelessWidget
implements StyleReplacedElementWrapper {
const CurrentColorIcon(this.svg, {super.key});
final String svg;
@override
Widget build(BuildContext context) {
final color = IconTheme.of(context).color ?? const Color(0xFF000000);
return SvgPicture.string(svg, theme: SvgTheme(currentColor: color));
}
}
const _photoUrl =
'https://images.unsplash.com/photo-1523381210434-271e8be1f52b?auto=format&fit=crop&q=80&w=1160';
const _chevronDownSvg =
'''<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"/></svg>''';
const _filterToggleSvg =
'''<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"/></svg>''';
const _selectChevronSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20"><path stroke="#6b7280" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6 8l4 4 4-4"/></svg>''';
class ProductCollections4Example extends StatefulWidget {
const ProductCollections4Example({super.key});
@override
State<ProductCollections4Example> createState() =>
_ProductCollections4ExampleState();
}
class _ProductCollections4ExampleState
extends State<ProductCollections4Example> {
var _availabilityOpen = false;
var _priceOpen = false;
var _colorsOpen = false;
@override
Widget build(BuildContext context) {
return Style(
'mx-auto max-w-7xl px-4 py-8 sm:px-6 sm:py-12 lg:px-8',
children: [
const Style(
'text-xl font-bold text-gray-900 sm:text-3xl',
child: Text('Product Collection'),
),
const Style(
'mt-4 max-w-md text-gray-500',
child: Text(
'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Itaque '
'praesentium cumque iure dicta incidunt est ipsam, officia dolor '
'fugit natus?',
),
),
Style(
'mt-8 block lg:hidden',
child: Style(
'flex cursor-pointer items-center gap-2 border-b border-gray-400 pb-1 text-gray-900 transition hover:border-gray-600',
cursor: SystemMouseCursors.click,
children: [
const Style(
'text-sm font-medium',
display: DisplayType.inline,
child: Text('Filters & Sorting'),
),
const Style(
'size-4 rtl:rotate-180',
child: CurrentColorIcon(_filterToggleSvg),
),
],
),
),
Style(
'mt-4 lg:mt-8 lg:grid lg:grid-cols-4 lg:items-start lg:gap-8',
children: [
Style(
'hidden space-y-4 lg:block',
children: [
Style.empty(
children: [
const Style(
'block text-xs font-medium text-gray-700',
child: Text('Sort By'),
),
Style(
'mt-1 rounded-sm border-gray-300 text-sm',
child: Builder(
builder: (context) {
final baseStyle =
Theme.of(context).textTheme.bodyLarge ??
const TextStyle();
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(
2,
),
border: Border.all(
color: const Color(
0xFFD1D5DB,
),
),
),
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Sort By',
style: baseStyle.copyWith(
fontSize: 14,
height: 20 / 14,
color: const Color(0xFF111827),
letterSpacing: 0,
),
),
Padding(
padding: const EdgeInsets.only(left: 8),
child: SvgPicture.string(
_selectChevronSvg,
width: 21,
height: 21,
),
),
],
),
);
},
),
),
],
),
Style.empty(
children: [
const Style(
'block text-xs font-medium text-gray-700',
child: Text('Filters'),
),
Style(
'mt-1 space-y-2',
children: [
Style(
'overflow-hidden rounded-sm border border-gray-300 [&_summary::-webkit-details-marker]:hidden',
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => setState(
() => _availabilityOpen = !_availabilityOpen,
),
child: Style(
'flex cursor-pointer items-center justify-between gap-2 p-4 text-gray-900 transition',
cursor: SystemMouseCursors.click,
children: [
const Style(
'text-sm font-medium',
display: DisplayType.inline,
child: Text('Availability'),
),
const Style(
'transition group-open:-rotate-180',
child: Style(
'size-4',
child: CurrentColorIcon(_chevronDownSvg),
),
),
],
),
),
if (_availabilityOpen)
Style(
'border-t border-gray-200 bg-white',
children: [
const Style(
'flex items-center justify-between p-4',
children: [
Style(
'text-sm text-gray-700',
display: DisplayType.inline,
child: Text('0 Selected'),
),
Style(
'text-sm text-gray-900 underline underline-offset-4',
child: Text('Reset'),
),
],
),
Style(
'space-y-1 border-t border-gray-200 p-4',
children: const [
Style(
'inline-flex items-center gap-2',
children: [
Style(
'size-5 rounded-sm border-gray-300 shadow-sm',
formControl: true,
),
Style(
'text-sm font-medium text-gray-700',
display: DisplayType.inline,
child: Text('In Stock (5+)'),
),
],
),
Style(
'inline-flex items-center gap-2',
children: [
Style(
'size-5 rounded-sm border-gray-300 shadow-sm',
formControl: true,
),
Style(
'text-sm font-medium text-gray-700',
display: DisplayType.inline,
child: Text('Pre Order (3+)'),
),
],
),
Style(
'inline-flex items-center gap-2',
children: [
Style(
'size-5 rounded-sm border-gray-300 shadow-sm',
formControl: true,
),
Style(
'text-sm font-medium text-gray-700',
display: DisplayType.inline,
child: Text('Out of Stock (10+)'),
),
],
),
],
),
],
),
],
),
Style(
'overflow-hidden rounded-sm border border-gray-300 [&_summary::-webkit-details-marker]:hidden',
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () =>
setState(() => _priceOpen = !_priceOpen),
child: Style(
'flex cursor-pointer items-center justify-between gap-2 p-4 text-gray-900 transition',
cursor: SystemMouseCursors.click,
children: [
const Style(
'text-sm font-medium',
display: DisplayType.inline,
child: Text('Price'),
),
const Style(
'transition group-open:-rotate-180',
child: Style(
'size-4',
child: CurrentColorIcon(_chevronDownSvg),
),
),
],
),
),
if (_priceOpen)
Style(
'border-t border-gray-200 bg-white',
children: [
const Style(
'flex items-center justify-between p-4',
children: [
Style(
'text-sm text-gray-700',
display: DisplayType.inline,
child: Text(
'The highest price is \$600',
),
),
Style(
'text-sm text-gray-900 underline underline-offset-4',
child: Text('Reset'),
),
],
),
Style(
'border-t border-gray-200 p-4',
child: Style(
'flex justify-between gap-4',
children: [
Style(
'flex items-center gap-2',
children: [
const Style(
'text-sm text-gray-600',
display: DisplayType.inline,
child: Text('\$'),
),
Style(
'w-full rounded-md border-gray-200 shadow-xs sm:text-sm',
child: Builder(
builder: (context) =>
_buildPriceField(
context,
hintText: 'From',
),
),
),
],
),
Style(
'flex items-center gap-2',
children: [
const Style(
'text-sm text-gray-600',
display: DisplayType.inline,
child: Text('\$'),
),
Style(
'w-full rounded-md border-gray-200 shadow-xs sm:text-sm',
child: Builder(
builder: (context) =>
_buildPriceField(
context,
hintText: 'To',
),
),
),
],
),
],
),
),
],
),
],
),
Style(
'overflow-hidden rounded-sm border border-gray-300 [&_summary::-webkit-details-marker]:hidden',
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () =>
setState(() => _colorsOpen = !_colorsOpen),
child: Style(
'flex cursor-pointer items-center justify-between gap-2 p-4 text-gray-900 transition',
cursor: SystemMouseCursors.click,
children: [
const Style(
'text-sm font-medium',
display: DisplayType.inline,
child: Text('Colors'),
),
const Style(
'transition group-open:-rotate-180',
child: Style(
'size-4',
child: CurrentColorIcon(_chevronDownSvg),
),
),
],
),
),
if (_colorsOpen)
Style(
'border-t border-gray-200 bg-white',
children: [
const Style(
'flex items-center justify-between p-4',
children: [
Style(
'text-sm text-gray-700',
display: DisplayType.inline,
child: Text('0 Selected'),
),
Style(
'text-sm text-gray-900 underline underline-offset-4',
child: Text('Reset'),
),
],
),
Style(
'space-y-1 border-t border-gray-200 p-4',
children: const [
Style(
'inline-flex items-center gap-2',
children: [
Style(
'size-5 rounded-sm border-gray-300 shadow-sm',
formControl: true,
),
Style(
'text-sm font-medium text-gray-700',
display: DisplayType.inline,
child: Text('Red'),
),
],
),
Style(
'inline-flex items-center gap-2',
children: [
Style(
'size-5 rounded-sm border-gray-300 shadow-sm',
formControl: true,
),
Style(
'text-sm font-medium text-gray-700',
display: DisplayType.inline,
child: Text('Blue'),
),
],
),
Style(
'inline-flex items-center gap-2',
children: [
Style(
'size-5 rounded-sm border-gray-300 shadow-sm',
formControl: true,
),
Style(
'text-sm font-medium text-gray-700',
display: DisplayType.inline,
child: Text('Green'),
),
],
),
Style(
'inline-flex items-center gap-2',
children: [
Style(
'size-5 rounded-sm border-gray-300 shadow-sm',
formControl: true,
),
Style(
'text-sm font-medium text-gray-700',
display: DisplayType.inline,
child: Text('Orange'),
),
],
),
Style(
'inline-flex items-center gap-2',
children: [
Style(
'size-5 rounded-sm border-gray-300 shadow-sm',
formControl: true,
),
Style(
'text-sm font-medium text-gray-700',
display: DisplayType.inline,
child: Text('Purple'),
),
],
),
Style(
'inline-flex items-center gap-2',
children: [
Style(
'size-5 rounded-sm border-gray-300 shadow-sm',
formControl: true,
),
Style(
'text-sm font-medium text-gray-700',
display: DisplayType.inline,
child: Text('Teal'),
),
],
),
],
),
],
),
],
),
],
),
],
),
],
),
Style(
'lg:col-span-3',
child: Style(
'grid gap-4 sm:grid-cols-2 lg:grid-cols-3',
children: [
Style(
'group block overflow-hidden',
cursor: SystemMouseCursors.click,
children: [
Style(
'h-87.5 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-112.5',
child: Image.network(_photoUrl, fit: BoxFit.cover),
),
Style(
'relative bg-white pt-3',
children: [
const Style(
'text-xs text-gray-700 group-hover:underline group-hover:underline-offset-4',
child: Text('Basic Tee'),
),
Style(
'mt-2',
children: const [
Style('sr-only', child: Text('Regular Price')),
Style(
'tracking-wider text-gray-900',
child: Text('£24.00 GBP'),
),
],
),
],
),
],
),
Style(
'group block overflow-hidden',
cursor: SystemMouseCursors.click,
children: [
Style(
'h-87.5 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-112.5',
child: Image.network(_photoUrl, fit: BoxFit.cover),
),
Style(
'relative bg-white pt-3',
children: [
const Style(
'text-xs text-gray-700 group-hover:underline group-hover:underline-offset-4',
child: Text('Basic Tee'),
),
Style(
'mt-2',
children: const [
Style('sr-only', child: Text('Regular Price')),
Style(
'tracking-wider text-gray-900',
child: Text('£24.00 GBP'),
),
],
),
],
),
],
),
Style(
'group block overflow-hidden',
cursor: SystemMouseCursors.click,
children: [
Style(
'h-87.5 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-112.5',
child: Image.network(_photoUrl, fit: BoxFit.cover),
),
Style(
'relative bg-white pt-3',
children: [
const Style(
'text-xs text-gray-700 group-hover:underline group-hover:underline-offset-4',
child: Text('Basic Tee'),
),
Style(
'mt-2',
children: const [
Style('sr-only', child: Text('Regular Price')),
Style(
'tracking-wider text-gray-900',
child: Text('£24.00 GBP'),
),
],
),
],
),
],
),
],
),
),
],
),
],
);
}
}
Widget _buildPriceField(BuildContext context, {required String hintText}) {
final isSmUp = MediaQuery.sizeOf(context).width >= 640;
final fontSize = isSmUp ? 14.0 : 16.0;
final lineHeight = isSmUp ? 20.0 : 24.0;
final baseStyle = Theme.of(context).textTheme.bodyLarge ?? const TextStyle();
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6),
border: Border.all(color: const Color(0xFFE5E7EB)),
boxShadow: const [
BoxShadow(color: Color(0x0D000000), offset: Offset(0, 1), blurRadius: 2),
],
),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Text(
hintText,
style: baseStyle.copyWith(
fontSize: fontSize,
height: lineHeight / fontSize,
color: tailwindColors['gray']![400],
letterSpacing: 0,
),
),
);
}