⏱️

【Flutter】特定の画面で表示時間をStopwatchで計測したい。

2023/09/20に公開

はじめに

はじめまして。たけです。
初稿です。よろしくお願いします🌱

今回は、Flutterプロジェクトで特定の画面の表示時間を計測する機能についてまとめました。
この機能は、ユーザーがどれくらいその画面を見ていたかを把握する際に便利です。
アプリの使いやすさの改善や、特定のコンテンツへの関心度を測るためのデータとして利用します。

実装方法

  • riverpod, state_notifier, freezedを使用して、この機能を実装する方法を説明します。
  • 経過時間を測るには、Stopwatchクラスを使います。
  • アプリのライフサイクルを管理し、特定の画面が表示されている時間をストップウォッチで計測します。

考慮する点

  • アプリのライフサイクルを適切に管理し、バックグラウンドとフォアグラウンドの状態を識別できるようにする。
  • Freezedパッケージを利用して状態を管理し、コードの保守性と安全性を確保する。
  • Riverpod,StateNotifierを利用して状態管理を行うことで、状態の流れを明確にし、テストやデバッグを容易にする。

では、実装手順を見ていきましょう。

ステップ1: 依存関係の追加

プロジェクトのpubspec.yamlに以下の依存関係を追加します。バージョンはそれぞれの最新を指定してください。

dependencies:
  flutter:
    sdk: flutter
  hooks_riverpod: ^最新のバージョン
  freezed_annotation: ^最新のバージョン
  state_notifier: ^最新のバージョン

dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^最新のバージョン
  freezed: ^最新のバージョン

ステップ2: Freezedクラスの作成

sample_state.dartに、以下のコードを追加してSampleScreenStateを定義します。

import 'package:freezed_annotation/freezed_annotation.dart';

part 'sample_state.freezed.dart';


class SampleScreenState with _$SampleScreenState {
  const SampleScreenState._();

  const factory SampleScreenState({
    (Duration.zero) Duration totalDuration,
  }) = _SampleScreenState;
}

ステップ3: StateNotifierの作成

sample_screen_controller.dartに、以下のコードを追加してSampleScreenControllerを定義します。
didChangeAppLifecycleStateでは、アプリのライフサイクルを適切に管理し、バックグラウンドとフォアグラウンドの状態を識別できるようにしています。

import 'package:flutter/material.dart';
import 'package:riverpod/riverpod.dart';
import 'sample_state.dart';

class SampleScreenController extends StateNotifier<SampleScreenState>
    with WidgetsBindingObserver {
  SampleScreenController() : super(const SampleScreenState()) {
    WidgetsBinding.instance.addObserver(this); // アプリのライフサイクルを監視
    _stopwatch.start(); // ストップウォッチを開始
  }

  final Stopwatch _stopwatch = Stopwatch(); // ストップウォッチのインスタンスを作成

  
  void dispose() {
    WidgetsBinding.instance?.removeObserver(this); // アプリのライフサイクルの監視を停止
    _stopwatch.stop(); // ストップウォッチを停止
    state = state.copyWith(totalDuration: _stopwatch.elapsed); // 総経過時間を更新
    super.dispose();
  }

  
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      // アプリが前面に戻ったときにタイマーを再開
      _stopwatch.start();
    } else if (state == AppLifecycleState.paused) {
      // アプリが背景に移動したときにタイマーを停止
      _stopwatch.stop();
    }
  }
}

ステップ4: プロバイダの作成

次に、プロバイダを作成します。sample_provider.dartにプロバイダを定義します。

import 'package:riverpod/riverpod.dart';
import 'sample_screen_controller.dart';
import 'sample_state.dart';

final sampleScreenProvider = StateNotifierProvider<SampleScreenController, SampleScreenState>(
  (ref) => SampleScreenController(),
);

ステップ5: Widgetの作成

sample_screen.dartに、SampleScreenを定義します。
このステップでは、アプリの特定の画面の表示時間を計測し、その時間を画面に表示する仕組みを作成します。

import 'package:flutter/material.dart';
import 'package:riverpod/riverpod.dart';
import 'sample_provider.dart';

class SampleScreen extends ConsumerWidget {
  
  Widget build(BuildContext context, WidgetRef ref) {
    final state = ref.watch(sampleScreenProvider); // 状態を監視

    return Scaffold(
      appBar: AppBar(
        title: Text('Sample Screen'),
      ),
      body: Center(
        child: Text('Total Duration: ${state.totalDuration.inSeconds} seconds'), // 総経過時間を表示
      ),
    );
  }
}

ステップ6: UIの更新

SampleScreenElevatedButtonを使用して表示した時間を表示するupdateDurationメソッドと時間をリセットするresetDurationメソッドを呼び出し、計測した経過時間をStringとして表示できるようにします。

まず、SampleScreenControllerupdateDurationメソッドを作成し、以下のように実装します。

  void updateDuration() {
    state = state.copyWith(totalDuration: _stopwatch.elapsed); // 総経過時間を更新
  }

  void resetDuration() {
    _stopwatch.reset();
    state = state.copyWith(totalDuration: _stopwatch.elapsed);
    _stopwatch.start();
  }

次に、SampleScreenupdateDurationメソッドとresetDurationメソッドを呼び出すとともに、経過時間をStringで表示できるようにします。

      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'Total Duration: ${state.totalDuration.inSeconds} seconds',
              style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
            ),
            const Gap(30),
            //時間計測出力ボタン
            ElevatedButton(
              onPressed: () {
                ref
                    .read(sampleScreenProvider.notifier)
                    .updateDuration(); // updateDurationメソッドを呼び出し
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(
                    content: Text(
                        'Total Duration: ${ref.watch(sampleScreenProvider).totalDuration}'), // 総経過時間をStringで表示
                  ),
                );
              },
              child: Text(
                ' Stop',
                style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
              ),
            ),
            //時間計測リセットボタン
            ElevatedButton(
              onPressed: () {
                ref
                    .read(sampleScreenProvider.notifier)
                    .resetDuration(); // updateDurationメソッドを呼び出し
              },
              child: Text(
                ' Reset',
                style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
              ),
            ),
          ],
        ), // 総経過時間を表示
      ),

操作確認動画

まとめ

最後まで読んでくださりありがとうございます。
今回はFlutterで特定の画面を表示している時間を計測Stopwatchクラスを活用する方法を紹介してみました!
気になる点がありましたらお声がけください!
よろしければ、ハート・フォロー・シェアをいただけますと喜びます :)

Discussion