Skip to main content

MultiLineChartAppi

A powerful multi-line chart widget that renders interactive line charts with multiple data series using Chart.js, perfect for displaying trends, comparisons, and time-series data in web applications.

Overview

MultiLineChartAppi provides a comprehensive solution for visualizing multiple data series on a single chart. Built on Chart.js and rendered through an iframe, it offers smooth animations, interactive features, and professional styling for data visualization needs.

Features

  • 📊 Multiple Data Series - Display multiple lines on a single chart
  • 🎨 Professional Styling - Clean, modern chart appearance
  • 🖱️ Interactive Elements - Hover effects and tooltips
  • 📱 Responsive Design - Adapts to different screen sizes
  • 🎯 Custom Titles - Configurable chart titles with styling
  • 🔄 Smooth Animations - Animated chart rendering
  • 🌐 Web Optimized - Uses Chart.js for optimal web performance
  • 📈 Trend Analysis - Perfect for time-series and trend data
  • 🎪 Flexible Data - Support for various data structures
  • High Performance - Efficient rendering for large datasets

Basic Usage

MultiLineChartAppi(
xData: ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
data: [
MultiLineProductData(
label: 'Product A',
data: [10, 20, 15, 25, 30],
borderColor: '#FF6384',
backgroundColor: 'rgba(255, 99, 132, 0.2)',
),
MultiLineProductData(
label: 'Product B',
data: [15, 25, 20, 30, 35],
borderColor: '#36A2EB',
backgroundColor: 'rgba(54, 162, 235, 0.2)',
),
],
title: 'Sales Comparison',
interaction: true,
)

Properties

PropertyTypeDefaultDescription
xDataList<String>requiredX-axis labels (categories, dates, etc.)
dataList<MultiLineProductData>requiredData series for multiple lines
titleString?nullChart title
titleStyleTextStyle?nullStyling for the chart title
interactionbool?trueEnable/disable chart interactions

MultiLineProductData Structure

class MultiLineProductData {
final String label; // Series name
final List<int> data; // Data points
final String borderColor; // Line color
final String backgroundColor; // Fill color (optional)

MultiLineProductData({
required this.label,
required this.data,
required this.borderColor,
this.backgroundColor = 'transparent',
});
}

Examples

Basic Sales Comparison

class SalesComparisonChart extends StatelessWidget {

Widget build(BuildContext context) {
return Container(
height: 400,
padding: EdgeInsets.all(16),
child: MultiLineChartAppi(
xData: ['Q1', 'Q2', 'Q3', 'Q4'],
data: [
MultiLineProductData(
label: 'Product A',
data: [120, 150, 180, 200],
borderColor: '#FF6384',
backgroundColor: 'rgba(255, 99, 132, 0.1)',
),
MultiLineProductData(
label: 'Product B',
data: [100, 130, 160, 190],
borderColor: '#36A2EB',
backgroundColor: 'rgba(54, 162, 235, 0.1)',
),
MultiLineProductData(
label: 'Product C',
data: [80, 110, 140, 170],
borderColor: '#FFCE56',
backgroundColor: 'rgba(255, 206, 86, 0.1)',
),
],
title: 'Quarterly Sales Performance',
titleStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey[800],
),
interaction: true,
),
);
}
}

Monthly Revenue Tracking

class MonthlyRevenueChart extends StatefulWidget {

_MonthlyRevenueChartState createState() => _MonthlyRevenueChartState();
}

class _MonthlyRevenueChartState extends State<MonthlyRevenueChart> {
List<String> months = [
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
];


Widget build(BuildContext context) {
return Card(
elevation: 4,
margin: EdgeInsets.all(16),
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Revenue Analysis',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Icon(Icons.trending_up, color: Colors.green),
],
),
SizedBox(height: 20),

Container(
height: 350,
child: MultiLineChartAppi(
xData: months,
data: [
MultiLineProductData(
label: '2023 Revenue',
data: [45000, 52000, 48000, 61000, 55000, 67000,
72000, 68000, 75000, 82000, 78000, 85000],
borderColor: '#4CAF50',
backgroundColor: 'rgba(76, 175, 80, 0.1)',
),
MultiLineProductData(
label: '2024 Revenue',
data: [50000, 58000, 54000, 68000, 62000, 74000,
79000, 75000, 82000, 89000, 85000, 92000],
borderColor: '#2196F3',
backgroundColor: 'rgba(33, 150, 243, 0.1)',
),
MultiLineProductData(
label: 'Target',
data: [55000, 60000, 58000, 70000, 65000, 75000,
80000, 78000, 85000, 90000, 88000, 95000],
borderColor: '#FF9800',
backgroundColor: 'transparent',
),
],
title: 'Monthly Revenue Comparison',
titleStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey[700],
),
interaction: true,
),
),

SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildLegendItem('2023', Colors.green),
_buildLegendItem('2024', Colors.blue),
_buildLegendItem('Target', Colors.orange),
],
),
],
),
),
);
}

Widget _buildLegendItem(String label, Color color) {
return Row(
children: [
Container(
width: 12,
height: 12,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
),
SizedBox(width: 4),
Text(
label,
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
),
],
);
}
}

Website Analytics Dashboard

class WebAnalyticsChart extends StatelessWidget {

Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Website Analytics',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.grey[800],
),
),
SizedBox(height: 8),
Text(
'Last 30 days performance',
style: TextStyle(
fontSize: 14,
color: Colors.grey[600],
),
),
SizedBox(height: 20),

Container(
height: 400,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 2,
blurRadius: 8,
offset: Offset(0, 2),
),
],
),
child: Padding(
padding: EdgeInsets.all(16),
child: MultiLineChartAppi(
xData: _generateDates(),
data: [
MultiLineProductData(
label: 'Page Views',
data: _generatePageViews(),
borderColor: '#3F51B5',
backgroundColor: 'rgba(63, 81, 181, 0.1)',
),
MultiLineProductData(
label: 'Unique Visitors',
data: _generateUniqueVisitors(),
borderColor: '#E91E63',
backgroundColor: 'rgba(233, 30, 99, 0.1)',
),
MultiLineProductData(
label: 'Sessions',
data: _generateSessions(),
borderColor: '#00BCD4',
backgroundColor: 'rgba(0, 188, 212, 0.1)',
),
],
title: 'Traffic Overview',
titleStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey[700],
),
interaction: true,
),
),
),

SizedBox(height: 16),
_buildMetricsSummary(),
],
),
);
}

List<String> _generateDates() {
List<String> dates = [];
DateTime now = DateTime.now();
for (int i = 29; i >= 0; i--) {
DateTime date = now.subtract(Duration(days: i));
dates.add('${date.day}/${date.month}');
}
return dates;
}

List<int> _generatePageViews() {
return List.generate(30, (index) => 1200 + (index * 50) + (index % 7 * 200));
}

List<int> _generateUniqueVisitors() {
return List.generate(30, (index) => 800 + (index * 30) + (index % 5 * 150));
}

List<int> _generateSessions() {
return List.generate(30, (index) => 900 + (index * 35) + (index % 6 * 180));
}

Widget _buildMetricsSummary() {
return Row(
children: [
Expanded(
child: _buildMetricCard('Total Views', '45.2K', Colors.indigo),
),
SizedBox(width: 12),
Expanded(
child: _buildMetricCard('Visitors', '28.7K', Colors.pink),
),
SizedBox(width: 12),
Expanded(
child: _buildMetricCard('Sessions', '32.1K', Colors.cyan),
),
],
);
}

Widget _buildMetricCard(String title, String value, Color color) {
return Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: color.withOpacity(0.3)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 4),
Text(
value,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: color,
),
),
],
),
);
}
}

Stock Price Tracking

class StockPriceChart extends StatefulWidget {
final List<String> stockSymbols;

const StockPriceChart({required this.stockSymbols});


_StockPriceChartState createState() => _StockPriceChartState();
}

class _StockPriceChartState extends State<StockPriceChart> {
List<String> timeLabels = [];
List<MultiLineProductData> stockData = [];


void initState() {
super.initState();
_generateStockData();
}

void _generateStockData() {
// Generate time labels (last 24 hours)
timeLabels = List.generate(24, (index) {
int hour = (DateTime.now().hour - 23 + index) % 24;
return '${hour.toString().padLeft(2, '0')}:00';
});

// Generate stock data
List<Color> colors = [
Colors.blue,
Colors.red,
Colors.green,
Colors.orange,
Colors.purple,
];

stockData = widget.stockSymbols.asMap().entries.map((entry) {
int index = entry.key;
String symbol = entry.value;
Color color = colors[index % colors.length];

return MultiLineProductData(
label: symbol,
data: _generateStockPrices(100 + index * 50),
borderColor: '#${color.value.toRadixString(16).substring(2)}',
backgroundColor: 'rgba(${color.red}, ${color.green}, ${color.blue}, 0.1)',
);
}).toList();
}

List<int> _generateStockPrices(int basePrice) {
List<int> prices = [basePrice];
for (int i = 1; i < 24; i++) {
int change = (Random().nextInt(21) - 10); // -10 to +10
int newPrice = (prices.last + change).clamp(basePrice - 50, basePrice + 50);
prices.add(newPrice);
}
return prices;
}


Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Stock Prices',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.1),
borderRadius: BorderRadius.circular(16),
),
child: Text(
'LIVE',
style: TextStyle(
color: Colors.green,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
),
],
),
SizedBox(height: 8),
Text(
'Last 24 hours',
style: TextStyle(
color: Colors.grey[600],
fontSize: 14,
),
),
SizedBox(height: 20),

Container(
height: 350,
decoration: BoxDecoration(
color: Colors.grey[50],
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey[200]!),
),
child: Padding(
padding: EdgeInsets.all(16),
child: MultiLineChartAppi(
xData: timeLabels,
data: stockData,
title: 'Price Movement',
titleStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.grey[700],
),
interaction: true,
),
),
),

SizedBox(height: 16),
_buildStockLegend(),
],
),
);
}

Widget _buildStockLegend() {
return Wrap(
spacing: 16,
runSpacing: 8,
children: stockData.map((stock) {
Color color = Color(int.parse('0xFF${stock.borderColor.substring(1)}'));
int currentPrice = stock.data.last;
int previousPrice = stock.data[stock.data.length - 2];
bool isUp = currentPrice > previousPrice;

return Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey[300]!),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
),
SizedBox(width: 6),
Text(
stock.label,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 12,
),
),
SizedBox(width: 4),
Text(
'\$${currentPrice}',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
SizedBox(width: 4),
Icon(
isUp ? Icons.arrow_upward : Icons.arrow_downward,
size: 12,
color: isUp ? Colors.green : Colors.red,
),
],
),
);
}).toList(),
);
}
}

Performance Metrics Dashboard

class PerformanceMetricsChart extends StatelessWidget {

Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Performance Metrics',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.grey[800],
),
),
SizedBox(height: 20),

Container(
height: 400,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue[50]!, Colors.white],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 10,
offset: Offset(0, 4),
),
],
),
child: Padding(
padding: EdgeInsets.all(20),
child: MultiLineChartAppi(
xData: ['Week 1', 'Week 2', 'Week 3', 'Week 4', 'Week 5', 'Week 6'],
data: [
MultiLineProductData(
label: 'CPU Usage (%)',
data: [45, 52, 48, 61, 55, 49],
borderColor: '#FF5722',
backgroundColor: 'rgba(255, 87, 34, 0.1)',
),
MultiLineProductData(
label: 'Memory Usage (%)',
data: [65, 70, 68, 75, 72, 69],
borderColor: '#9C27B0',
backgroundColor: 'rgba(156, 39, 176, 0.1)',
),
MultiLineProductData(
label: 'Network I/O (MB/s)',
data: [25, 30, 28, 35, 32, 29],
borderColor: '#4CAF50',
backgroundColor: 'rgba(76, 175, 80, 0.1)',
),
MultiLineProductData(
label: 'Disk Usage (%)',
data: [80, 82, 81, 85, 83, 82],
borderColor: '#FF9800',
backgroundColor: 'rgba(255, 152, 0, 0.1)',
),
],
title: 'System Performance Over Time',
titleStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey[700],
),
interaction: true,
),
),
),

SizedBox(height: 20),
_buildPerformanceAlerts(),
],
),
);
}

Widget _buildPerformanceAlerts() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Performance Alerts',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.grey[800],
),
),
SizedBox(height: 12),

_buildAlertCard(
'High Disk Usage',
'Disk usage is consistently above 80%',
Colors.orange,
Icons.storage,
),
SizedBox(height: 8),

_buildAlertCard(
'Memory Optimization',
'Memory usage trending upward',
Colors.purple,
Icons.memory,
),
],
);
}

Widget _buildAlertCard(String title, String description, Color color, IconData icon) {
return Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: color.withOpacity(0.3)),
),
child: Row(
children: [
Icon(icon, color: color, size: 20),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 14,
color: color,
),
),
Text(
description,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
),
),
],
),
);
}
}

Advanced Features

Dynamic Data Updates

class RealTimeChart extends StatefulWidget {

_RealTimeChartState createState() => _RealTimeChartState();
}

class _RealTimeChartState extends State<RealTimeChart> {
Timer? _timer;
List<String> timeLabels = [];
List<MultiLineProductData> chartData = [];


void initState() {
super.initState();
_initializeData();
_startRealTimeUpdates();
}

void _initializeData() {
timeLabels = List.generate(10, (index) {
DateTime time = DateTime.now().subtract(Duration(minutes: 9 - index));
return '${time.hour}:${time.minute.toString().padLeft(2, '0')}';
});

chartData = [
MultiLineProductData(
label: 'Server 1',
data: List.generate(10, (index) => 50 + Random().nextInt(50)),
borderColor: '#2196F3',
backgroundColor: 'rgba(33, 150, 243, 0.1)',
),
MultiLineProductData(
label: 'Server 2',
data: List.generate(10, (index) => 40 + Random().nextInt(60)),
borderColor: '#4CAF50',
backgroundColor: 'rgba(76, 175, 80, 0.1)',
),
];
}

void _startRealTimeUpdates() {
_timer = Timer.periodic(Duration(seconds: 5), (timer) {
setState(() {
// Add new time label
DateTime now = DateTime.now();
timeLabels.add('${now.hour}:${now.minute.toString().padLeft(2, '0')}');
if (timeLabels.length > 10) {
timeLabels.removeAt(0);
}

// Update data
for (var series in chartData) {
series.data.add(50 + Random().nextInt(50));
if (series.data.length > 10) {
series.data.removeAt(0);
}
}
});
});
}


void dispose() {
_timer?.cancel();
super.dispose();
}


Widget build(BuildContext context) {
return Container(
height: 300,
child: MultiLineChartAppi(
xData: timeLabels,
data: chartData,
title: 'Real-time Server Monitoring',
interaction: true,
),
);
}
}

Custom Color Schemes

class CustomColorChart extends StatelessWidget {
final List<Color> colorPalette = [
Color(0xFF6366F1), // Indigo
Color(0xFF8B5CF6), // Violet
Color(0xFF06B6D4), // Cyan
Color(0xFF10B981), // Emerald
Color(0xFFF59E0B), // Amber
Color(0xFFEF4444), // Red
];


Widget build(BuildContext context) {
return MultiLineChartAppi(
xData: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
data: _generateColoredData(),
title: 'Weekly Activity',
titleStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey[800],
),
interaction: true,
);
}

List<MultiLineProductData> _generateColoredData() {
List<String> activities = ['Walking', 'Running', 'Cycling', 'Swimming'];

return activities.asMap().entries.map((entry) {
int index = entry.key;
String activity = entry.value;
Color color = colorPalette[index % colorPalette.length];

return MultiLineProductData(
label: activity,
data: List.generate(7, (i) => 20 + Random().nextInt(80)),
borderColor: '#${color.value.toRadixString(16).substring(2)}',
backgroundColor: 'rgba(${color.red}, ${color.green}, ${color.blue}, 0.1)',
);
}).toList();
}
}

Chart Interactions

The interaction parameter enables several interactive features:

  • Hover Effects: Highlight data points on mouse hover
  • Tooltips: Display detailed information for each data point
  • Legend Interaction: Click legend items to show/hide data series
  • Zoom and Pan: Navigate through large datasets (when enabled)

Best Practices

  1. Data Preparation: Ensure consistent data array lengths across all series
  2. Color Selection: Use distinct, accessible colors for different data series
  3. Performance: Limit the number of data points for optimal performance
  4. Responsive Design: Consider chart sizing for different screen sizes
  5. User Experience: Provide clear legends and meaningful titles
  6. Accessibility: Use high contrast colors and descriptive labels

Common Use Cases

  • Financial Data: Stock prices, revenue tracking, budget analysis
  • Analytics: Website traffic, user engagement, conversion rates
  • Performance Monitoring: System metrics, application performance
  • Scientific Data: Research results, experimental data, trends
  • Business Intelligence: Sales comparisons, market analysis
  • IoT Monitoring: Sensor data, environmental measurements

Migration Notes

When upgrading from simple charts:

  1. Restructure data into MultiLineProductData format
  2. Update color specifications to hex format
  3. Consider chart sizing and responsive behavior
  4. Test interactive features across different devices

Technical Notes

  • Built on Chart.js for optimal web performance
  • Rendered through iframe for security and isolation
  • Supports responsive design out of the box
  • Automatically handles data scaling and formatting
  • Optimized for web deployment

Ready to create stunning data visualizations? Combine with other chart widgets for comprehensive dashboards!