image

14 Mar 2026

9K

35K

Building a Product Detail Page Widget with Related Items and Review Carousel in Flutter

A well-designed Product Detail Page (PDP) is crucial for any e-commerce application, providing users with comprehensive information about a product and influencing their purchase decisions. In Flutter, creating a robust and engaging PDP involves combining various widgets to display product details, highlight related items, and showcase user reviews. This article will guide you through building a modular Product Detail Page widget, complete with a horizontal scrollable list for related products and a carousel for customer reviews.

Understanding the Product Detail Page Structure

A typical Product Detail Page comprises several key sections:

  • Product Header: Product image(s), name, brand, price, and primary call-to-actions (e.g., "Add to Cart").
  • Product Description: Detailed text about the product's features and specifications.
  • Related Items: A section suggesting other products that might interest the user, often displayed in a horizontal scrollable list.
  • Customer Reviews: A dedicated area for users to read and submit reviews, frequently presented as a carousel for easy browsing.

Designing Data Models

Before building the UI, let's define simple data models for our Product and Review objects.

Product Model

This model will hold basic product information.


// lib/models/product.dart
class Product {
  final String id;
  final String name;
  final String imageUrl;
  final String description;
  final double price;
  final double rating;

  Product({
    required this.id,
    required this.name,
    required this.imageUrl,
    required this.description,
    required this.price,
    required this.rating,
  });

  // Example factory constructor for demo purposes
  factory Product.fromJson(Map<String, dynamic> json) {
    return Product(
      id: json['id'],
      name: json['name'],
      imageUrl: json['imageUrl'],
      description: json['description'],
      price: json['price'].toDouble(),
      rating: json['rating'].toDouble(),
    );
  }
}

Review Model

This model will store review details, including the reviewer's name, rating, and comments.


// lib/models/review.dart
class Review {
  final String id;
  final String reviewerName;
  final String comment;
  final double rating;
  final DateTime date;

  Review({
    required this.id,
    required this.reviewerName,
    required this.comment,
    required this.rating,
    required this.date,
  });

  // Example factory constructor for demo purposes
  factory Review.fromJson(Map<String, dynamic> json) {
    return Review(
      id: json['id'],
      reviewerName: json['reviewerName'],
      comment: json['comment'],
      rating: json['rating'].toDouble(),
      date: DateTime.parse(json['date']),
    );
  }
}

Building the Main Product Detail Widget

The main Product Detail Page widget will be a StatefulWidget or StatelessWidget depending on whether data is fetched internally or passed externally. For simplicity, we'll assume data is passed in or fetched in a parent widget. We'll use a SingleChildScrollView to ensure the page is scrollable, containing a Column to stack our different sections vertically.


// lib/widgets/product_detail_page.dart
import 'package:flutter/material.dart';
import '../models/product.dart';
import '../models/review.dart';
import 'related_products_widget.dart';
import 'review_carousel_widget.dart';

class ProductDetailPage extends StatelessWidget {
  final Product product;
  final List<Product> relatedProducts;
  final List<Review> reviews;

  const ProductDetailPage({
    Key? key,
    required this.product,
    required this.relatedProducts,
    required this.reviews,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(product.name),
      ),
      body: SingleChildScrollView(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // Product Image
            Image.network(
              product.imageUrl,
              height: 300,
              width: double.infinity,
              fit: BoxFit.cover,
              loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
                if (loadingProgress == null) return child;
                return Center(
                  child: CircularProgressIndicator(
                    value: loadingProgress.expectedTotalBytes != null
                        ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes!
                        : null,
                  ),
                );
              },
              errorBuilder: (context, error, stackTrace) =>
                  const Icon(Icons.error, size: 100),
            ),
            Padding(
              padding: const EdgeInsets.all(16.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  // Product Name
                  Text(
                    product.name,
                    style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
                  ),
                  const SizedBox(height: 8),
                  // Product Price
                  Text(
                    '\$${product.price.toStringAsFixed(2)}',
                    style: const TextStyle(fontSize: 22, color: Colors.green, fontWeight: FontWeight.w600),
                  ),
                  const SizedBox(height: 8),
                  // Product Rating
                  Row(
                    children: [
                      Icon(Icons.star, color: Colors.amber, size: 20),
                      const SizedBox(width: 4),
                      Text('${product.rating.toStringAsFixed(1)} out of 5', style: TextStyle(fontSize: 16)),
                    ],
                  ),
                  const SizedBox(height: 16),
                  // Product Description
                  Text(
                    product.description,
                    style: const TextStyle(fontSize: 16),
                  ),
                  const SizedBox(height: 24),
                  // Related Items Section
                  if (relatedProducts.isNotEmpty) ...[
                    const Text(
                      'Related Products',
                      style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
                    ),
                    const SizedBox(height: 16),
                    RelatedProductsWidget(products: relatedProducts),
                    const SizedBox(height: 24),
                  ],
                  // Reviews Section
                  if (reviews.isNotEmpty) ...[
                    const Text(
                      'Customer Reviews',
                      style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
                    ),
                    const SizedBox(height: 16),
                    ReviewCarouselWidget(reviews: reviews),
                    const SizedBox(height: 24),
                  ],
                ],
              ),
            ),
          ],
        ),
      ),
      bottomNavigationBar: Padding(
        padding: const EdgeInsets.all(16.0),
        child: ElevatedButton(
          onPressed: () {
            // Implement add to cart logic
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('${product.name} added to cart!')),
            );
          },
          style: ElevatedButton.styleFrom(
            minimumSize: const Size(double.infinity, 50), // full width
            padding: const EdgeInsets.symmetric(vertical: 12),
            textStyle: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(10),
            ),
          ),
          child: const Text('Add to Cart'),
        ),
      ),
    );
  }
}

Crafting the Related Items Widget

The RelatedProductsWidget will display a horizontal list of product cards. We'll use a SizedBox with a fixed height to contain a ListView.builder with scrollDirection: Axis.horizontal.


// lib/widgets/related_products_widget.dart
import 'package:flutter/material.dart';
import '../models/product.dart';

class RelatedProductsWidget extends StatelessWidget {
  final List<Product> products;

  const RelatedProductsWidget({Key? key, required this.products}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 200, // Fixed height for the horizontal list
      child: ListView.builder(
        scrollDirection: Axis.horizontal,
        itemCount: products.length,
        itemBuilder: (context, index) {
          final product = products[index];
          return Container(
            width: 150,
            margin: const EdgeInsets.only(right: 16),
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(12),
              boxShadow: [
                BoxShadow(
                  color: Colors.grey.withOpacity(0.2),
                  spreadRadius: 1,
                  blurRadius: 5,
                  offset: const Offset(0, 3),
                ),
              ],
            ),
            child: GestureDetector(
              onTap: () {
                // Navigate to related product detail page
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text('Tapped on related product: ${product.name}')),
                );
                // Example: Navigator.push(context, MaterialPageRoute(builder: (context) => ProductDetailPage(product: product, ...)));
              },
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  ClipRRect(
                    borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
                    child: Image.network(
                      product.imageUrl,
                      height: 100,
                      width: double.infinity,
                      fit: BoxFit.cover,
                      errorBuilder: (context, error, stackTrace) =>
                          const Icon(Icons.broken_image, size: 80),
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          product.name,
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis,
                          style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
                        ),
                        const SizedBox(height: 4),
                        Text(
                          '\$${product.price.toStringAsFixed(2)}',
                          style: const TextStyle(color: Colors.green, fontSize: 13),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}

Creating the Review Carousel

The ReviewCarouselWidget will display customer reviews in a horizontally scrollable format. A PageView.builder is an excellent choice for this, offering snapping behavior for each review card. Alternatively, a ListView.builder can also be used.


// lib/widgets/review_carousel_widget.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../models/review.dart';

class ReviewCarouselWidget extends StatelessWidget {
  final List<Review> reviews;

  const ReviewCarouselWidget({Key? key, required this.reviews}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 180, // Fixed height for the review carousel
      child: PageView.builder( // Using PageView for a carousel effect
        itemCount: reviews.length,
        itemBuilder: (context, index) {
          final review = reviews[index];
          return Container(
            margin: const EdgeInsets.only(right: 16),
            padding: const EdgeInsets.all(16),
            decoration: BoxDecoration(
              color: Colors.blueGrey.shade50,
              borderRadius: BorderRadius.circular(12),
              boxShadow: [
                BoxShadow(
                  color: Colors.grey.withOpacity(0.1),
                  spreadRadius: 1,
                  blurRadius: 3,
                  offset: const Offset(0, 2),
                ),
              ],
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row(
                  children: [
                    Text(
                      review.reviewerName,
                      style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                    ),
                    const Spacer(),
                    Icon(Icons.star, color: Colors.amber, size: 18),
                    Text(
                      '${review.rating.toStringAsFixed(1)}',
                      style: const TextStyle(fontSize: 14),
                    ),
                  ],
                ),
                const SizedBox(height: 8),
                Expanded(
                  child: Text(
                    review.comment,
                    maxLines: 3,
                    overflow: TextOverflow.ellipsis,
                    style: const TextStyle(fontSize: 14),
                  ),
                ),
                const SizedBox(height: 8),
                Text(
                  DateFormat('MMM dd, yyyy').format(review.date),
                  style: const TextStyle(fontSize: 12, color: Colors.grey),
                ),
              ],
            ),
          );
        },
      ),
    );
  }
}

Integrating All Components and Mock Data

To see our PDP in action, let's create some mock data and use the ProductDetailPage in a simple Flutter app.


// lib/main.dart
import 'package:flutter/material.dart';
import 'models/product.dart';
import 'models/review.dart';
import 'widgets/product_detail_page.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // Mock Data
    final Product mainProduct = Product(
      id: 'p1',
      name: 'Smartwatch Pro X',
      imageUrl: 'https://images.unsplash.com/photo-1546868871-7041f2a55e12?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1700&q=80',
      description:
          'The Smartwatch Pro X is a sleek and powerful wearable device designed to keep you connected and on track with your fitness goals. Featuring a vibrant AMOLED display, advanced health monitoring, and long-lasting battery life. Integrated GPS and water resistance make it perfect for any adventure.',
      price: 199.99,
      rating: 4.7,
    );

    final List<Product> relatedProducts = [
      Product(
        id: 'rp1',
        name: 'Wireless Earbuds',
        imageUrl: 'https://images.unsplash.com/photo-1606775791782-2d189a696238?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1700&q=80',
        description: 'High-fidelity audio with active noise cancellation.',
        price: 89.99,
        rating: 4.5,
      ),
      Product(
        id: 'rp2',
        name: 'Fitness Tracker Band',
        imageUrl: 'https://images.unsplash.com/photo-1579586337278-f73922979e38?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1700&q=80',
        description: 'Track your steps, heart rate, and sleep.',
        price: 45.00,
        rating: 4.2,
      ),
      Product(
        id: 'rp3',
        name: 'Portable Charger',
        imageUrl: 'https://images.unsplash.com/photo-1588872657578-fu3d50013093?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1700&q=80',
        description: 'Keep your devices charged on the go.',
        price: 29.99,
        rating: 4.6,
      ),
       Product(
        id: 'rp4',
        name: 'Smart Scale',
        imageUrl: 'https://images.unsplash.com/photo-1549484738-4e42702a4501?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1700&q=80',
        description: 'Monitor your health metrics with precision.',
        price: 65.00,
        rating: 4.3,
      ),
    ];

    final List<Review> reviews = [
      Review(
        id: 'r1',
        reviewerName: 'Alice W.',
        comment: 'Absolutely love this smartwatch! The battery life is incredible, and it tracks my workouts perfectly. Highly recommend!',
        rating: 5.0,
        date: DateTime(2023, 10, 26),
      ),
      Review(
        id: 'r2',
        reviewerName: 'Bob J.',
        comment: 'Great product for the price. The screen is vibrant, and notifications are easy to read. Wish the app had more features.',
        rating: 4.0,
        date: DateTime(2023, 10, 20),
      ),
      Review(
        id: 'r3',
        reviewerName: 'Charlie K.',
        comment: 'Sleek design and comfortable to wear. I mainly use it for step tracking and sleep monitoring, and it does a fantastic job.',
        rating: 4.5,
        date: DateTime(2023, 10, 15),
      ),
      Review(
        id: 'r4',
        reviewerName: 'Diana L.',
        comment: 'A good entry-level smartwatch. It covers the basics well. Setup was a bit tricky, but worth it.',
        rating: 3.5,
        date: DateTime(2023, 10, 10),
      ),
    ];

    return MaterialApp(
      title: 'Product Detail Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blueGrey,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: ProductDetailPage(
        product: mainProduct,
        relatedProducts: relatedProducts,
        reviews: reviews,
      ),
    );
  }
}

Conclusion

By breaking down the Product Detail Page into manageable widgets like RelatedProductsWidget and ReviewCarouselWidget, we can create a highly modular, maintainable, and engaging user experience in Flutter. This approach leverages Flutter's rich set of widgets and declarative UI paradigm to build complex layouts with relative ease, enhancing both functionality and aesthetic appeal of your e-commerce application. Remember to adapt the styling and data fetching mechanisms to fit your application's specific needs and backend integrations.

Related Articles

May 14, 2026

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter Countdown timers are essential in many applic

May 11, 2026

Unleashing Dynamic UIs: Flutter's Animation Prowess

Unleashing Dynamic UIs: Flutter's Animation Prowess for Slide & Scale Effects Flutter's declarative UI framework, combined with its powerful animation capabilit

May 11, 2026

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy A well-designed Product Detail Page (PDP) is