😽

Flutterのfl_chartのBarChartについて

2025/01/14に公開

Flutter の fl_chart の BarChart を使用して UI を実装する機会があったので、その際調べたことについてです。

fl_chart?

Flutter でグラフを表現する際に使用するライブラリで、様々な形式に対応しています。

  • Line Chart
  • Bar Chart
  • Pie Chart
  • Scatter Chart
  • Radar Chart

公式のドキュメントを見ると、それぞれについてのドキュメントがあります。動画もアップロードされていて、50 分くらいの長さなので動画を見るとより実装イメージが湧くかもしれません。
https://www.youtube.com/watch?v=vYe0RY1nCAA&list=PL1-_rCwRcnbNpvodmbt43O81wMUdBv8-a
また、こちらのページには、実際に fl_chart を使用した例が載っています。

他の選択肢はどうか?

過去には、charts_flutterというライブラリが、Google から出てたみたいですが、現在はメンテナスされていないようです。
現状だと、以下のライブラリ達が選択肢として上がりそうです。

  • fl_chart
  • syncfusion_flutter_charts
  • graphic

fl_chart 以外の 2 つは実際に使ったことがないので、使いやすさなどは分かりませんが、syncfusion_flutter_charts・graphic のどちらも、カスタマイズ性が高く実装できるようです。

今回、fl_chart を使用した理由としては、pub.dev の LIKE 数が一番多かったのと、そこまで複雑な条件でチャートを表示する必要がなかったので、fl_chart を使用しました。

(こちらの記事を参考に比較させていただきました。)
https://zenn.dev/tsuruo/articles/a8fc96ff5aa43a

使い方

BarChartBarChartDataを使用して、実際にチャートを表示します。
BarChartは以下のように定義されています。

class BarChart extends ImplicitlyAnimatedWidget {
  /// [data] determines how the [BarChart] should be look like,
  /// when you make any change in the [BarChartData], it updates
  /// new values with animation, and duration is [duration].
  /// also you can change the [curve]
  /// which default is [Curves.linear].
  BarChart(
    this.data, {
    this.chartRendererKey,
    super.key,
    ('Please use [duration] instead')
    Duration? swapAnimationDuration,
    Duration duration = const Duration(milliseconds: 150),
    ('Please use [curve] instead') Curve? swapAnimationCurve,
    Curve curve = Curves.linear,
    this.transformationConfig = const FlTransformationConfig(),
  })  : assert(
          switch (data.alignment) {
            BarChartAlignment.center ||
            BarChartAlignment.end ||
            BarChartAlignment.start =>
              transformationConfig.scaleAxis != FlScaleAxis.horizontal &&
                  transformationConfig.scaleAxis != FlScaleAxis.free,
            _ => true,
          },
          'Can not scale horizontally when BarChartAlignment is center, '
          'end or start',
        ),
        super(
          duration: swapAnimationDuration ?? duration,
          curve: swapAnimationCurve ?? curve,
        );

  /// Determines how the [BarChart] should be look like.
  final BarChartData data;

  /// {@macro fl_chart.AxisChartScaffoldWidget.transformationConfig}
  final FlTransformationConfig transformationConfig;

  /// We pass this key to our renderers which are supposed to
  /// render the chart itself (without anything around the chart).
  final Key? chartRendererKey;

  /// Creates a [_BarChartState]
  
  _BarChartState createState() => _BarChartState();
}

dataというプロパティに、BarChartDataを渡します。ここで渡すBarChartDataにカラーなどの設定をしていくイメージです。
そのほかのdurationcurveを使うと、アニメーションを扱うことができるみたいです。

BarChart(
  BarChartData(
    // ....
  ),
  duration: Duration(milliseconds: 150), // Durationを指定
  curve: Curves.linear, // Curvesを指定
);

肝心のBarChartDataは以下のように定義されています。

class BarChartData extends AxisChartData with EquatableMixin {
  BarChartData({
    List<BarChartGroupData>? barGroups,
    double? groupsSpace,
    BarChartAlignment? alignment,
    FlTitlesData? titlesData,
    BarTouchData? barTouchData,
    double? maxY,
    double? minY,
    super.baselineY,
    FlGridData? gridData,
    super.borderData,
    RangeAnnotations? rangeAnnotations,
    super.backgroundColor,
    ExtraLinesData? extraLinesData,
    super.rotationQuarterTurns,
  })  : barGroups = barGroups ?? const [],
        groupsSpace = groupsSpace ?? 16,
        alignment = alignment ?? BarChartAlignment.spaceEvenly,
        barTouchData = barTouchData ?? BarTouchData(),
        super(
          titlesData: titlesData ??
              const FlTitlesData(
                topTitles: AxisTitles(),
              ),
          gridData: gridData ?? const FlGridData(),
          rangeAnnotations: rangeAnnotations ?? const RangeAnnotations(),
          touchData: barTouchData ?? BarTouchData(),
          extraLinesData: extraLinesData ?? const ExtraLinesData(),
          minX: 0,
          maxX: 1,
          maxY: maxY ?? double.nan,
          minY: minY ?? double.nan,
        );

  /// [BarChart] draws [barGroups] that each of them contains a list of [BarChartRodData].
  final List<BarChartGroupData> barGroups;

  /// Apply space between the [barGroups].
  final double groupsSpace;

  /// Arrange the [barGroups], see [BarChartAlignment].
  final BarChartAlignment alignment;

  /// Handles touch behaviors and responses.
  final BarTouchData barTouchData;

  /// Copies current [BarChartData] to a new [BarChartData],
  /// and replaces provided values.
  BarChartData copyWith({
    List<BarChartGroupData>? barGroups,
    double? groupsSpace,
    BarChartAlignment? alignment,
    FlTitlesData? titlesData,
    RangeAnnotations? rangeAnnotations,
    BarTouchData? barTouchData,
    FlGridData? gridData,
    FlBorderData? borderData,
    double? maxY,
    double? minY,
    double? baselineY,
    Color? backgroundColor,
    ExtraLinesData? extraLinesData,
    int? rotationQuarterTurns,
  }) =>
      BarChartData(
        barGroups: barGroups ?? this.barGroups,
        groupsSpace: groupsSpace ?? this.groupsSpace,
        alignment: alignment ?? this.alignment,
        titlesData: titlesData ?? this.titlesData,
        rangeAnnotations: rangeAnnotations ?? this.rangeAnnotations,
        barTouchData: barTouchData ?? this.barTouchData,
        gridData: gridData ?? this.gridData,
        borderData: borderData ?? this.borderData,
        maxY: maxY ?? this.maxY,
        minY: minY ?? this.minY,
        baselineY: baselineY ?? this.baselineY,
        backgroundColor: backgroundColor ?? this.backgroundColor,
        extraLinesData: extraLinesData ?? this.extraLinesData,
        rotationQuarterTurns: rotationQuarterTurns ?? this.rotationQuarterTurns,
      );

  /// Lerps a [BaseChartData] based on [t] value, check [Tween.lerp].
  
  BarChartData lerp(BaseChartData a, BaseChartData b, double t) {
    if (a is BarChartData && b is BarChartData) {
      return BarChartData(
        barGroups: lerpBarChartGroupDataList(a.barGroups, b.barGroups, t),
        groupsSpace: lerpDouble(a.groupsSpace, b.groupsSpace, t),
        alignment: b.alignment,
        titlesData: FlTitlesData.lerp(a.titlesData, b.titlesData, t),
        rangeAnnotations:
            RangeAnnotations.lerp(a.rangeAnnotations, b.rangeAnnotations, t),
        barTouchData: b.barTouchData,
        gridData: FlGridData.lerp(a.gridData, b.gridData, t),
        borderData: FlBorderData.lerp(a.borderData, b.borderData, t),
        maxY: lerpDouble(a.maxY, b.maxY, t),
        minY: lerpDouble(a.minY, b.minY, t),
        baselineY: lerpDouble(a.baselineY, b.baselineY, t),
        backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
        extraLinesData:
            ExtraLinesData.lerp(a.extraLinesData, b.extraLinesData, t),
        rotationQuarterTurns: b.rotationQuarterTurns,
      );
    } else {
      throw Exception('Illegal State');
    }
  }

  /// Used for equality check, see [EquatableMixin].
  
  List<Object?> get props => [
        barGroups,
        groupsSpace,
        alignment,
        titlesData,
        barTouchData,
        maxY,
        minY,
        baselineY,
        gridData,
        borderData,
        rangeAnnotations,
        backgroundColor,
        extraLinesData,
        rotationQuarterTurns,
      ];
}

今回実際プロジェクトで使用した際に、使用したプロパティは以下のプロパティだったので、それぞれまとめておきます。

  • barGroups : 棒グラフをグループごとにまとめて表示する際に使用します。BarChartGroupDataで棒のカラーや高さを指定するプロパティがあり、それらを指定して実際に表示していきます。
  • groupsSpace : グループごとのスペースを指定します。alignmentが、BarChartAlignment.start BarChartAlignment.centerBarChartAlignment.endの場合に適用されます。
  • alignment : Columnなどで使用するMainAxisAlignmentと同じような感じで、使用します。
  • titlesData : チャートのタイトルの部分を指定するFlTitlesDataを指定します。top、right、bottom、left それぞれのタイトルを表示するかどうかや、実際に表示する内容を、FlTitlesDataに定義します。
  • maxY : 縦軸の最大値を指定します。
  • barTouchData : チャートをタップした時の挙動を定義します。BarTouchData(enabled: false)を指定すると、タップした時に何も挙動を起こさないようになります。
  • borderData : top、right、left、bottom それぞれで、ボーダーを表示するかどうかを定義します。

実際に使用してみてどうだったか?

アニメーションをリッチにしたり、特殊なチャート形式だったりすると、fl_chart 以外の選択肢も出てくるのかもしれませんが、今回はそこまで複雑な UI ではなかったこともあり、fl_chart でも十分対応することができました。
基本的には、fl_chart でほとんどのケースを対応できるのではないでしょうか。
ぜひ使ってみてください。

参考

https://pub.dev/packages/fl_chart
https://www.youtube.com/watch?v=vYe0RY1nCAA&list=PL1-_rCwRcnbNpvodmbt43O81wMUdBv8-a
https://app.flchart.dev/#/bar
https://zenn.dev/tsuruo/articles/a8fc96ff5aa43a
https://pub.dev/packages/graphic
https://pub.dev/packages/syncfusion_flutter_charts

Discussion