image

11 Mar 2026

9K

35K

Building a Product Quick View Modal with Animation and Details in Flutter

In the competitive world of e-commerce, user experience is paramount. One feature that significantly enhances the shopping journey is a "Product Quick View" modal. This allows users to get essential product information and make quick decisions without navigating away from the main product listing page. This article will guide you through building a professional Product Quick View modal in Flutter, complete with smooth animations and comprehensive details.

Why a Quick View Modal?

  • Improved User Experience: Users can preview product details without full page loads.
  • Faster Decision Making: Key information is presented concisely, reducing friction in the buying process.
  • Engaging Interaction: Animations and interactive elements make the experience more dynamic.

Prerequisites

To follow along, you should have a basic understanding of Flutter development, including widgets, state management (stateless/stateful widgets), and routing.

Step 1: Define the Product Model

First, let's create a simple Dart class to represent our product data. This model will hold all the information we want to display in our quick view modal.


class Product {
  final String id;
  final String name;
  final String description;
  final double price;
  final String imageUrl;

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

Step 2: Create the Product Card Widget

Next, we'll build a widget to display each product in a grid or list. This widget will also contain a button to trigger our quick view modal. We'll introduce the

Hero
widget here to enable a beautiful shared-element animation for the product image.


import 'package:flutter/material.dart';
// Assume Product model is defined in product_model.dart or above

class ProductCard extends StatelessWidget {
  final Product product;
  final VoidCallback onQuickViewPressed;

  const ProductCard({
    Key? key,
    required this.product,
    required this.onQuickViewPressed,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 2,
      margin: const EdgeInsets.all(8),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Hero(
            tag: 'productImage-${product.id}', // Unique tag for Hero animation
            child: Image.network(
              product.imageUrl,
              height: 120,
              width: double.infinity,
              fit: BoxFit.cover,
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  product.name,
                  style: const TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 16,
                  ),
                ),
                Text('\$${product.price.toStringAsFixed(2)}'),
                Align(
                  alignment: Alignment.centerRight,
                  child: ElevatedButton(
                    onPressed: onQuickViewPressed,
                    child: const Text('Quick View'),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

Step 3: Implement the Quick View Modal Widget

For the quick view modal, we'll use

showModalBottomSheet
combined with
DraggableScrollableSheet
. This combination provides a native-feeling bottom sheet that users can drag up and down, and it also comes with built-in entry/exit animations. The
Hero
widget will be used again here to complete the image animation.


import 'package:flutter/material.dart';
// Assume Product model is defined in product_model.dart or above

class ProductQuickViewModal extends StatelessWidget {
  final Product product;

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

  @override
  Widget build(BuildContext context) {
    return DraggableScrollableSheet(
      initialChildSize: 0.8, // Start at 80% height
      minChildSize: 0.5,
      maxChildSize: 1.0,
      expand: false, 
      builder: (context, scrollController) {
        return Container(
          decoration: const BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
          ),
          child: Column(
            children: [
              // Drag handle
              Container(
                margin: const EdgeInsets.symmetric(vertical: 10),
                height: 4,
                width: 40,
                decoration: BoxDecoration(
                  color: Colors.grey[300],
                  borderRadius: BorderRadius.circular(2),
                ),
              ),
              Expanded(
                child: ListView(
                  controller: scrollController,
                  padding: const EdgeInsets.all(16.0),
                  children: [
                    Align(
                      alignment: Alignment.topRight,
                      child: IconButton(
                        icon: const Icon(Icons.close),
                        onPressed: () => Navigator.of(context).pop(),
                      ),
                    ),
                    Hero(
                      tag: 'productImage-${product.id}', // Must match the tag in ProductCard
                      child: Image.network(
                        product.imageUrl,
                        height: 250,
                        width: double.infinity,
                        fit: BoxFit.contain, // Changed to contain for better modal view
                      ),
                    ),
                    const SizedBox(height: 16),
                    Text(
                      product.name,
                      style: const TextStyle(
                        fontSize: 24,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 8),
                    Text(
                      '\$${product.price.toStringAsFixed(2)}',
                      style: TextStyle(
                        fontSize: 20,
                        color: Theme.of(context).primaryColor,
                        fontWeight: FontWeight.w600,
                      ),
                    ),
                    const SizedBox(height: 16),
                    Text(
                      product.description,
                      style: const TextStyle(fontSize: 16),
                    ),
                    const SizedBox(height: 24),
                    SizedBox(
                      width: double.infinity,
                      child: ElevatedButton.icon(
                        onPressed: () {
                          // Handle Add to Cart logic
                          ScaffoldMessenger.of(context).showSnackBar(
                            SnackBar(content: Text('${product.name} added to cart!')),
                          );
                          Navigator.of(context).pop(); // Close modal
                        },
                        icon: const Icon(Icons.shopping_cart),
                        label: const Text('Add to Cart'),
                        style: ElevatedButton.styleFrom(
                          padding: const EdgeInsets.symmetric(vertical: 12),
                          textStyle: const TextStyle(fontSize: 18),
                        ),
                      ),
                    ),
                    const SizedBox(height: 20), // Padding for the bottom
                  ],
                ),
              ),
            ],
          ),
        );
      },
    );
  }
}

Step 4: Integrating and Animating

Now, let's put it all together in a main application screen that displays a grid of products. When the "Quick View" button is pressed, the

_showProductQuickView
function will be called, which then uses
showModalBottomSheet
to present our
ProductQuickViewModal
.

The

Hero
widgets automatically handle the animation between the image in the
ProductCard
and the image in the
ProductQuickViewModal
, as long as they share the same
tag
. The
showModalBottomSheet
and
DraggableScrollableSheet
provide the bottom-up entry animation and drag functionality.


import 'package:flutter/material.dart';
// Import your Product model and widgets
// import 'product_model.dart';
// import 'product_card.dart';
// import 'product_quick_view_modal.dart';

class ProductGridScreen extends StatelessWidget {
  final List products = [
    Product(
      id: '1',
      name: 'Wireless Headphones',
      description: 'Experience crystal clear audio with these premium wireless headphones. Long-lasting battery, active noise cancellation, and comfortable design for all-day wear.',
      price: 129.99,
      imageUrl: 'https://via.placeholder.com/150/FF0000/FFFFFF?text=Headphones',
    ),
    Product(
      id: '2',
      name: 'Smart Watch',
      description: 'Stay connected and track your fitness goals with this stylish smart watch. Features include heart rate monitoring, GPS, and notifications for calls and messages.',
      price: 89.99,
      imageUrl: 'https://via.placeholder.com/150/0000FF/FFFFFF?text=Smart+Watch',
    ),
    Product(
      id: '3',
      name: 'Portable Speaker',
      description: 'Take your music anywhere with this compact and powerful portable speaker. Waterproof design for outdoor adventures, rich bass, and 10 hours of playtime.',
      price: 49.99,
      imageUrl: 'https://via.placeholder.com/150/00FF00/FFFFFF?text=Speaker',
    ),
    Product(
      id: '4',
      name: 'E-reader',
      description: 'Enjoy reading on the go with this lightweight e-reader. Glare-free display, adjustable front light, and weeks of battery life.',
      price: 99.99,
      imageUrl: 'https://via.placeholder.com/150/FFFF00/000000?text=E-reader',
    ),
  ];

  ProductGridScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Our Products'),
      ),
      body: GridView.builder(
        padding: const EdgeInsets.all(8.0),
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          crossAxisSpacing: 8.0,
          mainAxisSpacing: 8.0,
          childAspectRatio: 0.7, // Adjust as needed
        ),
        itemCount: products.length,
        itemBuilder: (context, index) {
          final product = products[index];
          return ProductCard(
            product: product,
            onQuickViewPressed: () {
              _showProductQuickView(context, product);
            },
          );
        },
      ),
    );
  }

  void _showProductQuickView(BuildContext context, Product product) {
    showModalBottomSheet(
      context: context,
      isScrollControlled: true, // Allows modal to take up more screen space
      backgroundColor: Colors.transparent, // Ensures rounded corners are visible
      builder: (context) {
        return ProductQuickViewModal(product: product);
      },
    );
  }
}

void main() {
  runApp(
    MaterialApp(
      title: 'Product Quick View Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: ProductGridScreen(),
    ),
  );
}

Conclusion

You have successfully built a sophisticated Product Quick View modal in Flutter. By combining

showModalBottomSheet
with
DraggableScrollableSheet
for an interactive, animated modal, and leveraging the
Hero
widget for seamless image transitions, you've created a highly engaging and user-friendly component. This pattern can be easily adapted and extended for various e-commerce applications, significantly improving the overall user experience.

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