Flutter 現在時刻をリアルタイムに表示する時計アプリ animation無し

2023/06/24に公開1

今回は、Flutterで『リアルタイム』を『リアルタイム』に表示する方法を解説します。

今回、Flutterでリアルタイムに現在時間を表示するアプリを作成しました。
この回では、実際に作成したソースコードのロジックを解説しており、『誰でもリアルタイムアプリが作成できる』ようになっております。また、Githubにもソースコードを載せてますので、
是非、クローンして使ってみてください。

Githubへディレクトリをあげる方法について、以前解説したので
気になる方は以下の記事を読んでみてください。

https://zenn.dev/lisras/articles/00856eab6aa632

✅今回の内容

  1. この回で作成するアプリ
  2. 現在時刻の取得と反映
  3. プログラムの解説
  4. まとめ

✅メイン内容

🛠 この回で作成するアプリ

今回は、下記のようなアプリを作成します。
お手軽に出来て楽しいので、以降の解説もぜひお読みください。


🛠 現在時刻の取得と反映

1. 現在時刻の取得

まず、Flutterでの現在時刻の取得について。
Flutterで現在時刻を取得するには下記のようなコードを書きます。

DateTime nowtime = DateTime.now()

このDateTimeクラスを利用するためには下記のパッケージをimportする必要があります。

intl: ^0.18.1

上記のintlpubspec.yamlファイルに追加しましょう。

pub getを実行して、DateTimeクラスを使用できるようにします。

ひとまず、現在時刻を扱うための設定は完了です。🍎


2. 画面にリアルタイムで反映

次に、実際に画面にどう反映させるかについて。

先ほどのDateTime nowtime = DateTime.now();ではnowtime変数に
現在時刻を代入する処理を行なっております。

しかし、この現在時刻というのはアプリがビルドされて、
nowtime変数の初期化が実行された時の時間となります。

画面にリアルタイムを更新し続けて表示するには、毎秒現在時刻を取得する必要があります。

やり方は複数あるのですが、今回は初心者の方でもイメージのつきやすいStatefulWidget
を使用して、毎秒現在時刻を取得して画面に反映させていきたいと思います。


🛠 プログラムの解説

1. プログラム全体

import 'dart:async';
import 'dart:ffi';
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

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

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

  // This widget is the root of your application.
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'リアルタイム取得'),
    );
  }
}

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  String now_time = DateFormat('HH:mm:ss').format(DateTime.now()).toString();
  int index = 0;

  
  void initState() {
    super.initState();
    Timer.periodic(Duration(seconds: 1), _onTimer);
  }

  void _onTimer(Timer timer) {
    var new_time = DateFormat('HH:mm:ss').format(DateTime.now());
    setState(() => {
          now_time = new_time,
        });
  }


  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              DateFormat.yMMMMEEEEd('ja').format(DateTime.now()).toString(),
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
            SizedBox(
              height: 10,
            ),
            Container(
              width: 200,
              height: 200,
              decoration: BoxDecoration(
                color: Colors.yellow[200],
                borderRadius: BorderRadius.circular(20),
              ),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  Text("ただいまの時刻"),
                  Text(
                    '${now_time}',
                    style: Theme.of(context).textTheme.headlineMedium,
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}


1. 画面表示の部分

child: Column(
       mainAxisAlignment: MainAxisAlignment.center,
       crossAxisAlignment: CrossAxisAlignment.center,
           children: <Widget>[
               Text("ただいまの時刻"),
               Text(
                 '${now_time}',
                 style: Theme.of(context).textTheme.headlineMedium,
               ),
	],
),

上記のコードが画面表示の部分になります。
now_time変数を表示しているだけのシンプルなコードです。

このnow_time変数は、_MyHomePageStateクラスが呼ばれた時に初期化をしています。

class _MyHomePageState extends State<MyHomePage> {
  String now_time = DateFormat('HH:mm:ss').format(DateTime.now()).toString();

下記のコードの解説が必要ですね。

DateFormat('HH:mm:ss').format(DateTime.now())

上記のコードでは、
現在時刻を「時:分:秒」にフォーマットして取得する』という処理を行なっております。

これにより、アプリ起動時にnow_time変数が初期化される時、変数の中には
「時:分:秒」にフォーマットされた現在時刻が格納されます。

そのnow_time変数を画面に表示するのが上記の画面表示のコードになります。


2. Statefullで毎秒画面を更新する

先ほど解説した画面表示のコードだけでは、アプリビルド時の現在時刻だけが表示されます。
Flutterで画面を再描画するためにはbuildメソッドを発火させなければなりません。

StatefullWidgetでは、buildメソッドを発火させるトリガー(SetStateメソッド)
を発火させることで画面を再描画することが可能です。

今回のアプリで発火の役割を担っているのが下記のコード

  
  void initState() {
    super.initState();
    Timer.periodic(Duration(seconds: 1), _onTimer);
  }

  void _onTimer(Timer timer) {
    var new_time = DateFormat('HH:mm:ss').format(DateTime.now());
    setState(() => {
          now_time = new_time,
        });
  }

initStateメソッドから。
initStateメソッドは、画面が生成されるタイミングで一度だけ実行されるメソッドです。

initStateinitinitializeで、初期化という意味を持ちます。
詳しくは今後の記事で解説しますので、
今回は『画面の生成時に一度だけ呼ばれるメソッド』と覚えてもらればと思います。

このinitStateの中で、以下の処理が実行されています。

Timer.periodic(Duration(seconds: 1), _onTimer);

ここが、今回のメインとなる部分です。

Timer.periodicとは、
1回または繰り返し起動するようにできているカウントダウンタイマー』です。

上記のコードでは、1秒経つごとに_onTimerメソッドを呼び出す。 という処理を
Timer.periodicで実行しています。

呼び出される_onTimerメソッドの中身は下記の通り。

  void _onTimer(Timer timer) {
    var new_time = DateFormat('HH:mm:ss').format(DateTime.now());
    setState(() => {
          now_time = new_time,
        });
  }

new_time変数の部分は序盤に一度解説しているのでスキップ。
(現在時刻を「時:分:秒」で取得する処理)

このメソッド内で見るべき部分は下記の部分です。

setState(() => {
          now_time = new_time,
        });

このsetStateメソッドは先ほどから何度か出てきた、buildメソッドを発火させるトリガーとなるメソッドです。

このsetStateメソッドを実行することで画面が再描画されます。
再描画のタイミングで、画面に表示しているnow_time変数にnew_time変数の内容を代入します。このようにする事で、新しく描画された画面に毎秒新しい時間を表示させる事ができるのです。

その他の部分に関しては、UI等なので今回は解説しません。


3. 実際の動作

上記で解説した内容を用いて、さらにUIを強化してみました。
下記動画のプログラムでは、
現在時間の取得と同時に10秒毎にを並べて表示する処理を追加しております。
また、そのに合わせて針が動くような処理を実装してます。

animationを使わなくても、このくらいシンプルな構成であれば簡単に作る事ができるので、
是非、下記のGithubからクローンして使用してみてください。🍎

https://github.com/LisRas-code/zenn_clock_app

実際の動作がこちら。


✅まとめ

今回は、『リアルタイム』を『リアルタイム』に表示する方法を解説しました。

Riverpodを使った状態管理を行なっていなかったので、自由度が低かったですが、
次はRiverpodを使ってこれよりオモシロイ時計アプリを作成できればと思います。

この時計アプリは、state管理の学習やflutterの画面描画の仕組みを理解するのにとても良いので、今後もアップデートを重ねて自身のLv.UPに繋げていきたいです。

Flutterをこれから学習しようと考えている方は、
是非、下記の最新版Flutter環境構築の仕方を参考にしてください。

https://zenn.dev/lisras/articles/9f4fe12f920e59

今回はここまで 

Discussion