Flutter - 水平条形图堆叠在一个条形图中

Flutter - Horizontal Bar Chart stacked in one bar chart

我正在尝试实现这种堆叠在一个条形图中的水平条形图。我遇到了 fl_chart 包,但其中的 none 似乎具有我正在寻找的类型。如果有任何冠军可以支持我为我提供实现此目标的步骤,或者示例代码将非常有帮助。非常感谢您。

感谢@ChiragBargoojar 提供的代码,我只是添加了一些自定义项,图表按照我设计的方式工作。

如果还有人想知道,这里是代码:

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),
                      ],
                    )
          ],
        ),
      ),
    );
  }
}

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),
            ),
          ),
        ),
      ),
    );
  }
}

结果

您也可以使用 LinearGradient 来实现。

LinearGradient 采用 List<Color> colorsList<double> stops

为了具有清晰的颜色边界,您复制颜色并在边界处停止。

示例:

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