🏂

【Flutter】syncfusion_flutter_chartsでグラフを書いてみる

2024/12/09に公開

はじめに

はじめまして、れむです。今回はFlutterで「syncfusion_flutter_charts」を使ってグラフを描画してみましたのでそれについて書こうと思います。
 多数のライブラリが紹介されてている「Flutter Gems」グラフ用カテゴリで見てみると2番目に「Pub Likes」の多いライブラリになります。1番多い「fl_chart」を使わなかったのは「syncfusion_flutter_charts」のライブラリ紹介ページの方が作成出来るグラフのサンプルが多く紹介されていたからです。
https://fluttergems.dev/plots-visualization/

syncfusion_flutter_chartsの紹介

ご覧の様に31種類のグラフが紹介されており、基本的なグラフから複合的なグラフまで作成出来るライブラリになっております。

 以下は「pub.dev」のリンクです。
https://pub.dev/packages/syncfusion_flutter_charts

実際に作成したグラフ

今回はアプリで用いたグラフの中から一般的な棒グラフと円グラフを紹介します。実際のアプリでは棒グラフと円グラフ共に生成されていくアニメーションが追加されています。

棒グラフ

1つ目の棒グラフでは都道府県別の来場者です。この棒グラフのアニメーションはが左から右に伸びていく様子を見ることが出来ます。都道府県だと47要素あるので画面に描画しきれないためスクロール出来るようにして表示するようにしました。

円グラフ

2つ目の円グラフでは来場者の男女別割合です。この円グラフのアニメーションでは時計周りに円グラフが生成されていく様子を見ることが出来ます。円グラフでX軸とY軸のデータマップ?と思いましたが、値を入れてみれば分かってくると思います。

実際のコード

基本的にコードはChatGPT 4oを使用して作成していますので余分な処理などが含まれている可能性がありますのでコピペする際にはご注意ください。

呼び出し側の受け渡し変数

// 性別のMap形式
genderCount = {"男性" : 0, "女性" : 0, "その他" : 0,  };

// 都道府県別のMap形式
const List<String> prefectures = [
  "北海道", "青森県", "岩手県", "宮城県", "秋田県", "山形県", "福島県",
  "茨城県", "栃木県", "群馬県", "埼玉県", "千葉県", "東京都", "神奈川県",
  "新潟県", "富山県", "石川県", "福井県", "山梨県", "長野県",
  "岐阜県", "静岡県", "愛知県", "三重県",
  "滋賀県", "京都府", "大阪府", "兵庫県", "奈良県", "和歌山県",
  "鳥取県", "島根県", "岡山県", "広島県", "山口県",
  "徳島県", "香川県", "愛媛県", "高知県",
  "福岡県", "佐賀県", "長崎県", "熊本県", "大分県", "宮崎県", "鹿児島県", "沖縄県"
];
return Map.fromEntries(prefectures.map((prefecture) => MapEntry(prefecture, 0)));
}

棒グラフのコード

import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_charts/charts.dart';

class GenderTab extends StatelessWidget {
  final Map<String, int> genderCount;
  const GenderTab(this.genderCount, {super.key});

  @override
  Widget build(BuildContext context) {
    // キー(性別)のリスト
    final List<String> keys = genderCount.keys.toList();
    // 値(人数)のリスト
    final List<int> values = genderCount.values.toList();
    // 総合計を計算
    final int totalCount = values.fold(0, (previousValue, element) => previousValue + element);

    // データをマップからリスト形式に変換
    final List<ChartData> chartData = List.generate(
      keys.length,
      (index) => ChartData(keys[index], values[index]),
    );

    return Scaffold(
      body: Center(
        child: Column(
          children: [
            Expanded(
              child: SfCircularChart(
                title: const ChartTitle(text: '性別の内訳'),
                legend: const Legend(
                  isVisible: true, // 凡例を表示
                  overflowMode: LegendItemOverflowMode.wrap,
                ),
                tooltipBehavior: TooltipBehavior(enable: true),
                series: <PieSeries<ChartData, String>>[
                  PieSeries<ChartData, String>(
                    dataSource: chartData,
                    xValueMapper: (ChartData data, _) => data.category, // 性別
                    yValueMapper: (ChartData data, _) => data.count,    // 人数
                    pointColorMapper: (ChartData data, _) {
                      // 色の割り当て
                      switch (data.category) {
                        case '男性':
                          return Colors.blue; // 男性は青色
                        case '女性':
                          return Colors.red; // 女性は赤色
                        default:
                          return Colors.grey; // その他はグレー
                      }
                    },
                    dataLabelMapper: (ChartData data, _) {
                      final double percentage = (data.count / totalCount) * 100;
                      return '${data.category}\n${data.count}人 (${percentage.toStringAsFixed(1)}%)';
                    },
                    dataLabelSettings: const DataLabelSettings(
                      isVisible: true, // データラベルを表示
                      labelPosition: ChartDataLabelPosition.outside, // ラベルを外側に配置
                    ),
                  ),
                ],
              ),
            ),
            // 総合計を表示するウィジェット
            Padding(
              padding: const EdgeInsets.all(16.0),
              child: Text(
                '総合計: $totalCount人',
                style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// 円グラフ用データモデル
class ChartData {
  ChartData(this.category, this.count);
  final String category; // 性別
  final int count;       // 人数
}

円グラフのコード

import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_charts/charts.dart';

class GenderTab extends StatelessWidget {
  final Map<String, int> genderCount;
  const GenderTab(this.genderCount, {super.key});

  @override
  Widget build(BuildContext context) {
    // キー(性別)のリスト
    final List<String> keys = genderCount.keys.toList();
    // 値(人数)のリスト
    final List<int> values = genderCount.values.toList();
    // 総合計を計算
    final int totalCount = values.fold(0, (previousValue, element) => previousValue + element);

    // データをマップからリスト形式に変換
    final List<ChartData> chartData = List.generate(
      keys.length,
      (index) => ChartData(keys[index], values[index]),
    );

    return Scaffold(
      body: Center(
        child: Column(
          children: [
            Expanded(
              child: SfCircularChart(
                title: const ChartTitle(text: '性別の内訳'),
                legend: const Legend(
                  isVisible: true, // 凡例を表示
                  overflowMode: LegendItemOverflowMode.wrap,
                ),
                tooltipBehavior: TooltipBehavior(enable: true),
                series: <PieSeries<ChartData, String>>[
                  PieSeries<ChartData, String>(
                    dataSource: chartData,
                    xValueMapper: (ChartData data, _) => data.category, // 性別
                    yValueMapper: (ChartData data, _) => data.count,    // 人数
                    pointColorMapper: (ChartData data, _) {
                      // 色の割り当て
                      switch (data.category) {
                        case '男性':
                          return Colors.blue; // 男性は青色
                        case '女性':
                          return Colors.red; // 女性は赤色
                        default:
                          return Colors.grey; // その他はグレー
                      }
                    },
                    dataLabelMapper: (ChartData data, _) {
                      final double percentage = (data.count / totalCount) * 100;
                      return '${data.category}\n${data.count}人 (${percentage.toStringAsFixed(1)}%)';
                    },
                    dataLabelSettings: const DataLabelSettings(
                      isVisible: true, // データラベルを表示
                      labelPosition: ChartDataLabelPosition.outside, // ラベルを外側に配置
                    ),
                  ),
                ],
              ),
            ),
            // 総合計を表示するウィジェット
            Padding(
              padding: const EdgeInsets.all(16.0),
              child: Text(
                '総合計: $totalCount人',
                style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// 円グラフ用データモデル
class ChartData {
  ChartData(this.category, this.count);

  final String category; // 性別
  final int count;       // 人数
}

さいごに

今回はグラフ描画ライブラリについて紹介しました。コードを書いていてX軸とY軸が90度回転している状態で定義されている気がしたので使用する際には描画されるグラフを確認しながら使ってみてください。

Discussion