class MarketAnalysisDashboard extends StatefulWidget {
@override
_MarketAnalysisDashboardState createState() => _MarketAnalysisDashboardState();
}
class _MarketAnalysisDashboardState extends State<MarketAnalysisDashboard> {
double _bubbleScale = 1.0;
final _marketData = [
{
'name': 'TechCorp Solutions',
'revenue': 250.0,
'customers': 180.0,
'marketShare': 28.0,
'category': 'Enterprise',
},
{
'name': 'StartupX Labs',
'revenue': 85.0,
'customers': 120.0,
'marketShare': 12.0,
'category': 'Startup',
},
{
'name': 'MidSize Systems',
'revenue': 150.0,
'customers': 160.0,
'marketShare': 18.0,
'category': 'SMB',
},
// ... more data
];
@override
Widget build(BuildContext context) {
final minSize = 8.0 + _bubbleScale * 5.0;
final maxSize = 15.0 + _bubbleScale * 15.0;
return Scaffold(
body: Column(
children: [
// Header with controls
Container(
padding: EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Market Performance Analysis',
style: Theme.of(context).textTheme.headlineSmall,
),
Text('Revenue vs Customer Base'),
],
),
),
// Bubble size control
Column(
children: [
Text('Bubble Size'),
Slider(
value: _bubbleScale,
min: 0.1,
max: 2.0,
divisions: 19,
label: '${(_bubbleScale * 100).round()}%',
onChanged: (value) => setState(() => _bubbleScale = value),
),
],
),
],
),
),
// Chart
Expanded(
child: Padding(
padding: EdgeInsets.all(16),
child: CristalyseChart()
.data(_marketData)
.mapping(
x: 'revenue',
y: 'customers',
size: 'marketShare',
color: 'category',
)
.geomBubble(
minSize: minSize,
maxSize: maxSize,
alpha: 0.75,
borderWidth: 2.0,
borderColor: Colors.white,
)
.scaleXContinuous(
labels: (value) => '\$${value.toStringAsFixed(0)}M',
)
.scaleYContinuous(
labels: (value) => '${value.toStringAsFixed(0)}K',
)
.theme(ChartTheme.defaultTheme())
.animate(
duration: Duration(milliseconds: 1000),
curve: Curves.easeOutCubic,
)
.interaction(
tooltip: TooltipConfig(
builder: _buildRichTooltip,
),
)
.build(),
),
),
// Legend and insights
Container(
padding: EdgeInsets.all(16),
child: Column(
children: [
// Category legend
_buildLegend(),
SizedBox(height: 16),
// Key insights
_buildInsightsPanel(),
],
),
),
],
),
);
}
Widget _buildRichTooltip(DataPointInfo point) {
final name = point.getDisplayValue('name');
final revenue = point.getDisplayValue('revenue');
final customers = point.getDisplayValue('customers');
final marketShare = point.getDisplayValue('marketShare');
final category = point.getDisplayValue('category');
return Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black87,
borderRadius: BorderRadius.circular(8),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(name, style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
SizedBox(height: 4),
Text('Revenue: \$${revenue}M', style: TextStyle(color: Colors.white)),
Text('Customers: ${customers}K', style: TextStyle(color: Colors.white)),
Text('Market Share: ${marketShare}%', style: TextStyle(color: Colors.white)),
Text('Category: ${category}', style: TextStyle(color: Colors.white)),
],
),
);
}
Widget _buildLegend() {
final categories = ['Enterprise', 'SMB', 'Startup'];
final colors = [Colors.blue, Colors.green, Colors.orange];
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: categories.asMap().entries.map((entry) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: Row(
children: [
Container(
width: 12,
height: 12,
decoration: BoxDecoration(
color: colors[entry.key],
shape: BoxShape.circle,
),
),
SizedBox(width: 6),
Text(entry.value),
],
),
);
}).toList(),
);
}
Widget _buildInsightsPanel() {
return Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.blue[200]!),
),
child: Column(
children: [
Row(
children: [
Icon(Icons.insights, color: Colors.blue[700]),
SizedBox(width: 8),
Text(
'Key Insights',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue[900],
),
),
],
),
SizedBox(height: 8),
Text(
'• Bubble size represents market share percentage\n'
'• Hover over bubbles for detailed company metrics\n'
'• Colors indicate company categories',
style: TextStyle(fontSize: 12, color: Colors.blue[700]),
),
],
),
);
}
}