728x90

학습목표


1. Firebase cloud messaging & foreground notification 기능 구현

앱이 실행 중일 때 notification 받도록 구현

 

2. Firebase cloud messaging & background notification 기능 구현

앱이 종료되었을 때 notification 받도록 구현

 

3. 특정 시간에 알람 설정 시, 앱이 실행되고 소리 또는 진동이 울리는 기능 구현

진동 모드, 무음 모드일 때 어떻게 처리하는지 찾아보기

 

회고


1. Firebase cloud messaging & foreground notification 기능 구현

앱이 실행 중일 때 notification 받도록 구현

 

2. Firebase cloud messaging & background notification 기능 구현

앱이 종료되었을 때 notification 받도록 구현

 

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

import 'package:awesome_notifications/awesome_notifications.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:notify/layout/default_layout.dart';
import 'package:notify/main.dart';
import 'package:notify/utils/notification_util.dart';

Future<void> onBackgroundMessage(RemoteMessage message) async {
  // 기존
  // await Firebase.initializeApp();
  // 앱 실행 코드 추가
  await init();
  runApp(const App());

  print('background messgae !!!');
  print(message.messageId);
  if (message.data.containsKey('data')) {
    // Handle data message
    final data = message.data['data'];
    print(data);
  }

  if (message.data.containsKey('notification')) {
    // Handle notification message
    final notification = message.data['notification'];
    print(notification);
  }
  // Or do other work.

  // awesome_notification background handler
  await AwesomeNotifications().createNotification(
      content: NotificationContent(
    id: 1,
    channelKey: 'basic_channel',
    title: message.notification!.title!,
    body: message.notification!.body!,
  ));
}

class FCM {
  final _firebaseMessaging = FirebaseMessaging.instance;

  final streamCtlr = StreamController<String>.broadcast();
  final titleCtlr = StreamController<String>.broadcast();
  final bodyCtlr = StreamController<String>.broadcast();

  setNotifications() {
    FirebaseMessaging.onBackgroundMessage(onBackgroundMessage);

    // handle when app in active state
    forgroundNotification();

    // handle when app running in background state
    backgroundNotification();

    // handle when app completely closed by the user
    terminateNotification();

    // With this token you can test it easily on your phone
    final token =
        _firebaseMessaging.getToken().then((value) => print('Token: $value'));
  }

  forgroundNotification() {
    FirebaseMessaging.onMessage.listen(
      (message) async {
        print('foregroundmessage');
        print(message);

        if (message.data.containsKey('data')) {
          // Handle data message
          streamCtlr.sink.add(message.data['data']);
        }
        if (message.data.containsKey('notification')) {
          // Handle notification message
          streamCtlr.sink.add(message.data['notification']);
        }
        // Or do other work.
        titleCtlr.sink.add(message.notification!.title!);
        bodyCtlr.sink.add(message.notification!.body!);

        // awesome_notifications
        // await AwesomeNotifications().createNotification(
        //     content: NotificationContent(
        //         id: 1,
        //         channelKey: 'basic_channel',
        //         title: message.notification!.title!,
        //         body: message.notification!.body!));
      },
    );
  }

  backgroundNotification() {
    FirebaseMessaging.onMessageOpenedApp.listen(
      (message) async {
        print('backgroundmessage');
        print(message);
        if (message.data.containsKey('data')) {
          // Handle data message
          streamCtlr.sink.add(message.data['data']);
        }
        if (message.data.containsKey('notification')) {
          // Handle notification message
          streamCtlr.sink.add(message.data['notification']);
        }
        // Or do other work.
        titleCtlr.sink.add(message.notification!.title!);
        bodyCtlr.sink.add(message.notification!.body!);
      },
    );
  }

  terminateNotification() async {
    RemoteMessage? initialMessage =
        await FirebaseMessaging.instance.getInitialMessage();
    print('terminatemessage');
    print(initialMessage);
    if (initialMessage != null) {
      if (initialMessage.data.containsKey('data')) {
        // Handle data message
        streamCtlr.sink.add(initialMessage.data['data']);
      }
      if (initialMessage.data.containsKey('notification')) {
        // Handle notification message
        streamCtlr.sink.add(initialMessage.data['notification']);
      }
      // Or do other work.
      titleCtlr.sink.add(initialMessage.notification!.title!);
      bodyCtlr.sink.add(initialMessage.notification!.body!);
    }
  }

  dispose() {
    streamCtlr.close();
    bodyCtlr.close();
    titleCtlr.close();
  }
}

 

알람 소리 재생 페이지

// import 'package:firebase_database/firebase_database.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:notify/addalarm/add_alarm.dart';
import 'package:notify/alarmlog/alarm_log.dart';
import 'package:notify/alarmsettings/alarm_settings.dart';
import 'package:notify/layout/default_layout.dart';
import 'package:notify/routinealarm/routine_alarm.dart';
import 'package:flutter/material.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';

import 'package:notify/constraints.dart';
import 'package:notify/myalarm/my_alarm.dart';
import 'package:notify/sharealarm/share_alarm.dart';
import 'package:get/get.dart';

class AlarmSoundPage extends StatefulWidget {
  final AudioPlayer player;

  const AlarmSoundPage({
    Key? key,
    required AudioPlayer this.player,
  }) : super(key: key);

  @override
  _AlarmSoundPageState createState() => _AlarmSoundPageState();
}

class _AlarmSoundPageState extends State<AlarmSoundPage> {
  int currentIndex = 0;

  void changeCurrentIndex(int selectedIndex) {
    setState(() {
      currentIndex = selectedIndex;
    });
  }

  @override
  Widget build(BuildContext context) {
    double width = MediaQuery.of(context).size.width;
    double height = MediaQuery.of(context).size.height;

    return Scaffold(
      backgroundColor: kBackgroundColor,
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(10.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              SizedBox(
                height: height * 0.1,
              ),
              Text(
                '1월 11일 화요일',
                style: TextStyle(
                  color: ktextColor,
                ),
              ),
              SizedBox(
                height: height * 0.05,
              ),
              Text(
                '3:55',
                style: TextStyle(
                  color: ktextColor,
                  fontSize: 30,
                ),
              ),
              Spacer(),
              Row(
                children: [
                  Expanded(
                    child: MaterialButton(
                      height: 50,

                      clipBehavior: Clip.antiAlias, // Add This
                      elevation: 0,
                      textColor: ktextColor,
                      color: kRedColor,
                      onPressed: () {
                        // _dateTime 시간에 알람 울리도록 추가
                        widget.player.stop();
                        Get.off(DefaultLayout());
                      },
                      child: const Text(
                        '종료하기',
                        style: TextStyle(fontSize: 16),
                      ),
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

 

 

 

3. 특정 시간에 알람 설정 시, 앱이 실행되고 소리 또는 진동이 울리는 기능 구현

진동 모드, 무음 모드일 때 어떻게 처리하는지 찾아보기

 

 return SafeArea(
      child: Scaffold(
          body: ListView(
        padding: EdgeInsets.symmetric(horizontal: 15, vertical: 8),
        children: <Widget>[
          /* ******************************************************************** */
          /* notification test */
          TextDivisor(title: 'FCM Foreground\n알림 제목/텍스트'),

          Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                SizedBox(
                  height: 20,
                ),
                Text(
                  'Foreground 알림 제목:- $notificationTitle',
                  style: Theme.of(context).textTheme.headline6,
                ),
                Text(
                  'Foreground 알림 텍스트:- $notificationBody',
                  style: Theme.of(context).textTheme.headline6,
                ),
                // MaterialButton(
                //   onPressed: _showNotificationWithNoTitle,
                //   child: Text("noti"),
                // ),
                // 알람 추가
                TextDivisor(title: '알람 추가하기'),
                hourMinute12HCustomStyle(),
                Container(
                  margin: EdgeInsets.symmetric(vertical: 100),
                  child: new Text(
                    _alarmTime.hour.toString().padLeft(2, '0') +
                        ':' +
                        _alarmTime.minute.toString().padLeft(2, '0') +
                        ':' +
                        _alarmTime.second.toString().padLeft(2, '0'),
                    style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
                  ),
                ),
                Row(
                  children: [
                    Expanded(
                      child: MaterialButton(
                        height: 50,
                        shape: const RoundedRectangleBorder(
                          borderRadius: BorderRadius.all(Radius.circular(0)),
                          side: BorderSide(
                            color: Color(0xffdddddd),
                          ),
                        ),
                        clipBehavior: Clip.antiAlias, // Add This
                        elevation: 0,
                        textColor: Colors.white,
                        color: Colors.red,
                        onPressed: () {
                          // _dateTime 시간에 알람 울리도록 추가
                          Duration diff = _alarmTime.difference(DateTime.now());
                          int second = diff.inSeconds;
                          print("second : $second");
                          NotificationUtils.scheduleNotificationWithWakeUp(
                              27, (second));
                        },
                        child: const Text(
                          '추가하기',
                          style: TextStyle(fontSize: 16),
                        ),
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
          /* notification test */

 

 

  static Future<void> scheduleNotificationWithWakeUp(
      int id, int seconds) async {
    Timer(
      Duration(seconds: seconds),
      // Ensure we have a unique alarm ID.
      () async {
        print('alarm excuted');
        AudioCache cache = new AudioCache();
        AudioPlayer player = new AudioPlayer();
        String alarmAudioPath = "sounds/missyoulove.mp3";
        player = await cache.loop(alarmAudioPath);
        Get.to(AlarmSoundPage(player: player));
      },
    );


    Get.to(const DefaultLayout());
  }

추후에 FCM 개념을 적용하여 푸시로 처리하는 게 좋을 지 로컬에서 AndroidAlarmManager를 사용하는 게 좋을 지 고민해봐야겠다.

 

https://youtu.be/AsHqRnKf9cI

 

+ Recent posts