Alert Widget
Dismissible alert banners in 5 styles — Info, Success, Warning, and Danger
View component →A reusable Flutter Stat Card widget for dashboards and analytics screens. One StatelessWidget driven by a StatCardStyle enum gives you 4 layouts — Simple text card, leading-icon card, trend-percentage card, and circular-progress card — covering nearly every dashboard metric you will ever need to render.
Total Revenue
$45,231.89
vs last month
Active Users
2,350
last 30 days
Conversions
12.5%
goal: 15%
New Orders
573
today
What's Included
Use Cases
PRO TIP
Pair StatCard with a GridView.count(crossAxisCount: 2) for a clean 2-column dashboard on mobile and crossAxisCount: 4 on tablets via LayoutBuilder. For animated number transitions on data refresh, wrap the value Text in a TweenAnimationBuilder<double> — it makes your dashboard feel alive without any extra packages.
Copy this into your Flutter project. No external packages required.
import 'package:flutter/material.dart';
/// Flutter Stat Card Widget — All Variants
/// Copy the StatCard class and switch layout via StatCardStyle.
/// rohansurve.in/flutterkit/stat-card
enum StatCardStyle { simple, withIcon, withTrend, withProgress }
class StatCard extends StatelessWidget {
final StatCardStyle style;
final String label;
final String value;
final String? description;
final IconData? icon;
final Color iconBg;
final Color iconColor;
final double? trend;
final double? progress;
final Color accentColor;
const StatCard({
super.key,
required this.label,
required this.value,
this.style = StatCardStyle.simple,
this.description,
this.icon,
this.iconBg = const Color(0xFFF3E8FF),
this.iconColor = const Color(0xFF7C3AED),
this.trend,
this.progress,
this.accentColor = const Color(0xFF7C3AED),
});
@override
Widget build(BuildContext context) {
switch (style) {
case StatCardStyle.simple:
return _buildSimple();
case StatCardStyle.withIcon:
return _buildWithIcon();
case StatCardStyle.withTrend:
return _buildWithTrend();
case StatCardStyle.withProgress:
return _buildWithProgress();
}
}
Widget _shell({required Widget child}) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFEEEEEE)),
),
child: child,
);
}
Widget _buildSimple() {
return _shell(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label,
style: const TextStyle(
fontSize: 13,
color: Color(0xFF888888),
fontWeight: FontWeight.w500)),
const SizedBox(height: 8),
Text(value,
style: const TextStyle(
fontSize: 26,
fontWeight: FontWeight.w600,
color: Color(0xFF111111))),
if (description != null) ...[
const SizedBox(height: 6),
Text(description!,
style: const TextStyle(
fontSize: 12, color: Color(0xFFAAAAAA))),
],
],
),
);
}
Widget _buildWithIcon() {
return _shell(
child: Row(
children: [
Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: iconBg,
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon ?? Icons.analytics_rounded,
color: iconColor, size: 22),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label,
style: const TextStyle(
fontSize: 12,
color: Color(0xFF888888),
fontWeight: FontWeight.w500)),
const SizedBox(height: 4),
Text(value,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: Color(0xFF111111))),
],
),
),
],
),
);
}
Widget _buildWithTrend() {
final isPositive = (trend ?? 0) >= 0;
final trendColor = isPositive
? const Color(0xFF15803D)
: const Color(0xFFBE123C);
final trendBg = isPositive
? const Color(0xFFF0FDF4)
: const Color(0xFFFFF1F2);
return _shell(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label,
style: const TextStyle(
fontSize: 13,
color: Color(0xFF888888),
fontWeight: FontWeight.w500)),
if (trend != null)
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: trendBg,
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
isPositive
? Icons.arrow_upward_rounded
: Icons.arrow_downward_rounded,
color: trendColor,
size: 12),
const SizedBox(width: 2),
Text('${trend!.abs().toStringAsFixed(1)}%',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: trendColor)),
],
),
),
],
),
const SizedBox(height: 8),
Text(value,
style: const TextStyle(
fontSize: 26,
fontWeight: FontWeight.w600,
color: Color(0xFF111111))),
],
),
);
}
Widget _buildWithProgress() {
final pct = (progress ?? 0).clamp(0.0, 1.0);
return _shell(
child: Row(
children: [
SizedBox(
width: 56,
height: 56,
child: Stack(
alignment: Alignment.center,
children: [
CircularProgressIndicator(
value: pct,
strokeWidth: 6,
backgroundColor: const Color(0xFFEDE9FE),
valueColor: AlwaysStoppedAnimation<Color>(accentColor),
),
Text('${(pct * 100).round()}%',
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: Color(0xFF111111))),
],
),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label,
style: const TextStyle(
fontSize: 12,
color: Color(0xFF888888),
fontWeight: FontWeight.w500)),
const SizedBox(height: 4),
Text(value,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: Color(0xFF111111))),
],
),
),
],
),
);
}
}
// ─── USAGE EXAMPLES ──────────────────────────────────────────────
// Simple
// StatCard(
// label: 'Total Revenue',
// value: '\$45,231.89',
// description: 'vs last month',
// )
// With Icon
// StatCard(
// style: StatCardStyle.withIcon,
// label: 'Active Users',
// value: '2,350',
// icon: Icons.people_outline_rounded,
// )
// With Trend
// StatCard(
// style: StatCardStyle.withTrend,
// label: 'Conversions',
// value: '12.5%',
// trend: 4.2,
// )
// With Progress
// StatCard(
// style: StatCardStyle.withProgress,
// label: 'Audits Complete',
// value: '\$12,540',
// progress: 0.5,
// )Dismissible alert banners in 5 styles — Info, Success, Warning, and Danger
View component →Course detail with hero image, mentor, tabs, lessons and sticky enroll bar
View component →Grocery home with brand header, search, banner, product cards and rounded bottom nav
View component →