diff --git a/packages/android_alarm_manager_plus/example/lib/main.dart b/packages/android_alarm_manager_plus/example/lib/main.dart index 061f5af4a6..0698c267b8 100644 --- a/packages/android_alarm_manager_plus/example/lib/main.dart +++ b/packages/android_alarm_manager_plus/example/lib/main.dart @@ -1,186 +1,138 @@ // Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. + import 'dart:developer' as developer; import 'dart:isolate'; import 'dart:math'; -import 'dart:ui'; import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter/material.dart'; -/// The [SharedPreferences] key to access the alarm fire count. const String countKey = 'count'; - -/// The name associated with the UI isolate's [SendPort]. const String isolateName = 'isolate'; -/// A port used to communicate from a background isolate to the UI isolate. -ReceivePort port = ReceivePort(); - -/// Global [SharedPreferences] object. -SharedPreferences? prefs; +final ReceivePort port = ReceivePort(); +late final SharedPreferences prefs; +/// Entry point of the app Future main() async { WidgetsFlutterBinding.ensureInitialized(); - // Register the UI isolate's SendPort to allow for communication from the - // background isolate. - IsolateNameServer.registerPortWithName( - port.sendPort, - isolateName, - ); + IsolateNameServer.registerPortWithName(port.sendPort, isolateName); prefs = await SharedPreferences.getInstance(); - if (!prefs!.containsKey(countKey)) { - await prefs!.setInt(countKey, 0); + if (!prefs.containsKey(countKey)) { + await prefs.setInt(countKey, 0); } runApp(const AlarmManagerExampleApp()); } -/// Example app for Espresso plugin. class AlarmManagerExampleApp extends StatelessWidget { const AlarmManagerExampleApp({super.key}); - // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter Demo', + title: 'Flutter Alarm Manager', theme: ThemeData( useMaterial3: true, colorSchemeSeed: const Color(0x9f4376f8), ), - home: const _AlarmHomePage(), + home: const AlarmHomePage(), ); } } -class _AlarmHomePage extends StatefulWidget { - const _AlarmHomePage(); +class AlarmHomePage extends StatefulWidget { + const AlarmHomePage({super.key}); @override _AlarmHomePageState createState() => _AlarmHomePageState(); } -class _AlarmHomePageState extends State<_AlarmHomePage> { +class _AlarmHomePageState extends State { int _counter = 0; PermissionStatus _exactAlarmPermissionStatus = PermissionStatus.granted; + static SendPort? uiSendPort; @override void initState() { super.initState(); AndroidAlarmManager.initialize(); _checkExactAlarmPermission(); - - // Register for events from the background isolate. These messages will - // always coincide with an alarm firing. - port.listen((_) async => await _incrementCounter()); + port.listen((_) => _incrementCounter()); } - void _checkExactAlarmPermission() async { - final currentStatus = await Permission.scheduleExactAlarm.status; + Future _checkExactAlarmPermission() async { + final status = await Permission.scheduleExactAlarm.status; if (!mounted) return; - setState(() { - _exactAlarmPermissionStatus = currentStatus; - }); + setState(() => _exactAlarmPermissionStatus = status); } Future _incrementCounter() async { - developer.log('Increment counter!'); - // Ensure we've loaded the updated count from the background isolate. - await prefs?.reload(); - - setState(() { - _counter++; - }); + developer.log('Increment counter!', name: 'AlarmManagerExample'); + await prefs.reload(); + if (!mounted) return; + setState(() => _counter++); } - // The background - static SendPort? uiSendPort; - - // The callback for our alarm @pragma('vm:entry-point') static Future callback() async { - developer.log('Alarm fired!'); - // Get the previous cached count and increment it. final prefs = await SharedPreferences.getInstance(); final currentCount = prefs.getInt(countKey) ?? 0; await prefs.setInt(countKey, currentCount + 1); - // This will be null if we're running in the background. uiSendPort ??= IsolateNameServer.lookupPortByName(isolateName); uiSendPort?.send(null); } + Future _requestPermission() async { + await Permission.scheduleExactAlarm + .onGrantedCallback(() => setState(() => _exactAlarmPermissionStatus = PermissionStatus.granted)) + .request(); + } + + Future _scheduleAlarm() async { + await AndroidAlarmManager.oneShot( + const Duration(seconds: 5), + Random().nextInt(1 << 31), + callback, + exact: true, + wakeup: true, + ); + } + @override Widget build(BuildContext context) { final textStyle = Theme.of(context).textTheme.headlineMedium; return Scaffold( - appBar: AppBar( - title: const Text('Android alarm manager plus example'), - elevation: 4, - ), + appBar: AppBar(title: const Text('Android Alarm Manager Example')), body: Center( child: Column( - crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ const Spacer(), - Text( - 'Alarms fired during this run of the app: $_counter', - style: textStyle, - textAlign: TextAlign.center, - ), + Text('Alarms fired during this run: $_counter', style: textStyle, textAlign: TextAlign.center), const SizedBox(height: 16), + Text('Total alarms fired: ${prefs.getInt(countKey)}', style: textStyle, textAlign: TextAlign.center), + const Spacer(), Text( - 'Total alarms fired since app installation: ${prefs?.getInt(countKey).toString() ?? ''}', - style: textStyle, + _exactAlarmPermissionStatus.isDenied + ? 'Exact alarm permission denied. Alarms not available.' + : 'Exact alarm permission granted. Alarms available.', textAlign: TextAlign.center, ), - const Spacer(), - if (_exactAlarmPermissionStatus.isDenied) - Text( - 'SCHEDULE_EXACT_ALARM is denied\n\nAlarms scheduling is not available', - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.titleMedium, - ) - else - Text( - 'SCHEDULE_EXACT_ALARM is granted\n\nAlarms scheduling is available', - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.titleMedium, - ), const SizedBox(height: 32), ElevatedButton( - onPressed: _exactAlarmPermissionStatus.isDenied - ? () async { - await Permission.scheduleExactAlarm - .onGrantedCallback(() => setState(() { - _exactAlarmPermissionStatus = - PermissionStatus.granted; - })) - .request(); - } - : null, + onPressed: _exactAlarmPermissionStatus.isDenied ? _requestPermission : null, child: const Text('Request exact alarm permission'), ), const SizedBox(height: 32), ElevatedButton( - onPressed: _exactAlarmPermissionStatus.isGranted - ? () async { - await AndroidAlarmManager.oneShot( - const Duration(seconds: 5), - // Ensure we have a unique alarm ID. - Random().nextInt(pow(2, 31) as int), - callback, - exact: true, - wakeup: true, - ); - } - : null, + onPressed: _exactAlarmPermissionStatus.isGranted ? _scheduleAlarm : null, child: const Text('Schedule OneShot Alarm'), ), const Spacer(),