E-commerce

Course Detail Screen

A complete e-learning home screen built in Flutter — featuring a personalized greeting header, search bar, gradient promotional banner, horizontally scrollable mentor avatars, category filter chips, and course cards with pricing, ratings and student counts. Drops straight into any online learning, course-marketplace or skill-building app.

e-learningcardshome

WHAT'S INCLUDED

  • Greeting header with user avatar, bell and bookmark action icons
  • Rounded search bar with built-in filter trailing button
  • Blue gradient "Today's Special" promo banner with carousel dots
  • Horizontally scrolling Top Mentors row with circular avatars
  • Category filter chips with selected/unselected states and icons
  • Course cards showing image, tag, title, discounted price and rating
  • Bottom navigation bar with five labeled destinations

USE CASES

  • Online course marketplace home screen (Udemy, Coursera style)
  • Skill-building or bootcamp app discovery feed
  • Tutoring marketplace where users browse mentors and classes
  • Any e-commerce vertical with category chips + product cards

PRO TIP

Wrap the entire scrollable area in a CustomScrollView with SliverList sections so the mentors row, categories and course list stay performant once you load real data. Also extract each course card into its own widget — it will be reused on the search results, "see all" and bookmarks screens, so building it once saves you from three near-duplicate widgets.

Complete Dart Code

Copy this into your Flutter project. No external packages required.

dart
import 'package:flutter/material.dart';

class CourseDetailScreen extends StatefulWidget {
  const CourseDetailScreen({super.key});

  @override
  State<CourseDetailScreen> createState() => _CourseDetailScreenState();
}

class _CourseDetailScreenState extends State<CourseDetailScreen> {
  static const Color _primaryBlue = Color(0xFF335EF7);
  static const Color _bg = Color(0xFFF5F7FB);
  static const Color _textDark = Color(0xFF111827);
  static const Color _textMuted = Color(0xFF6B7280);

  final TextEditingController _searchController = TextEditingController();
  String _selectedCategory = 'All';
  int _selectedNavIndex = 0;

  final List<_Mentor> _mentors = const [
    _Mentor(name: 'Jacob', color: Color(0xFFFFD6C0)),
    _Mentor(name: 'Claire', color: Color(0xFFD0F0FF)),
    _Mentor(name: 'Priscilla', color: Color(0xFFFFE5F0)),
    _Mentor(name: 'Wade', color: Color(0xFFE0E7FF)),
    _Mentor(name: 'Kathry', color: Color(0xFFFEF3C7)),
  ];

  final List<_CategoryChip> _categories = const [
    _CategoryChip(label: 'All', icon: Icons.local_fire_department),
    _CategoryChip(label: '3D Design', icon: Icons.view_in_ar),
    _CategoryChip(label: 'Business', icon: Icons.business_center),
    _CategoryChip(label: 'Entrepreneurship', icon: Icons.lightbulb_outline),
    _CategoryChip(label: 'UI/UX Design', icon: Icons.design_services),
  ];

  final List<_Course> _courses = const [
    _Course(
      tag: '3D Design',
      title: '3D Design Illustration',
      price: 48,
      originalPrice: 80,
      rating: 4.8,
      students: '8,289',
      coverColor: Color(0xFF1E3A8A),
    ),
    _Course(
      tag: 'Entrepreneurship',
      title: 'Digital Entrepreneur...',
      price: 39,
      originalPrice: null,
      rating: 4.9,
      students: '6,182',
      coverColor: Color(0xFF92400E),
    ),
    _Course(
      tag: 'UI/UX Design',
      title: 'Learn UX User Persona',
      price: 42,
      originalPrice: 75,
      rating: 4.7,
      students: '7,938',
      coverColor: Color(0xFFFBBF24),
    ),
  ];

  @override
  void dispose() {
    _searchController.dispose();
    super.dispose();
  }

  // TODO: Replace with your actual API/Firestore call
  void _onCoursePressed(_Course course) {}

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: _bg,
      body: SafeArea(
        child: SingleChildScrollView(
          padding: const EdgeInsets.fromLTRB(20, 12, 20, 20),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              _buildHeader(),
              const SizedBox(height: 16),
              _buildSearchBar(),
              const SizedBox(height: 20),
              _buildPromoBanner(),
              const SizedBox(height: 22),
              _buildSectionHeader('Top Mentors', 'See All'),
              const SizedBox(height: 14),
              _buildMentorsRow(),
              const SizedBox(height: 22),
              _buildSectionHeader('Most Popular Courses', 'See All'),
              const SizedBox(height: 14),
              _buildCategoryChips(),
              const SizedBox(height: 16),
              ..._courses.map(_buildCourseCard).toList(),
              const SizedBox(height: 24),
            ],
          ),
        ),
      ),
      bottomNavigationBar: _buildBottomNav(),
    );
  }

  Widget _buildHeader() {
    return Row(
      children: [
        const CircleAvatar(
          radius: 22,
          backgroundColor: Color(0xFFE5E7EB),
          child: Icon(Icons.person, color: _textMuted),
        ),
        const SizedBox(width: 12),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: const [
              Text(
                'Good Morning 👋',
                style: TextStyle(fontSize: 13, color: _textMuted),
              ),
              SizedBox(height: 2),
              Text(
                'Andrew Ainsley',
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.w700,
                  color: _textDark,
                ),
              ),
            ],
          ),
        ),
        const Icon(Icons.notifications_none, color: _textDark),
        const SizedBox(width: 12),
        const Icon(Icons.bookmark_border, color: _textDark),
      ],
    );
  }

  Widget _buildSearchBar() {
    return Container(
      height: 52,
      padding: const EdgeInsets.symmetric(horizontal: 16),
      decoration: BoxDecoration(
        color: const Color(0xFFEEF1F6),
        borderRadius: BorderRadius.circular(14),
      ),
      child: Row(
        children: [
          const Icon(Icons.search, color: _textMuted, size: 22),
          const SizedBox(width: 10),
          Expanded(
            child: TextField(
              controller: _searchController,
              decoration: const InputDecoration(
                hintText: 'Search',
                hintStyle: TextStyle(color: _textMuted, fontSize: 15),
                border: InputBorder.none,
                isCollapsed: true,
              ),
            ),
          ),
          Container(width: 1, height: 22, color: const Color(0xFFD1D5DB)),
          const SizedBox(width: 12),
          const Icon(Icons.tune, color: _primaryBlue, size: 22),
        ],
      ),
    );
  }

  Widget _buildPromoBanner() {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.fromLTRB(20, 18, 20, 18),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(16),
        gradient: const LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: [Color(0xFF4A6CF7), Color(0xFF335EF7)],
        ),
      ),
      child: Stack(
        children: [
          Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Container(
                padding:
                    const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
                decoration: BoxDecoration(
                  color: Colors.white.withOpacity(0.18),
                  borderRadius: BorderRadius.circular(20),
                ),
                child: const Text(
                  '40% OFF',
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 11,
                    fontWeight: FontWeight.w700,
                  ),
                ),
              ),
              const SizedBox(height: 10),
              const Text(
                "Today's Special",
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 20,
                  fontWeight: FontWeight.w700,
                ),
              ),
              const SizedBox(height: 6),
              const SizedBox(
                width: 200,
                child: Text(
                  'Get a discount for every course order!\nOnly valid for today!',
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 12,
                    height: 1.4,
                  ),
                ),
              ),
              const SizedBox(height: 12),
              Row(children: [
                _buildDot(true),
                const SizedBox(width: 6),
                _buildDot(false),
                const SizedBox(width: 6),
                _buildDot(false),
              ]),
            ],
          ),
          Positioned(
            right: 0,
            top: 0,
            child: Text(
              '40%',
              style: TextStyle(
                color: Colors.white.withOpacity(0.95),
                fontSize: 44,
                fontWeight: FontWeight.w800,
                height: 1,
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildDot(bool active) {
    return Container(
      width: active ? 18 : 6,
      height: 6,
      decoration: BoxDecoration(
        color: Colors.white.withOpacity(active ? 1 : 0.45),
        borderRadius: BorderRadius.circular(4),
      ),
    );
  }

  Widget _buildSectionHeader(String title, String action) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Text(
          title,
          style: const TextStyle(
            fontSize: 17,
            fontWeight: FontWeight.w700,
            color: _textDark,
          ),
        ),
        Text(
          action,
          style: const TextStyle(
            fontSize: 13,
            fontWeight: FontWeight.w600,
            color: _primaryBlue,
          ),
        ),
      ],
    );
  }

  Widget _buildMentorsRow() {
    return SizedBox(
      height: 84,
      child: ListView.separated(
        scrollDirection: Axis.horizontal,
        itemCount: _mentors.length,
        separatorBuilder: (_, __) => const SizedBox(width: 18),
        itemBuilder: (_, index) {
          final mentor = _mentors[index];
          return Column(
            children: [
              CircleAvatar(
                radius: 28,
                backgroundColor: mentor.color,
                child: Text(
                  mentor.name.substring(0, 1),
                  style: const TextStyle(
                    color: _textDark,
                    fontWeight: FontWeight.w700,
                  ),
                ),
              ),
              const SizedBox(height: 8),
              Text(
                mentor.name,
                style: const TextStyle(fontSize: 12, color: _textDark),
              ),
            ],
          );
        },
      ),
    );
  }

  Widget _buildCategoryChips() {
    return SizedBox(
      height: 38,
      child: ListView.separated(
        scrollDirection: Axis.horizontal,
        itemCount: _categories.length,
        separatorBuilder: (_, __) => const SizedBox(width: 10),
        itemBuilder: (_, index) {
          final cat = _categories[index];
          final selected = cat.label == _selectedCategory;
          return GestureDetector(
            onTap: () => setState(() => _selectedCategory = cat.label),
            child: Container(
              padding:
                  const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
              decoration: BoxDecoration(
                color: selected ? _primaryBlue : Colors.white,
                borderRadius: BorderRadius.circular(22),
                border: Border.all(
                  color: selected ? _primaryBlue : _primaryBlue,
                  width: 1,
                ),
              ),
              child: Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(
                    cat.icon,
                    size: 16,
                    color: selected ? Colors.white : _primaryBlue,
                  ),
                  const SizedBox(width: 6),
                  Text(
                    cat.label,
                    style: TextStyle(
                      fontSize: 13,
                      fontWeight: FontWeight.w600,
                      color: selected ? Colors.white : _primaryBlue,
                    ),
                  ),
                ],
              ),
            ),
          );
        },
      ),
    );
  }

  Widget _buildCourseCard(_Course course) {
    return Container(
      margin: const EdgeInsets.only(bottom: 14),
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
      ),
      child: Material(
        color: Colors.transparent,
        child: InkWell(
          onTap: () => _onCoursePressed(course),
          borderRadius: BorderRadius.circular(12),
          child: Row(
            children: [
              Container(
                width: 86,
                height: 86,
                decoration: BoxDecoration(
                  color: course.coverColor,
                  borderRadius: BorderRadius.circular(12),
                ),
              ),
              const SizedBox(width: 14),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        Text(
                          course.tag,
                          style: const TextStyle(
                            fontSize: 11,
                            fontWeight: FontWeight.w600,
                            color: _primaryBlue,
                          ),
                        ),
                        const Icon(
                          Icons.bookmark_border,
                          size: 18,
                          color: _primaryBlue,
                        ),
                      ],
                    ),
                    const SizedBox(height: 4),
                    Text(
                      course.title,
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                      style: const TextStyle(
                        fontSize: 15,
                        fontWeight: FontWeight.w700,
                        color: _textDark,
                      ),
                    ),
                    const SizedBox(height: 6),
                    Row(
                      crossAxisAlignment: CrossAxisAlignment.end,
                      children: [
                        Text(
                          '\$${course.price}',
                          style: const TextStyle(
                            fontSize: 16,
                            fontWeight: FontWeight.w700,
                            color: _primaryBlue,
                          ),
                        ),
                        if (course.originalPrice != null) ...[
                          const SizedBox(width: 6),
                          Text(
                            '\$${course.originalPrice}',
                            style: const TextStyle(
                              fontSize: 12,
                              color: _textMuted,
                              decoration: TextDecoration.lineThrough,
                            ),
                          ),
                        ],
                      ],
                    ),
                    const SizedBox(height: 4),
                    Row(
                      children: [
                        const Icon(Icons.star, size: 14, color: Color(0xFFF59E0B)),
                        const SizedBox(width: 3),
                        Text(
                          course.rating.toString(),
                          style: const TextStyle(
                            fontSize: 12,
                            fontWeight: FontWeight.w600,
                            color: _textDark,
                          ),
                        ),
                        const SizedBox(width: 8),
                        Container(
                            width: 3,
                            height: 3,
                            decoration: const BoxDecoration(
                              color: _textMuted,
                              shape: BoxShape.circle,
                            )),
                        const SizedBox(width: 8),
                        Text(
                          '${course.students} students',
                          style: const TextStyle(
                            fontSize: 12,
                            color: _textMuted,
                          ),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildBottomNav() {
    const items = [
      _NavItem(label: 'Home', icon: Icons.home_outlined),
      _NavItem(label: 'My Course', icon: Icons.menu_book_outlined),
      _NavItem(label: 'Inbox', icon: Icons.chat_bubble_outline),
      _NavItem(label: 'Transact.', icon: Icons.swap_horiz),
      _NavItem(label: 'Profile', icon: Icons.person_outline),
    ];
    return Container(
      decoration: const BoxDecoration(
        color: Colors.white,
        boxShadow: [
          BoxShadow(
            color: Color(0x14000000),
            blurRadius: 16,
            offset: Offset(0, -2),
          ),
        ],
      ),
      child: SafeArea(
        top: false,
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 10),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: List.generate(items.length, (index) {
              final item = items[index];
              final selected = index == _selectedNavIndex;
              return GestureDetector(
                onTap: () => setState(() => _selectedNavIndex = index),
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Icon(
                      item.icon,
                      size: 22,
                      color: selected ? _primaryBlue : _textMuted,
                    ),
                    const SizedBox(height: 4),
                    Text(
                      item.label,
                      style: TextStyle(
                        fontSize: 11,
                        fontWeight:
                            selected ? FontWeight.w700 : FontWeight.w500,
                        color: selected ? _primaryBlue : _textMuted,
                      ),
                    ),
                  ],
                ),
              );
            }),
          ),
        ),
      ),
    );
  }
}

class _Mentor {
  final String name;
  final Color color;
  const _Mentor({required this.name, required this.color});
}

class _CategoryChip {
  final String label;
  final IconData icon;
  const _CategoryChip({required this.label, required this.icon});
}

class _Course {
  final String tag;
  final String title;
  final int price;
  final int? originalPrice;
  final double rating;
  final String students;
  final Color coverColor;

  const _Course({
    required this.tag,
    required this.title,
    required this.price,
    required this.originalPrice,
    required this.rating,
    required this.students,
    required this.coverColor,
  });
}

class _NavItem {
  final String label;
  final IconData icon;
  const _NavItem({required this.label, required this.icon});
}

How to use this widget

  1. 1Copy the Dart code above
  2. 2Create a new file — course_detail_screen.dart
  3. 3Paste the code and add to your route. Replace the TODO comment with your own course-fetching API or Firestore call.

Related components

Auth & Onboarding

Login Screen

Clean email + password login with form validation and loading state

View component →
Auth & Onboarding

Sign Up with Role Selector

Registration screen with role selection grid — Owner, Manager, or Employee

View component →
Auth & Onboarding

Splash Screen

Animated splash with logo fade-in and auto-navigate after delay

View component →