Course Detail Screen
E-learning home with promo banner, mentors, category chips and course cards
View component →A premium Flutter course detail page with a 16:9 hero banner, floating glassy back/share buttons, category pill, rating row, mentor chip, switchable About/Curriculum/Reviews tabs, lesson cards with locked/unlocked states and a sticky bottom price + Enroll Now CTA. Drops straight into any e-learning, masterclass or bootcamp app.
WHAT'S INCLUDED
USE CASES
PRO TIP
Wrap the body in a CustomScrollView with a SliverAppBar (expandedHeight matching the hero) so the hero image pins and parallaxes as you scroll, while the sticky Enroll bar stays anchored. Drive the tab content with an IndexedStack instead of swapping widgets — it preserves scroll position when the user switches between About, Curriculum and Reviews.
Copy this into your Flutter project. No external packages required.
import 'package:flutter/material.dart';
class CourseDetailPage extends StatefulWidget {
const CourseDetailPage({super.key});
@override
State<CourseDetailPage> createState() => _CourseDetailPageState();
}
class _CourseDetailPageState extends State<CourseDetailPage> {
static const Color _primary = Color(0xFF0042DE);
static const Color _primaryContainer = Color(0xFF335EF7);
static const Color _surface = Color(0xFFFBF8FF);
static const Color _surfaceCard = Color(0xFFFFFFFF);
static const Color _surfaceContainerLow = Color(0xFFF3F2FF);
static const Color _surfaceContainer = Color(0xFFEDEDFA);
static const Color _textPrimary = Color(0xFF111827);
static const Color _textMuted = Color(0xFF6B7280);
static const Color _textBody = Color(0xFF434655);
static const Color _ratingAccent = Color(0xFFF59E0B);
static const Color _secondaryFixed = Color(0xFFDDE1FF);
static const Color _onSecondaryFixed = Color(0xFF001355);
static const Color _outline = Color(0xFF747687);
int _selectedTab = 0;
static const List<String> _tabs = <String>['About', 'Curriculum', 'Reviews'];
static const List<_Lesson> _lessons = <_Lesson>[
_Lesson(title: 'Introduction to Advanced UI', duration: '12:45 mins', locked: false),
_Lesson(title: 'Designing for Scalability', duration: '24:20 mins', locked: true),
_Lesson(title: 'Advanced Prototyping II', duration: '18:15 mins', locked: true),
];
Future<void> _handleEnroll() async {
// TODO: Replace with your enrollment / Stripe / Razorpay / Firestore call.
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: _surface,
bottomNavigationBar: _buildStickyFooter(),
body: SingleChildScrollView(
padding: EdgeInsets.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHero(context),
Transform.translate(
offset: const Offset(0, -24),
child: Container(
width: double.infinity,
decoration: const BoxDecoration(
color: _surface,
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
boxShadow: [
BoxShadow(color: Color(0x0A000000), blurRadius: 20, offset: Offset(0, -8)),
],
),
padding: const EdgeInsets.fromLTRB(20, 24, 20, 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildCategoryPill(),
const SizedBox(height: 8),
const Text(
'UI/UX Advanced',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.w700,
color: _textPrimary,
height: 1.2,
letterSpacing: -0.56,
),
),
const SizedBox(height: 16),
_buildRatingMentorRow(),
const SizedBox(height: 24),
_buildTabs(),
const SizedBox(height: 24),
_buildTabContent(),
],
),
),
),
],
),
),
);
}
Widget _buildHero(BuildContext context) {
return AspectRatio(
aspectRatio: 16 / 9,
child: Stack(
fit: StackFit.expand,
children: [
Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFF4568F3), Color(0xFF0042DE)],
),
),
),
Positioned(
top: -40,
left: -20,
child: Container(
width: 200,
height: 200,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white.withOpacity(0.12),
),
),
),
SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_glassButton(
icon: Icons.arrow_back,
onTap: () => Navigator.maybePop(context),
),
_glassButton(
icon: Icons.share_outlined,
onTap: () {
// TODO: Replace with your share sheet logic.
},
),
],
),
),
),
],
),
);
}
Widget _glassButton({required IconData icon, required VoidCallback onTap}) {
return Material(
color: Colors.white.withOpacity(0.85),
borderRadius: BorderRadius.circular(12),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.all(10),
child: Icon(icon, color: _textPrimary, size: 20),
),
),
);
}
Widget _buildCategoryPill() {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: _secondaryFixed,
borderRadius: BorderRadius.circular(999),
),
child: const Text(
'DESIGN',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w700,
letterSpacing: 1.6,
color: _onSecondaryFixed,
),
),
);
}
Widget _buildRatingMentorRow() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: const [
Icon(Icons.star_rounded, color: _ratingAccent, size: 22),
SizedBox(width: 4),
Text('4.8',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: _textPrimary)),
SizedBox(width: 4),
Text('(1.2k reviews)',
style: TextStyle(fontSize: 14, color: _textMuted)),
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: _surfaceContainerLow,
borderRadius: BorderRadius.circular(999),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 32,
height: 32,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFFB8C4FF), Color(0xFF4568F3)],
),
border: Border.all(color: _surface, width: 2),
),
alignment: Alignment.center,
child: const Text(
'S',
style: TextStyle(
color: _onSecondaryFixed,
fontWeight: FontWeight.w700,
fontSize: 14,
),
),
),
const SizedBox(width: 8),
const Text(
'Sarah',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: _textPrimary),
),
],
),
),
],
);
}
Widget _buildTabs() {
return Container(
decoration: const BoxDecoration(
border: Border(bottom: BorderSide(color: _surfaceContainer, width: 1)),
),
child: Row(
children: List<Widget>.generate(_tabs.length, (int i) {
final bool selected = i == _selectedTab;
return Expanded(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => setState(() => _selectedTab = i),
child: Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Column(
children: [
Text(
_tabs[i],
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: selected ? _primary : _textMuted,
),
),
const SizedBox(height: 12),
Container(
height: 2,
color: selected ? _primary : Colors.transparent,
),
],
),
),
),
);
}),
),
);
}
Widget _buildTabContent() {
switch (_selectedTab) {
case 1:
return _buildCurriculumOnly();
case 2:
return _buildReviewsPlaceholder();
case 0:
default:
return _buildAbout();
}
}
Widget _buildAbout() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Course Description',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: _textPrimary),
),
const SizedBox(height: 12),
const Text(
"Take your design skills to the next level with this comprehensive UI/UX advanced masterclass. You will learn professional workflows, complex prototyping, and user-centric design strategies used by top-tier product teams globally. We'll dive deep into Figma's latest features and real-world case studies.",
style: TextStyle(fontSize: 14, color: _textBody, height: 1.6),
),
const SizedBox(height: 32),
_buildCurriculumHeader(),
const SizedBox(height: 12),
..._lessons.map(_buildLessonCard),
],
);
}
Widget _buildCurriculumOnly() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildCurriculumHeader(),
const SizedBox(height: 12),
..._lessons.map(_buildLessonCard),
],
);
}
Widget _buildReviewsPlaceholder() {
return const SizedBox(
width: double.infinity,
child: Padding(
padding: EdgeInsets.symmetric(vertical: 32),
child: Text(
'Reviews coming soon',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, color: _textMuted),
),
),
);
}
Widget _buildCurriculumHeader() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Text('Curriculum',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: _textPrimary)),
Text('12 Lessons',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: _primary)),
],
);
}
Widget _buildLessonCard(_Lesson lesson) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: _surfaceCard,
borderRadius: BorderRadius.circular(12),
boxShadow: const [
BoxShadow(color: Color(0x0F000000), blurRadius: 16, offset: Offset(0, 4)),
],
),
child: Row(
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: _primaryContainer.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: const Icon(Icons.play_circle, color: _primary, size: 26),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
lesson.title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: _textPrimary,
),
),
const SizedBox(height: 2),
Text(
lesson.duration,
style: const TextStyle(fontSize: 12, color: _textMuted),
),
],
),
),
Icon(
lesson.locked ? Icons.lock_outline : Icons.chevron_right,
color: _outline,
size: 22,
),
],
),
),
);
}
Widget _buildStickyFooter() {
return Container(
decoration: const BoxDecoration(
color: _surfaceCard,
boxShadow: [
BoxShadow(color: Color(0x14000000), blurRadius: 24, offset: Offset(0, -4)),
],
),
padding: const EdgeInsets.fromLTRB(20, 16, 20, 16),
child: SafeArea(
top: false,
child: Row(
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'TOTAL PRICE',
style: TextStyle(
fontSize: 12,
color: _textMuted,
letterSpacing: 0.4,
fontWeight: FontWeight.w600,
),
),
Text(
r'$49',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.w700,
color: _primary,
height: 1.1,
),
),
],
),
const SizedBox(width: 20),
Expanded(
child: SizedBox(
height: 52,
child: ElevatedButton(
onPressed: _handleEnroll,
style: ElevatedButton.styleFrom(
backgroundColor: _primaryContainer,
foregroundColor: Colors.white,
elevation: 4,
shadowColor: _primaryContainer.withOpacity(0.3),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
'Enroll Now',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700),
),
SizedBox(width: 12),
Icon(Icons.arrow_forward, size: 20),
],
),
),
),
),
],
),
),
);
}
}
class _Lesson {
final String title;
final String duration;
final bool locked;
const _Lesson({
required this.title,
required this.duration,
required this.locked,
});
}
E-learning home with promo banner, mentors, category chips and course cards
View component →Registration screen with role selection grid — Owner, Manager, or Employee
View component →Clean email + password login with form validation and loading state
View component →