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

问题描述 投票:0回答:4

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

Example
flutter dart flutter-layout flutter-widget
4个回答
3
投票

您也可以通过

LinearGradient
来实现这一点。

LinearGradient 需要

List<Color> colors
List<double> stops

为了获得清晰的颜色边界,您可以复制颜色并在边界处停止。

示例:

colors: [red, red, transparent, transparent, green, green]
stops: [0.0, 0.45, 0.45, 0.55, 0.55, 1]

完整代码示例

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

2
投票

感谢 @ChiragBargoojar 的代码,我刚刚添加了一些自定义功能,图表按照我的设计方式工作。

Outcome

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

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


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

结果

enter image description here


0
投票

我认为,在没有任何包的情况下执行此操作的最佳和最简单的方法是使用行和扩展容器以及相应类别百分比的 Flex。

像这样

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

在这里,我希望您的模型是一个类别模型,其中进度和颜色作为此阶梯栏的属性。

© www.soinside.com 2019 - 2024. All rights reserved.