Reputation: 135
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.
Upvotes: 4
Views: 4428
Reputation: 1654
There is a simple flutter package for that: https://pub.dev/packages/staked_horizontal_bar_chart
Upvotes: 0
Reputation: 154
I assume, the best and simplest way to do this without any packages is using row and expanded containers with flex of respective category percentages.
Like this
Widget buildSteppedProgressBar(List<Category> categories) {
double totalProgress = categories.fold(0, (sum, item) => sum + item.progress);
return Padding(
padding: const EdgeInsets.symmetric(vertical: Dimens.DIMEN_TWELVE),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Row(
children: categories.map((category) {
return Flexible(
flex: (100 * category.progress / totalProgress).ceil(),
child: Container(
margin: EdgeInsets.symmetric(horizontal: 1),
height: 20,
color: category.color,
),
);
}).toList(),
),
),
);
}
Here I expect your model to be a category model with progress and color as a property for this stepped bar.
Upvotes: 0
Reputation: 8393
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]
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});
}
Upvotes: 3
Reputation: 401
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
Upvotes: 0
Reputation: 135
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),
],
)
],
),
),
);
}
}
Upvotes: 2