マグネテック備忘録

Flutterアプリ開発の備忘録

【Flutter】アンドロイドで指定時刻に処理(android_alarm_manager_plus)



概要

  • android_alarm_manager_plusはアンドロイドでのアラームなどの実装に使えるパッケージです。
  • ↓↓公式ドキュメント[1]

pub.dev


準備

AndroidManifest.xml

公式ドキュメント[1]にある通り「android/app/src/main/AndroidManifest.xml」に変更をする。

<manifest ~>

    <!-- 追加(1) -->
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <!-- For apps with targetSDK=31 (Android 12) -->
    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
    <!-- (1)はここまで -->

    <application ~>
        
        <!-- 追加(2) -->
        <service
            android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:exported="false"/>
        <receiver
            android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmBroadcastReceiver"
            android:exported="false"/>
        <receiver
            android:name="dev.fluttercommunity.plus.androidalarmmanager.RebootBroadcastReceiver"
            android:enabled="false"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
        <!-- (2)はここまで -->

        <!-- 以下略 -->



pubspec.yaml

  • 「android_alarm_manager_plus」を追加します(バージョンは現時点での最新版)。

dependencies:
  android_alarm_manager_plus: ^3.0.3



エラー「Execution failed for task ‘:app:checkDebugDuplicateClasses’」の解決策

  • 公式ドキュメント[1]には上記までの設定しか書かれていませんでしたが、実装していくと「Execution failed for task ‘:app:checkDebugDuplicateClasses’.」というエラーが発生しました。
  • 色々調べたところ以下のサイト様[2]に「android/app/build.gradle」を書き換えればよいと書かれて、これをやれば解決しました。

flutter.salon



デモアプリ

ソースコード

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

void main() {
  WidgetsFlutterBinding.ensureInitialized(); //ウィジェット関連の初期化処理
  AndroidAlarmManager.initialize(); //初期化
  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'),
    );
  }
}

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {

  // 現在時刻を表示
  static Future<void> displayTime() async {
    print('now = ${DateTime.now()}');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            ElevatedButton(
              onPressed: () async {
                print('10秒後に現在時刻を表示(Schedule OneShot At)');

                await AndroidAlarmManager.oneShotAt(
                  DateTime.now().add(Duration(seconds: 10)),
                  0,
                  displayTime
                );
              },
              child: const Text('Schedule OneShot At'),
            ),

            ElevatedButton(
              onPressed: () async {
                print('5秒後に現在時刻を表示(Schedule OneShot Alarm)');

                await AndroidAlarmManager.oneShot(
                  const Duration(seconds: 5),
                  1,
                  displayTime,
                );
              },
              child: const Text('Schedule OneShot Alarm'),
            ),

            ElevatedButton(
              onPressed: () async {
                print('1分毎に現在時刻を表示(periodic)');

                await AndroidAlarmManager.periodic(
                  const Duration(minutes: 1),
                  // const Duration(seconds: 10),
                  2,
                  displayTime,
                  // startAt: DateTime.now(),
                  // exact: true
                );
              },
              child: const Text('periodic'),
            ),
          ]
        )
      ),
    );
  }
}



解説

初期化処理

  • AndroidAlarmManagerの初期化処理。
  • これをせずにAndroidAlarmManagerの処理をしても動作するが、初期化⇒処理という順になるので数秒処理が遅く呼び出されるかも?
AndroidAlarmManager.initialize();



指定時刻に呼び出し(oneShotAt)

  • デモアプリだと現在時刻から10秒後に処理をします。
  • displayTimeは呼び出したい処理。
// printHelloを表示
static Future<void> displayTime() async {
  print('now = ${DateTime.now()}');
}

//略

await AndroidAlarmManager.oneShotAt(
  DateTime.now().add(Duration(seconds: 10)),
  0,
  displayTime
);



指定時刻経過後に呼び出し(oneShot)

  • デモアプリだと現在時刻から5秒後に処理をします。
await AndroidAlarmManager.oneShot(
  const Duration(seconds: 5),
  1,
  displayTime,
);



一定間隔で処理を呼び出す(periodic)

  • デモアプリだと1分間隔で処理を呼び出します。
  • 1秒とか間隔が短いと正しく動作しないこともありました。
await AndroidAlarmManager.periodic(
  const Duration(minutes: 1),
  2,
  displayTime,
);



キャンセル(periodic)

  • 上記で割り当てたアラームをid指定で削除できます。
  • 例:id=0のアラームを削除
await AndroidAlarmManager.cancel(0);