Guides / Building Search UI / Ecommerce ui template / Components / Product listing page display

The Product view components allow you to show your product data from an Algolia index as a product card. Use:

Product card

The ProductCardView component lets you display product information. For example, you can show product cards in a list or grid view.

Product card

Code summary

You can customize ProductCardView in:

Usage and props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
class ProductCardView extends StatelessWidget {
  const ProductCardView(
      {Key? key,
      required this.product,
      this.imageAlignment = Alignment.center,
      this.onTap})
      : super(key: key);

  final Product product;
  final Alignment imageAlignment;
  final Function(String)? onTap;

  @override
  Widget build(BuildContext context) {
    final priceValue = (product.price?.onSales ?? false)
        ? product.price?.discountedValue
        : product.price?.value;
    final crossedValue =
        (product.price?.onSales ?? false) ? product.price?.value : null;
    return GestureDetector(
      onTap: () => onTap?.call(product.objectID!),
      child: SizedBox(
        width: 150,
        child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
          Stack(
            alignment: AlignmentDirectional.bottomStart,
            children: [
              SizedBox(
                  height: 100,
                  width: MediaQuery.of(context).size.width,
                  child: Image.network('${product.image}',
                      alignment: imageAlignment, fit: BoxFit.cover)),
              if (product.price?.onSales == true)
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: Text(" ON SALE ${product.price?.discountLevel}% ",
                      style: Theme.of(context).textTheme.caption?.copyWith(
                          color: Colors.white,
                          backgroundColor: AppTheme.darkPink)),
                )
            ],
          ),
          const SizedBox(height: 8),
          SizedBox(
              child: Text('${product.brand}',
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  softWrap: false,
                  style: Theme.of(context).textTheme.caption)),
          SizedBox(
              child: Text('${product.name}',
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  softWrap: false,
                  style: Theme.of(context).textTheme.bodyText2)),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 2.0),
            child: ColorIndicatorView(product: product),
          ),
          Row(
            children: [
              Text('$priceValue €',
                  maxLines: 1,
                  overflow: TextOverflow.clip,
                  softWrap: false,
                  style: Theme.of(context).textTheme.bodyText2?.copyWith(
                      fontWeight: FontWeight.bold,
                      color: AppTheme.vividOrange)),
              if (crossedValue != null)
                Padding(
                  padding: const EdgeInsets.only(left: 8.0),
                  child: Text('$crossedValue €',
                      maxLines: 1,
                      overflow: TextOverflow.clip,
                      softWrap: false,
                      style: Theme.of(context)
                          .textTheme
                          .caption
                          ?.copyWith(decoration: TextDecoration.lineThrough)),
                ),
            ],
          ),
          RatingView(
              value: product.reviews?.rating?.toInt() ?? 0,
              reviewsCount: product.reviews?.count?.toInt() ?? 0),
        ]),
      ),
    );
  }
}

Product item

The ProductItemView component lets you display product information. For example, you can show product item with details in a list.

Product card

Code summary

You can customize ProductItemView in:

Usage and props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
class ProductItemView extends StatelessWidget {
  const ProductItemView(
      {Key? key,
      required this.product,
      this.imageAlignment = Alignment.center,
      this.onProductPressed})
      : super(key: key);

  final Product product;
  final Alignment imageAlignment;
  final Function(String)? onProductPressed;

  @override
  Widget build(BuildContext context) {
    final priceValue = (product.price?.onSales ?? false)
        ? product.price?.discountedValue
        : product.price?.value;
    final crossedValue =
        (product.price?.onSales ?? false) ? product.price?.value : null;
    return GestureDetector(
      onTap: () {
        onProductPressed?.call(product.objectID!);
      },
      child: SizedBox(
        child: Row(mainAxisSize: MainAxisSize.min, children: [
          Stack(
            alignment: AlignmentDirectional.bottomStart,
            children: [
              SizedBox(
                  height: 100,
                  width: 100,
                  child: Image.network('${product.image}',
                      alignment: imageAlignment, fit: BoxFit.cover)),
              if (product.price?.onSales == true)
                Padding(
                    padding: const EdgeInsets.all(4.0),
                    child: Text(
                      " ON SALE ${product.price?.discountLevel}% ",
                      style: Theme.of(context).textTheme.caption?.copyWith(
                          color: Colors.white,
                          backgroundColor: AppTheme.darkPink),
                    ))
            ],
          ),
          const SizedBox(width: 8),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text('${product.brand}',
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                    softWrap: false,
                    style: Theme.of(context).textTheme.caption),
                Text('${product.name}',
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                    softWrap: false,
                    style: Theme.of(context).textTheme.bodyText1),
                if (product.description?.isNotEmpty == true)
                  Text('${product.description}',
                      maxLines: 2,
                      overflow: TextOverflow.ellipsis,
                      softWrap: false,
                      style: Theme.of(context)
                          .textTheme
                          .bodyText2
                          ?.copyWith(fontSize: 12, color: Colors.grey)),
                Padding(
                  padding: const EdgeInsets.symmetric(vertical: 2.0),
                  child: ColorIndicatorView(product: product),
                ),
                Row(
                  children: [
                    Text('$priceValue €',
                        maxLines: 1,
                        overflow: TextOverflow.clip,
                        softWrap: false,
                        style: Theme.of(context).textTheme.bodyText2?.copyWith(
                            fontWeight: FontWeight.bold,
                            color: AppTheme.vividOrange)),
                    if (crossedValue != null)
                      Padding(
                        padding: const EdgeInsets.only(left: 8.0),
                        child: Text('$crossedValue €',
                            maxLines: 1,
                            overflow: TextOverflow.clip,
                            softWrap: false,
                            style: Theme.of(context)
                                .textTheme
                                .caption
                                ?.copyWith(
                                    decoration: TextDecoration.lineThrough)),
                      ),
                  ],
                ),
                RatingView(
                    value: product.reviews?.rating?.toInt() ?? 0,
                    reviewsCount: product.reviews?.count?.toInt() ?? 0),
              ],
            ),
          ),
        ]),
      ),
    );
  }
}
Did you find this page helpful?