フレームレートとは？

# 60fps, 120fpsというワードを知らない　
昔のFlutterの本を読んでいて、アニメーションがスムーズでなかったり、反応が遅かったりする話題が気になりました。
UIのパフォーマンスを見る際に重要なのが、フレームレートなるものだそうです。フレームレートとは、１秒間に見せるフレームの数です。　　
パラパラ漫画をイメージするとわかりやすいそうです。

参考になりそうな情報:
https://app-best.jp/articles/description_fps/

https://www.adobe.com/jp/creativecloud/video/discover/frame-rate.html

[Flutter公式の情報](https://docs.flutter.dev/perf/ui-performance)

:::message
学習内容 Flutter は、1 秒あたり 60 フレーム (fps) のパフォーマンス、または 120 Hz 更新が可能なデバイスで 120 fps のパフォーマンスを提供することを目指しています。60 fps の場合、フレームは約 16 ミリ秒ごとにレンダリングする必要があります。UI がスムーズにレンダリングされないと、ジャンクが発生します。たとえば、時々、フレームのレンダリングに 10 倍の時間がかかるため、フレームがドロップされ、アニメーションが目に見えてぎくしゃくします。
:::

:::message
Flutter フレームワークは、60fps でスムーズでないアプリケーションを作成しにくいように設計されています。多くの場合、ジャンクが発生するのは、単純なバグが原因で、フレームごとに必要以上に UI が再構築されるためです。ウィジェット再構築プロファイラーは、このような種類のバグによるパフォーマンスの問題をデバッグして修正するのに役立ちます。
:::

こんなコードを書くとパフォーマンスが下がるかも？
```dart
import 'package:flutter/material.dart';
import 'dart:math' as math;

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

enum ColorSchemeType {
  light,
  dark,
}

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  // lateをAnimationControllerにつけると、初期化を遅らせることができる。..repeat()で繰り返しアニメーションを行う。
  late final AnimationController _controller = AnimationController(
    duration: const Duration(seconds: 10),
    vsync: this,
  )..repeat();

  // AnimationControllerを破棄する必要があるので、dispose()をオーバーライドする。
  // @override
  // void dispose() {
  //   _controller.dispose();
  //   super.dispose();
  // }

  int _counter = 0;
  ColorSchemeType _colorSchemeType = ColorSchemeType.light;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  void _toggleTheme() {
    setState(() {
      _colorSchemeType = _colorSchemeType == ColorSchemeType.light
          ? ColorSchemeType.dark
          : ColorSchemeType.light;
    });
  }

  @override
  Widget build(BuildContext context) {
    final isLightTheme = _colorSchemeType == ColorSchemeType.light;
    final theme = ThemeData(
      brightness: isLightTheme ? Brightness.light : Brightness.dark,
      colorScheme: isLightTheme
          ? ColorScheme.fromSeed(seedColor: Colors.deepPurple)
          : const ColorScheme.dark(),
      useMaterial3: true,
    );

    return MaterialApp(
      title: 'Flutter Demo',
      theme: theme,
      home: Scaffold(
        appBar: AppBar(
          actions: [
            IconButton(
              icon: Icon(isLightTheme ? Icons.dark_mode : Icons.light_mode),
              onPressed: _toggleTheme,
            ),
          ],
          backgroundColor: theme.colorScheme.inversePrimary,
          title: Text(widget.title),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text(
                'You have pushed the button this many times:',
              ),
              Text(
                '$_counter',
                style: theme.textTheme.headlineMedium,
              ),
              Center(
                child: AnimatedBuilder(
                  animation: _controller, // AnimationControllerを渡す。
                  child: Container(
                    width: 200.0,
                    height: 200.0,
                    color: Colors.green,
                    child: const Center(
                      child: Text('くるくる回っちゃうもんね〜'),
                    ),
                  ),
                  builder: (BuildContext context, Widget? child) {
                    // Transform.rotateで回転させる。
                    return Transform.rotate(
                      angle: _controller.value * 2.0 * math.pi,
                      child: child,
                    );
                  },
                ),
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: _incrementCounter,
          tooltip: 'Increment',
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}
```

何度も赤い棒線が出てきた、ジャンクが多いことになります。　一瞬で消えたので、スクリーンショットに撮れませんでした💦
![](https://storage.googleapis.com/zenn-user-upload/615c93e4e369-20240801.png)


## 最後に
今回は、Flutterのフレームレートについてお勉強してみました。Flutterにはパフォーマンスを良くするために、推奨する方法があるようなので、それに従いましょう。

学習内容 Flutter は、1 秒あたり 60 フレーム (fps) のパフォーマンス、または 120 Hz 更新が可能なデバイスで 120 fps のパフォーマンスを提供することを目指しています。60 fps の場合、フレームは約 16 ミリ秒ごとにレンダリングする必要があります。UI がスムーズにレンダリングされないと、ジャンクが発生します。たとえば、時々、フレームのレンダリングに 10 倍の時間がかかるため、フレームがドロップされ、アニメーションが目に見えてぎくしゃくします。