voo_dashboard 0.0.1
voo_dashboard: ^0.0.1 copied to clipboard
A comprehensive, responsive dashboard UI kit for Flutter with sidebar/topbar navigation, stat cards, chart containers, and activity feeds.
example/lib/main.dart
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:voo_dashboard/voo_dashboard.dart';
import 'package:voo_navigation/voo_navigation.dart' hide VooBreadcrumbs, VooBreadcrumbItem, VooQuickAction;
void main() {
runApp(const VooDashboardExampleApp());
}
class VooDashboardExampleApp extends StatelessWidget {
const VooDashboardExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'VooDashboard Example',
debugShowCheckedModeBanner: false,
theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), useMaterial3: true),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue, brightness: Brightness.dark),
useMaterial3: true,
),
home: const DashboardExample(),
);
}
}
class DashboardExample extends StatefulWidget {
const DashboardExample({super.key});
@override
State<DashboardExample> createState() => _DashboardExampleState();
}
class _DashboardExampleState extends State<DashboardExample> {
String _selectedItemId = 'dashboard';
@override
Widget build(BuildContext context) {
return VooAdaptiveScaffold(
config: VooNavigationConfig(
items: [
VooNavigationItem(
id: 'dashboard',
label: 'Dashboard',
icon: Icons.dashboard_outlined,
selectedIcon: Icons.dashboard,
onTap: () => setState(() => _selectedItemId = 'dashboard'),
),
VooNavigationItem(
id: 'analytics',
label: 'Analytics',
icon: Icons.analytics_outlined,
selectedIcon: Icons.analytics,
badgeCount: 3,
onTap: () => setState(() => _selectedItemId = 'analytics'),
),
VooNavigationItem(
id: 'reports',
label: 'Reports',
icon: Icons.description_outlined,
selectedIcon: Icons.description,
onTap: () => setState(() => _selectedItemId = 'reports'),
),
VooNavigationItem(
id: 'users',
label: 'Users',
icon: Icons.people_outline,
selectedIcon: Icons.people,
onTap: () => setState(() => _selectedItemId = 'users'),
),
VooNavigationItem(
id: 'settings',
label: 'Settings',
icon: Icons.settings_outlined,
selectedIcon: Icons.settings,
onTap: () => setState(() => _selectedItemId = 'settings'),
),
],
selectedId: _selectedItemId,
),
body: VooDashboard(body: _buildBody()),
);
}
String _getPageTitle() {
switch (_selectedItemId) {
case 'dashboard':
return 'Dashboard';
case 'analytics':
return 'Analytics';
case 'reports':
return 'Reports';
case 'users':
return 'Users';
case 'settings':
return 'Settings';
default:
return 'Dashboard';
}
}
Widget _buildBody() {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Breadcrumbs
VooBreadcrumbs(
items: [
VooBreadcrumbItem(label: 'Home', onTap: () {}),
VooBreadcrumbItem(label: _getPageTitle(), isActive: true),
],
),
const SizedBox(height: 16),
// Stat Grid
VooStatGrid(
spacing: 12,
runSpacing: 12,
children: [
VooStatCard(value: '\$45,231', label: 'Total Revenue', icon: Icons.attach_money, trend: StatTrend.up, trendValue: 12.5, trendSuffix: 'vs last month'),
VooStatCard(value: '2,350', label: 'New Users', icon: Icons.people, trend: StatTrend.up, trendValue: 5.2),
VooStatCard(value: '12.5%', label: 'Conversion Rate', icon: Icons.trending_up, trend: StatTrend.down, trendValue: 2.1, positiveIsGood: false),
VooStatCard(value: '573', label: 'Active Orders', icon: Icons.shopping_cart, trend: StatTrend.neutral),
],
),
const SizedBox(height: 16),
// Chart and Activity Feed Row
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 800) {
return Column(children: [_buildChartCard(), const SizedBox(height: 16), _buildActivityFeed()]);
}
return IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(flex: 2, child: _buildChartCard()),
const SizedBox(width: 16),
Expanded(child: _buildActivityFeed()),
],
),
);
},
),
const SizedBox(height: 16),
// Additional Charts Row
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 800) {
return Column(
children: [
_buildBarChart(),
const SizedBox(height: 16),
_buildPieChart(),
],
);
}
return IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(child: _buildBarChart()),
const SizedBox(width: 16),
Expanded(child: _buildPieChart()),
],
),
);
},
),
const SizedBox(height: 16),
// Quick Actions
Text('Quick Actions', style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold)),
const SizedBox(height: 12),
Wrap(
spacing: 12,
runSpacing: 12,
children: [
VooQuickAction(label: 'New Report', icon: Icons.add_chart, onTap: () {}),
VooQuickAction(label: 'Export Data', icon: Icons.download, onTap: () {}),
VooQuickAction(label: 'Send Email', icon: Icons.email, onTap: () {}),
VooQuickAction(label: 'Schedule', icon: Icons.schedule, onTap: () {}),
],
),
],
),
);
}
Widget _buildChartCard() {
final colorScheme = Theme.of(context).colorScheme;
return VooChartContainer(
title: 'Revenue Overview',
subtitle: 'Monthly revenue for the past year',
actions: [TextButton(onPressed: () {}, child: const Text('View Details'))],
height: 300,
chart: Padding(
padding: const EdgeInsets.only(right: 16, top: 16),
child: LineChart(
LineChartData(
gridData: FlGridData(
show: true,
drawVerticalLine: false,
horizontalInterval: 10,
getDrawingHorizontalLine: (value) => FlLine(
color: colorScheme.outlineVariant.withValues(alpha: 0.3),
strokeWidth: 1,
),
),
titlesData: FlTitlesData(
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 40,
getTitlesWidget: (value, meta) => Text(
'\$${value.toInt()}k',
style: TextStyle(color: colorScheme.onSurfaceVariant, fontSize: 11),
),
),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 30,
getTitlesWidget: (value, meta) {
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
if (value.toInt() >= 0 && value.toInt() < months.length) {
return Padding(
padding: const EdgeInsets.only(top: 8),
child: Text(months[value.toInt()], style: TextStyle(color: colorScheme.onSurfaceVariant, fontSize: 11)),
);
}
return const SizedBox.shrink();
},
),
),
topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
),
borderData: FlBorderData(show: false),
minX: 0,
maxX: 11,
minY: 0,
maxY: 50,
lineBarsData: [
LineChartBarData(
spots: const [
FlSpot(0, 25),
FlSpot(1, 28),
FlSpot(2, 32),
FlSpot(3, 30),
FlSpot(4, 35),
FlSpot(5, 38),
FlSpot(6, 33),
FlSpot(7, 40),
FlSpot(8, 42),
FlSpot(9, 38),
FlSpot(10, 45),
FlSpot(11, 48),
],
isCurved: true,
color: colorScheme.primary,
barWidth: 3,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(
show: true,
color: colorScheme.primary.withValues(alpha: 0.15),
),
),
],
),
),
),
);
}
Widget _buildActivityFeed() {
return VooActivityFeed(
title: 'Recent Activity',
maxItems: 5,
onViewAll: () {},
items: [
VooActivityItem(
id: '1',
title: 'New user registered',
description: '[email protected]',
timestamp: DateTime.now().subtract(const Duration(minutes: 5)),
icon: Icons.person_add,
),
VooActivityItem(
id: '2',
title: 'Order completed',
description: 'Order #12345 was delivered',
timestamp: DateTime.now().subtract(const Duration(hours: 1)),
icon: Icons.check_circle,
iconColor: Colors.green,
),
VooActivityItem(
id: '3',
title: 'Payment received',
description: '\$1,234.56 from Customer Corp',
timestamp: DateTime.now().subtract(const Duration(hours: 3)),
icon: Icons.payment,
iconColor: Colors.blue,
),
VooActivityItem(
id: '4',
title: 'Report generated',
description: 'Monthly sales report',
timestamp: DateTime.now().subtract(const Duration(days: 1)),
icon: Icons.description,
),
VooActivityItem(
id: '5',
title: 'System update',
description: 'Version 2.0 deployed',
timestamp: DateTime.now().subtract(const Duration(days: 2)),
icon: Icons.system_update,
),
],
);
}
Widget _buildBarChart() {
final colorScheme = Theme.of(context).colorScheme;
return VooChartContainer(
title: 'Sales by Category',
subtitle: 'Top performing categories this quarter',
height: 280,
chart: Padding(
padding: const EdgeInsets.only(right: 16, top: 16, bottom: 8),
child: BarChart(
BarChartData(
alignment: BarChartAlignment.spaceAround,
maxY: 100,
barTouchData: BarTouchData(enabled: true),
titlesData: FlTitlesData(
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 35,
getTitlesWidget: (value, meta) => Text(
'${value.toInt()}%',
style: TextStyle(color: colorScheme.onSurfaceVariant, fontSize: 11),
),
),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 30,
getTitlesWidget: (value, meta) {
const categories = ['Electronics', 'Clothing', 'Home', 'Sports', 'Books'];
if (value.toInt() >= 0 && value.toInt() < categories.length) {
return Padding(
padding: const EdgeInsets.only(top: 8),
child: Text(
categories[value.toInt()],
style: TextStyle(color: colorScheme.onSurfaceVariant, fontSize: 10),
),
);
}
return const SizedBox.shrink();
},
),
),
topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
),
borderData: FlBorderData(show: false),
gridData: FlGridData(
show: true,
drawVerticalLine: false,
horizontalInterval: 25,
getDrawingHorizontalLine: (value) => FlLine(
color: colorScheme.outlineVariant.withValues(alpha: 0.3),
strokeWidth: 1,
),
),
barGroups: [
_makeBarGroup(0, 85, colorScheme.primary),
_makeBarGroup(1, 65, colorScheme.secondary),
_makeBarGroup(2, 72, colorScheme.tertiary),
_makeBarGroup(3, 48, colorScheme.primary.withValues(alpha: 0.7)),
_makeBarGroup(4, 38, colorScheme.secondary.withValues(alpha: 0.7)),
],
),
),
),
);
}
BarChartGroupData _makeBarGroup(int x, double y, Color color) => BarChartGroupData(
x: x,
barRods: [
BarChartRodData(
toY: y,
color: color,
width: 22,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(6),
topRight: Radius.circular(6),
),
),
],
);
Widget _buildPieChart() {
final colorScheme = Theme.of(context).colorScheme;
return VooChartContainer(
title: 'Traffic Sources',
subtitle: 'Where your visitors come from',
height: 280,
chart: Row(
children: [
Expanded(
flex: 3,
child: PieChart(
PieChartData(
sectionsSpace: 2,
centerSpaceRadius: 40,
sections: [
PieChartSectionData(
value: 40,
title: '40%',
color: colorScheme.primary,
radius: 60,
titleStyle: const TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold),
),
PieChartSectionData(
value: 25,
title: '25%',
color: colorScheme.secondary,
radius: 60,
titleStyle: const TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold),
),
PieChartSectionData(
value: 20,
title: '20%',
color: colorScheme.tertiary,
radius: 60,
titleStyle: const TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold),
),
PieChartSectionData(
value: 15,
title: '15%',
color: colorScheme.error,
radius: 60,
titleStyle: const TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold),
),
],
),
),
),
Expanded(
flex: 2,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildLegendItem('Direct', colorScheme.primary),
const SizedBox(height: 12),
_buildLegendItem('Organic', colorScheme.secondary),
const SizedBox(height: 12),
_buildLegendItem('Referral', colorScheme.tertiary),
const SizedBox(height: 12),
_buildLegendItem('Social', colorScheme.error),
],
),
),
],
),
);
}
Widget _buildLegendItem(String label, Color color) => Row(
children: [
Container(
width: 12,
height: 12,
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(3)),
),
const SizedBox(width: 8),
Text(label, style: Theme.of(context).textTheme.bodySmall),
],
);
}