Home > Software engineering >  Flutter - Horizontal Bar Chart stacked in one bar chart
Flutter - Horizontal Bar Chart stacked in one bar chart

Time:02-27

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.

Example

CodePudding user response:

Thanks for the code @ChiragBargoojar, I just added bits of customization and the graph works as how I designed it.

Outcome

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

enter image description here

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

enter image description here

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});
}
  • Related