3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Flutter で Bar Chart 📊 を自作しよう!

Last updated at Posted at 2024-09-15
Page 1 of 10

2024/9/14 に石川県で開催された FlutterKaigi mini #2 で LT した資料です。

💪 モチベーション

  • 🔋 愛車(EV)の日々の充電データをグラフ化したい
  • 📊 デザインやインタラクションにもこだわりたい
  • 🤔 fl_chart 等のパッケージは UI の細かい調整が難しい印象
  • 😌 Flutter で凝った UI を実装してみたいな〜

👉 📊 Bar Chart を(業務で)自作したので LT でサクっと紹介します!

iOS「ヘルスケア」アプリの Bar Chart の UI に近いものを作ったため、以降は「ヘルスケア」アプリの動作のスクリーン動画を例に、その実装例を紹介していきます。


📊 1. Bar Chart のベース部分 & スクロールによる表示内容の変化

2.gif


📊 1. Bar Chart ベース部分

  • 水平方向の ListView で、アイテムが項目軸(X)の各データ要素を表現
  • 各データ要素は基本的な Widget の組み合わせで
  • controllerPageControllerphysicsPageScrollPhysics を使うことでスクロール終了時にアイテム(=ページ)が綺麗にフィット
  • スクロールでのページ変化時に HapticFeedback つけるといい感じ
ListView.builder(
  controller: usePageController(...),
  physics: const PageScrollPhysics(),
  scrollDirection: Axis.horizontal,
  ...
);

📊 2. スクロールによる表示内容の変化

  • NotificationListenerScrollNotificationでスクロール状態を検知
  • 表示範囲内のデータによって以下の値を変化させる
    • データの期間 → ラベルと合計値
    • データの最大値 → 値軸(Y)の範囲と目盛り
NotificationListener<ScrollNotification>(
  onNotification: (notification) {
    // 表示範囲内のデータを求めてラベルや目盛りを変更
  },
  child: ListView.builder(
    ...
  )
);

📊 3. バーのアニメーション

3.gif


📊 3. バーのアニメーション

  • 値軸(Y)の範囲が変わるとデータ要素の高さも変わる
  • AnimationController で高さの変化をアニメーションさせることで、範囲が変わったことをわかりやすくなる
final controller = useAnimationController(...);
final value = useAnimation(CurvedAnimation(parent: controller, curve: ...));
...
SizedBox(
  height: barHeight * value,
  child: ...,
);

📊 4. タッチ中の要素にフォーカス

4.gif


📊 4. タッチ中の要素にフォーカス

  • データ要素をタッチ中はその要素だけのデータを値をラベルに表示
  • GestureDetector でタッチ中の要素を判定
ListView.builder(
  ...
  itemBuilder: (context, index) {
    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onTapDown: (_) => onSelectItem(index),
      onTapUp: (_) => onDeselectItem(),
      onLongPressStart: (_) => onDeselectItem(index),
      onLongPressEnd: (_) => onDeselectItem(),
      onLongPressUp: () => onDeselectItem(),
      onLongPressMoveUpdate: (details) {
        final touchIndex = /* details.globalPositionのある要素インデックス */
        onSelectItem(touchIndex);
      },
      child: ...
   );
  }
)

📊 SNS等にシェア(オマケ)

  • Bar Chart の部分を画像にして SNS 等にシェアする
  • Bar Chart カードの Widget を RepaintBoundary で囲い、指定したキーを通して得られる RenderRepaintBoundary から toImage() で画像データを取得
  • share_plusパッケージを使ってシェア
RepaintBoundary( 
  key: key, // GlobalKey
  child: BarChartCard(...),
);
final boundary =
        key.currentContext!.findRenderObject() as RenderRepaintBoundary;
final image = await boundary.toImage();
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?