fl_chart 条形图的样式和动画问题

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

我正在尝试使用需要显示正值和负值的条形图,但我面临三个问题:

  1. 我无法将图表放入我要显示的卡片中。
  2. 我希望图表在加载选项卡时呈现动画,但我似乎找不到方法来做到这一点。

这是正在发生的事情的图像: Screenshot of the page displaying the chart

这是条形图部分的代码:

import 'package:expense_tracker/Charts/bar_data.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';

class BarGraph extends StatelessWidget {
  final double? maxY;
  final double monAmount;
  final double tueAmount;
  final double wedAmount;
  final double thuAmount;
  final double friAmount;
  final double satAmount;
  final double sunAmount;

  const BarGraph({
    super.key,
    required this.maxY,
    required this.monAmount,
    required this.tueAmount,
    required this.wedAmount,
    required this.thuAmount,
    required this.friAmount,
    required this.satAmount,
    required this.sunAmount,
  });

  @override
  Widget build(BuildContext context) {
    //initialize bar data
    BarData myData = BarData(
      monAmount: monAmount,
      tueAmount: tueAmount,
      wedAmount: wedAmount,
      thuAmount: thuAmount,
      friAmount: friAmount,
      satAmount: satAmount,
      sunAmount: sunAmount,
    );
    myData.initializeBarData();
        

    return BarChart(
      BarChartData(
        maxY: maxY,
        minY: 0,
        gridData: FlGridData(show: false),
        borderData: FlBorderData(show: false),
        titlesData: FlTitlesData(
            show: true,
            topTitles: AxisTitles(
              sideTitles: SideTitles(showTitles: false),
            ),
            leftTitles: AxisTitles(
              sideTitles: SideTitles(showTitles: false),
            ),
            rightTitles: AxisTitles(
              sideTitles: SideTitles(showTitles: false),
            ),
            bottomTitles: AxisTitles(
              sideTitles: SideTitles(
                showTitles: true,
                getTitlesWidget: getBottomTitles,
              ),
            )),
        barGroups: myData.barData
            .map(
              (data) => BarChartGroupData(
                x: data.x,
                barRods: [
                  BarChartRodData(
                    toY: data.y,
                    color: Colors.blueGrey,
                    width: 25,
                    borderRadius: BorderRadius.circular(5),
                    backDrawRodData: BackgroundBarChartRodData(
                      show: true,
                      fromY: double.parse('-$maxY'),
                      toY: maxY,
                      color: Colors.blueGrey[300],
                    ),
                  ),
                ],
              ),
            )
            .toList(),
      ),
      swapAnimationCurve: Curves.bounceIn,
      swapAnimationDuration: const Duration(milliseconds: 300),
      
    );
  }
}

Widget getBottomTitles(double value, TitleMeta meta) {
  TextStyle style = TextStyle(
      color: Colors.blueGrey[700], fontWeight: FontWeight.bold, fontSize: 14);

  Widget text;

  switch (value.toInt()) {
    case 0:
      text = Text(
        'Mon',
        style: style,
      );
      break;
    case 1:
      text = Text(
        'Tue',
        style: style,
      );
      break;
    case 2:
      text = Text(
        'Wed',
        style: style,
      );
      break;
    case 3:
      text = Text(
        'Thu',
        style: style,
      );
      break;
    case 4:
      text = Text(
        'Fri',
        style: style,
      );
      break;
    case 5:
      text = Text(
        'Sat',
        style: style,
      );
      break;
    case 6:
      text = Text(
        'Sun',
        style: style,
      );
      break;
    default:
      text = Text(
        '',
        style: style,
      );
      break;
  }
  return SideTitleWidget(
    axisSide: meta.axisSide,
    child: text,
  );
}

  1. 我尝试过填充、大小框、容器等,但在条形图上没有任何效果。
  2. 对于动画,我根本不知道我能做些什么来实现它。

这是代码的可重现版本。

IndividualBar
BarData
BarGraph
在原始代码中都位于不同的dart文件中:

import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blueGrey,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Expanded(
            flex: 1,
            child: Card(
              color: Colors.blueGrey[200],
              child: const SizedBox(
                height: 100,
                child: BarGraph(
                  maxY: 50,
                  monAmount: 20,
                  tueAmount: 25,
                  wedAmount: 43,
                  thuAmount: -10,
                  friAmount: -36,
                  satAmount: 16,
                  sunAmount: -10,
                ),
              ),
            ),
          ),
          Expanded(
            flex: 1,
            child: Card(
              color: Colors.blueGrey[200],
              child: SizedBox(
                width: MediaQuery.of(context).size.width,
              ),
            ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

class IndividualBar {
  final int x; //position on x axis
  final double y; //amount

  IndividualBar({required this.x, required this.y});
}

class BarData {
  final double monAmount;
  final double tueAmount;
  final double wedAmount;
  final double thuAmount;
  final double friAmount;
  final double satAmount;
  final double sunAmount;

  BarData({
    required this.monAmount,
    required this.tueAmount,
    required this.wedAmount,
    required this.thuAmount,
    required this.friAmount,
    required this.satAmount,
    required this.sunAmount,
  });

  //make a list from Individual bar data
  List<IndividualBar> barData = [];

  //initialize bar data
  void initializeBarData() {
    barData = [
      //mon
      IndividualBar(x: 0, y: monAmount),
      //tue
      IndividualBar(x: 1, y: tueAmount),
      //wed
      IndividualBar(x: 2, y: wedAmount),
      //thu
      IndividualBar(x: 3, y: thuAmount),
      //fri
      IndividualBar(x: 4, y: friAmount),
      //sat
      IndividualBar(x: 5, y: satAmount),
      //sun
      IndividualBar(x: 6, y: sunAmount),
    ];
  }
}

class BarGraph extends StatelessWidget {
  final double? maxY;
  final double monAmount;
  final double tueAmount;
  final double wedAmount;
  final double thuAmount;
  final double friAmount;
  final double satAmount;
  final double sunAmount;

  const BarGraph({
    super.key,
    required this.maxY,
    required this.monAmount,
    required this.tueAmount,
    required this.wedAmount,
    required this.thuAmount,
    required this.friAmount,
    required this.satAmount,
    required this.sunAmount,
  });

  @override
  Widget build(BuildContext context) {
    //initialize bar data
    BarData myData = BarData(
      monAmount: monAmount,
      tueAmount: tueAmount,
      wedAmount: wedAmount,
      thuAmount: thuAmount,
      friAmount: friAmount,
      satAmount: satAmount,
      sunAmount: sunAmount,
    );
    myData.initializeBarData();

    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: BarChart(
        BarChartData(
          //alignment: BarChartAlignment.spaceAround,
          maxY: maxY,
          minY: double.parse('-$maxY'),
          gridData: FlGridData(show: false),
          borderData: FlBorderData(show: false),
          titlesData: FlTitlesData(
              show: true,
              topTitles: AxisTitles(
                sideTitles: SideTitles(showTitles: false),
              ),
              leftTitles: AxisTitles(
                sideTitles: SideTitles(showTitles: false),
              ),
              rightTitles: AxisTitles(
                sideTitles: SideTitles(showTitles: false),
              ),
              bottomTitles: AxisTitles(
                sideTitles: SideTitles(
                  showTitles: true,
                  getTitlesWidget: getBottomTitles,
                ),
              )),
          barGroups: myData.barData
              .map(
                (data) => BarChartGroupData(
                  x: data.x,
                  barRods: [
                    BarChartRodData(
                      toY: data.y,
                      color: data.y > 0
                          ? const Color.fromARGB(255, 0, 212, 138)
                          : const Color.fromARGB(255, 231, 0, 0),
                      width: 40,
                      borderRadius: BorderRadius.circular(5),
                      borderSide: BorderSide(
                        width: 2.0,
                        color: data.y > 0
                            ? const Color.fromARGB(255, 28, 169, 119)
                            : const Color.fromARGB(255, 171, 23, 23),
                      ),
                      backDrawRodData: BackgroundBarChartRodData(
                        show: true,
                        fromY: double.parse('-$maxY'),
                        toY: maxY,
                        color: Colors.blueGrey[300],
                      ),
                    ),
                  ],
                ),
              )
              .toList(),
        ),
        swapAnimationCurve: Curves.bounceIn,
        swapAnimationDuration: const Duration(milliseconds: 300),
      ),
    );
  }
}

Widget getBottomTitles(double value, TitleMeta meta) {
  TextStyle style = TextStyle(
      color: Colors.blueGrey[700], fontWeight: FontWeight.bold, fontSize: 14);

  Widget text;

  switch (value.toInt()) {
    case 0:
      text = Text(
        'Mon',
        style: style,
      );
      break;
    case 1:
      text = Text(
        'Tue',
        style: style,
      );
      break;
    case 2:
      text = Text(
        'Wed',
        style: style,
      );
      break;
    case 3:
      text = Text(
        'Thu',
        style: style,
      );
      break;
    case 4:
      text = Text(
        'Fri',
        style: style,
      );
      break;
    case 5:
      text = Text(
        'Sat',
        style: style,
      );
      break;
    case 6:
      text = Text(
        'Sun',
        style: style,
      );
      break;
    default:
      text = Text(
        '',
        style: style,
      );
      break;
  }
  return SideTitleWidget(
    axisSide: meta.axisSide,
    child: text,
  );
}

从这一切中可以很清楚地看出,我对 Flutter 非常陌生......所以我将依靠这里出色的人们的帮助! :)

提前致谢!

flutter flutter-animation
1个回答
0
投票

如果您还没有找到这个问题的答案,我可能有一些有用的信息。

  1. 设置图表小部件的宽高比以防止绘制错误。 如果您想将条形图放入卡片中,您可以使用小部件“AspectRatio”来设置约束。您还可以使用具有约束的 Container 或具有固定高度或宽度的 SizedBox。如果您希望图表适合卡片而不溢出渲染框,我建议使用 AspectRatio。

例如:

Card(
  child: AspectRatio(
    aspectRatio: 16/9, // HERE YOU CAN SET YOUR CUSTOM ASPECT RATIO LIKE 4/3, etc.
    child: BarGraph(
     ...
    ),
  ),
),

纵横比小部件

  1. 避免在无状态和有状态小部件的 Build 方法和示例中执行代码或函数。 每次用户与 UI 交互(例如滚动或按下按钮)时,flutter 中的构建方法都会执行,如果您在覆盖构建方法中运行代码,它将执行多次,并且会导致问题或性能问题。

widget 有两种类型:StatefulWidget、StatelessWidget。

总而言之,无状态小部件是那些不需要更改其内部值的小部件,例如不更改其值的标签。

有状态小部件是那些需要更新其中某些值的小部件,例如文本字段或具有多个文本字段和按钮的完整登录页面。

在您的情况下,如果您希望 BarChart 类是可变的,例如在添加随机化图表值的按钮时,我建议使用 StatefulWidget,这取决于您实际如何使用它。

无状态和有状态小部件

  1. 动画如何运作 flutter中有多种类型的动画,我记得的有:隐式动画和补间动画。

隐式动画是指当您更改小部件内部的值时,小部件会自动产生动画,例如:AnimatedOpacity,在此小部件中,您只需动态更改不透明度属性,它就会自动产生动画。

Tween 动画稍微复杂一点,它使用 AnimationController 和 Tween 值以编程方式控制小部件值,一个示例可能是进度条,您希望在按下按钮时从 0 到 100 进行动画处理,但您希望逐渐增加值,因此它看起来是动画的。

在您的场景中,fl_charts 包使用隐式动画来对图表的更改进行动画处理,因此您需要使用变量或变量列表来更改条形值,例如,您可以使用 dart 随机库以及整数列表来实现此目的:

import 'dart:math';

List<Int> values = [];

void onButtonPressed() {
  Random random = new Random();
  values = List.generate(10, () => random.nextInt(100)); // THIS REPLACES CURRENT LIST FOR 10 RANDOMIZED INTEGERS FROM 0 to 99
}

然后只需使用该整数列表将它们分配给您的图表值:

BarChart(
    BarChartData(
      //alignment: BarChartAlignment.spaceAround,
      maxY: maxY,
      minY: double.parse('-$maxY'),
      gridData: FlGridData(show: false),
      borderData: FlBorderData(show: false),
      titlesData: FlTitlesData(
          show: true,
          topTitles: AxisTitles(
            sideTitles: SideTitles(showTitles: false),
          ),
          leftTitles: AxisTitles(
            sideTitles: SideTitles(showTitles: false),
          ),
          rightTitles: AxisTitles(
            sideTitles: SideTitles(showTitles: false),
          ),
          bottomTitles: AxisTitles(
            sideTitles: SideTitles(
              showTitles: true,
              getTitlesWidget: getBottomTitles,
            ),
          )),
      barGroups: values
          .map(
            (value) => BarChartGroupData(
              x: value.index, // NOTE THAT THIS IS AN EXAMPLE AND YOU WOULD NEED TO ADAPT IT TO YOUR REAL CODE
              barRods: [
                BarChartRodData(
                  toY: value, // NOW "VALUE" IS A RANDOMIZED VALUE FROM 0 TO 99, BUT WITH REAL VALUES THE COLOR CODE WILL WORK CORRECTLY FOR NEGATIVES AND POSITIVES.
                  color: value > 0
                      ? const Color.fromARGB(255, 0, 212, 138)
                      : const Color.fromARGB(255, 231, 0, 0),
                  width: 40,
                  borderRadius: BorderRadius.circular(5),
                  borderSide: BorderSide(
                    width: 2.0,
                    color: value > 0
                        ? const Color.fromARGB(255, 28, 169, 119)
                        : const Color.fromARGB(255, 171, 23, 23),
                  ),
                  backDrawRodData: BackgroundBarChartRodData(
                    show: true,
                    fromY: double.parse('-$maxY'),
                    toY: maxY,
                    color: Colors.blueGrey[300],
                  ),
                ),
              ],
            ),
          )
          .toList(),
    ),
    swapAnimationCurve: Curves.bounceIn,
    swapAnimationDuration: const Duration(milliseconds: 300),
  ),

现在图表栏上使用了列表“值”,并且当您按下按钮时值会发生变化,然后 Flutter 将自动为您的图表添加动画效果。

颤动动画

我希望通过这些解释和例子你可以实现你想要的。

问候。

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