Creare grafici in Flutter con fl_chart
fl_chart è uno dei package per Flutter per la creazione di grafici.
E' molto completo, ha anche la possibilità di animare i grafici, ed è molto customizzabile.
Ovviamente tutto questo si paga con il fatto che non è facilissimo da usare.
Per installarlo:
flutter pub add fl_chart
Cominciamo con il creare una class per i colori, fondamentale per seguire gli esempi che trovate sulla documentazione:
import 'package:flutter/material.dart';
class AppColors {
static const Color primary = contentColorCyan;
static const Color menuBackground = Color(0xFF090912);
static const Color itemsBackground = Color(0xFF1B2339);
static const Color pageBackground = Color(0xFF282E45);
static const Color mainTextColor1 = Colors.white;
static const Color mainTextColor2 = Colors.white70;
static const Color mainTextColor3 = Colors.white38;
static const Color mainGridLineColor = Colors.white10;
static const Color borderColor = Colors.white54;
static const Color gridLinesColor = Color(0x11FFFFFF);
static const Color contentColorBlack = Colors.black;
static const Color contentColorWhite = Colors.white;
static const Color contentColorBlue = Color(0xFF2196F3);
static const Color contentColorYellow = Color(0xFFFFC300);
static const Color contentColorOrange = Color(0xFFFF683B);
static const Color contentColorGreen = Color(0xFF3BFF49);
static const Color contentColorPurple = Color(0xFF6E1BFF);
static const Color contentColorPink = Color(0xFFFF3AF2);
static const Color contentColorRed = Color(0xFFE80054);
static const Color contentColorCyan = Color(0xFF50E4FF);
}
Qui sotto un esempio completo di bar chart:
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
import 'AppColors.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
),
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
List<Color> get availableColors => const <Color>[
AppColors.contentColorPurple,
AppColors.contentColorYellow,
AppColors.contentColorBlue,
AppColors.contentColorOrange,
AppColors.contentColorPink,
AppColors.contentColorRed,
];
final Color barBackgroundColor = AppColors.contentColorWhite;
final Color barColor = AppColors.contentColorWhite;
final Color touchedBarColor = AppColors.contentColorGreen;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final Duration animDuration = const Duration(milliseconds: 250);
int touchedIndex = -1;
bool isPlaying = false;
@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 1,
child: Stack(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: BarChart(
isPlaying ? randomData() : mainBarData(),
swapAnimationDuration: animDuration,
),
),
),
const SizedBox(
height: 12,
),
],
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Align(
alignment: Alignment.topRight,
child: IconButton(
icon: Icon(
isPlaying ? Icons.pause : Icons.play_arrow,
color: AppColors.contentColorGreen,
),
onPressed: () {
setState(() {
isPlaying = !isPlaying;
if (isPlaying) {
refreshState();
}
});
},
),
),
)
],
),
);
}
BarChartGroupData makeGroupData(
int x,
double y, {
bool isTouched = false,
Color? barColor,
double width = 22,
List<int> showTooltips = const [],
}) {
barColor ??= widget.barColor;
return BarChartGroupData(
x: x,
barRods: [
BarChartRodData(
toY: isTouched ? y + 1 : y,
color: isTouched ? widget.touchedBarColor : barColor,
width: width,
borderSide: isTouched
? BorderSide(color: widget.touchedBarColor)
: const BorderSide(color: Colors.white, width: 0),
backDrawRodData: BackgroundBarChartRodData(
show: true,
toY: 20,
color: widget.barBackgroundColor,
),
),
],
showingTooltipIndicators: showTooltips,
);
}
List<BarChartGroupData> showingGroups() => List.generate(7, (i) {
switch (i) {
case 0:
return makeGroupData(0, 5, isTouched: i == touchedIndex);
case 1:
return makeGroupData(1, 6.5, isTouched: i == touchedIndex);
case 2:
return makeGroupData(2, 5, isTouched: i == touchedIndex);
case 3:
return makeGroupData(3, 7.5, isTouched: i == touchedIndex);
case 4:
return makeGroupData(4, 9, isTouched: i == touchedIndex);
case 5:
return makeGroupData(5, 11.5, isTouched: i == touchedIndex);
case 6:
return makeGroupData(6, 6.5, isTouched: i == touchedIndex);
default:
return throw Error();
}
});
BarChartData mainBarData() {
return BarChartData(
barTouchData: BarTouchData(
touchTooltipData: BarTouchTooltipData(
tooltipBgColor: Colors.blueGrey,
tooltipHorizontalAlignment: FLHorizontalAlignment.right,
tooltipMargin: -10,
getTooltipItem: (group, groupIndex, rod, rodIndex) {
String weekDay;
switch (group.x) {
case 0:
weekDay = 'Monday';
break;
case 1:
weekDay = 'Tuesday';
break;
case 2:
weekDay = 'Wednesday';
break;
case 3:
weekDay = 'Thursday';
break;
case 4:
weekDay = 'Friday';
break;
case 5:
weekDay = 'Saturday';
break;
case 6:
weekDay = 'Sunday';
break;
default:
throw Error();
}
return BarTooltipItem(
'$weekDayn',
const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
children: <TextSpan>[
TextSpan(
text: (rod.toY - 1).toString(),
style: TextStyle(
color: widget.touchedBarColor,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
],
);
},
),
touchCallback: (FlTouchEvent event, barTouchResponse) {
setState(() {
if (!event.isInterestedForInteractions ||
barTouchResponse == null ||
barTouchResponse.spot == null) {
touchedIndex = -1;
return;
}
touchedIndex = barTouchResponse.spot!.touchedBarGroupIndex;
});
},
),
titlesData: FlTitlesData(
show: true,
rightTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
topTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: getTitles,
reservedSize: 38,
),
),
leftTitles: const AxisTitles(
sideTitles: SideTitles(
showTitles: false,
),
),
),
borderData: FlBorderData(
show: false,
),
barGroups: showingGroups(),
gridData: const FlGridData(show: false),
);
}
Widget getTitles(double value, TitleMeta meta) {
const style = TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 14,
);
Widget text;
switch (value.toInt()) {
case 0:
text = const Text('M', style: style);
break;
case 1:
text = const Text('T', style: style);
break;
case 2:
text = const Text('W', style: style);
break;
case 3:
text = const Text('T', style: style);
break;
case 4:
text = const Text('F', style: style);
break;
case 5:
text = const Text('S', style: style);
break;
case 6:
text = const Text('S', style: style);
break;
default:
text = const Text('', style: style);
break;
}
return SideTitleWidget(
axisSide: meta.axisSide,
space: 16,
child: text,
);
}
BarChartData randomData() {
return BarChartData(
barTouchData: BarTouchData(
enabled: false,
),
titlesData: FlTitlesData(
show: true,
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: getTitles,
reservedSize: 38,
),
),
leftTitles: const AxisTitles(
sideTitles: SideTitles(
showTitles: false,
),
),
topTitles: const AxisTitles(
sideTitles: SideTitles(
showTitles: false,
),
),
rightTitles: const AxisTitles(
sideTitles: SideTitles(
showTitles: false,
),
),
),
borderData: FlBorderData(
show: false,
),
barGroups: List.generate(7, (i) {
switch (i) {
case 0:
return makeGroupData(
0,
Random().nextInt(15).toDouble() + 6,
barColor: widget.availableColors[
Random().nextInt(widget.availableColors.length)],
);
case 1:
return makeGroupData(
1,
Random().nextInt(15).toDouble() + 6,
barColor: widget.availableColors[
Random().nextInt(widget.availableColors.length)],
);
case 2:
return makeGroupData(
2,
Random().nextInt(15).toDouble() + 6,
barColor: widget.availableColors[
Random().nextInt(widget.availableColors.length)],
);
case 3:
return makeGroupData(
3,
Random().nextInt(15).toDouble() + 6,
barColor: widget.availableColors[
Random().nextInt(widget.availableColors.length)],
);
case 4:
return makeGroupData(
4,
Random().nextInt(15).toDouble() + 6,
barColor: widget.availableColors[
Random().nextInt(widget.availableColors.length)],
);
case 5:
return makeGroupData(
5,
Random().nextInt(15).toDouble() + 6,
barColor: widget.availableColors[
Random().nextInt(widget.availableColors.length)],
);
case 6:
return makeGroupData(
6,
Random().nextInt(15).toDouble() + 6,
barColor: widget.availableColors[
Random().nextInt(widget.availableColors.length)],
);
default:
return throw Error();
}
}),
gridData: const FlGridData(show: false),
);
}
Future<dynamic> refreshState() async {
setState(() {});
await Future<dynamic>.delayed(
animDuration + const Duration(milliseconds: 50),
);
if (isPlaying) {
await refreshState();
}
}
}
Enjoy!
dart flutter fl_chart
Commentami!