I'm trying to achieve this type of horizontal bar chart stacked within one Bar chart. I came across the fl_chart package, but none of it seems to have the type that I'm looking for. If any champ can support me in giving me steps to how to achieve this or an exemplary code will be so much helpful. Thank you so much in advance.
CodePudding user response:
Thanks for the code @ChiragBargoojar, I just added bits of customization and the graph works as how I designed it.
If anyone else wondering, here's the code:
class HorizontalBarChart extends StatelessWidget {
const HorizontalBarChart({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
List<Map<String, dynamic>> chartData = [
{
"units": 50,
"color": cCoffee,
},
{
"units": 10,
"color": cCyan,
},
{
"units": 70,
"color": cGreen,
},
{
"units": 100,
"color": cOrange,
},
];
double maxWidth = MediaQuery.of(context).size.width - 36;
var totalUnitNum = 0;
for (int i = 0; i < chartData.length; i ) {
totalUnitNum = totalUnitNum int.parse(chartData[i]["units"].toString());
}
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 18.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(90),
child: Row(
children: [
for (int i = 0; i < chartData.length; i )
i == chartData.length - 1
? Expanded(
child: SizedBox(
height: 16,
child: ColoredBox(
color: chartData[i]["color"],
),
),
)
: Row(
children: [
SizedBox(
width:
chartData[i]["units"] / totalUnitNum * maxWidth,
height: 16,
child: ColoredBox(
color: chartData[i]["color"],
),
),
const SizedBox(width: 6),
],
)
],
),
),
);
}
}
CodePudding user response:
List<int> acc = [500, 300, 400, 900, 800];
List<Color> col = [
Colors.red,
Colors.blue,
Colors.orange,
Colors.green,
Colors.pink
];
getSum() {
return acc.reduce((a, b) => a b);
}
getAccAver(int index) {
return (acc[index] / getSum() * 100).toInt();
}
Padding(
padding: const EdgeInsets.all(5.0),
child: SizedBox(
height: 20,
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
for (var i = 0; i < acc.length; i )
CardAccAve(
percentage: getAccAver(i),
leftBorder: i == 0 ? 10 : 0,
rightBorder: i == acc.length - 1 ? 10 : 0,
color: col[i],
),
],
),
),
),
class CardAccAve extends StatelessWidget {
CardAccAve({
Key? key,
required this.leftBorder,
required this.rightBorder,
required this.percentage,
required this.color,
}) : super(key: key);
double leftBorder;
double rightBorder;
final int percentage;
Color color;
@override
Widget build(BuildContext context) {
return Expanded(
flex: percentage,
child: SizedBox(
height: 20,
child: Card(
margin: const EdgeInsets.symmetric(horizontal: 1, vertical: 2),
color: color,
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(leftBorder),
topLeft: Radius.circular(leftBorder),
bottomRight: Radius.circular(rightBorder),
topRight: Radius.circular(rightBorder),
),
),
),
),
);
}
}
Result
CodePudding user response:
You could also achieve this with a LinearGradient
.
A LinearGradient takes a List<Color> colors
and List<double> stops
.
In order to have clear color boundaries, you duplicate the colors and stops at the boundaries.
Example:
colors: [red, red, transparent, transparent, green, green]
stops: [0.0, 0.45, 0.45, 0.55, 0.55, 1]
Full code sample
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.light(),
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final chartData = [
Data(units: 15, color: const Color(0xFF8A5426)),
Data(units: 20, color: const Color(0xFF00BCD5)),
Data(units: 12, color: const Color(0xFF7B8700)),
Data(units: 10, color: const Color(0xFFDD8B11)),
Data(units: 50, color: const Color(0xFF673BB7)),
];
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: SizedBox(
height: 20,
child: HorizontalBarChart(
data: chartData,
),
),
),
),
);
}
}
class HorizontalBarChart extends StatelessWidget {
final List<Data> data;
final double gap;
const HorizontalBarChart({
Key? key,
required this.data,
this.gap = .02,
}) : super(key: key);
List<double> get processedStops {
double totalGapsWith = gap * (data.length - 1);
double totalData = data.fold(0, (a, b) => a b.units);
return data.fold(<double>[0.0], (List<double> l, d) {
l.add(l.last d.units * (1 - totalGapsWith) / totalData);
l.add(l.last);
l.add(l.last gap);
l.add(l.last);
return l;
})
..removeLast()
..removeLast()
..removeLast();
}
List<Color> get processedColors {
return data.fold(
<Color>[],
(List<Color> l, d) => [
...l,
d.color,
d.color,
Colors.transparent,
Colors.transparent,
])
..removeLast()
..removeLast();
}
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(
Radius.circular(500),
),
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
stops: processedStops,
colors: processedColors,
),
),
);
}
}
class Data {
final double units;
final Color color;
Data({required this.units, required this.color});
}