Dart (Flutter)

[Flutter] How to Send Notifications on Flutter: Using flutter_local_notification

llHoYall 2023. 4. 21. 23:41

이번에는 iOSAndroid에서 알림을 보내는 방법에 대해 살펴보겠습니다.

이를 위해 flutter_local_notification이라는 플러그인을 사용하겠습니다.

Creating Project

해당 내용에만 집중하기 위해, 기본 example을 사용하겠습니다.

$ flutter create example

Setting Plugin

pubspec.yaml에 다음과 같이 사용할 플러그인을 명시해 줍니다.

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.2
  flutter_local_notifications: ^13.0.0

이제 프로젝트에 적용을 해줍니다.

$ flutter pub get

Setting iOS

ios/Runner/AppDelegate.swift 파일을 열고 다음과 같이 수정합니다.

import UIKit
import Flutter
import flutter_local_notifications

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    FlutterLocalNotificationsPlugin.setPluginRegistrantCallback { (registry) in
      GeneratedPluginRegistrant.register(with: registry)
    }
    GeneratedPluginRegistrant.register(with: self)
    if #available(iOS 10.0, *) {
      UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    }
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

 

Setting Android

android/app/build.gradle 파일을 열고 하기의 내용을 찾아 수정해 줍니다.

android {
    compileSdkVersion 33
    ndkVersion flutter.ndkVersion

    defaultConfig {
        ...
        multiDexEnabled true
    }
}

Implementing Notification Service

기본 앱에다 바로 구현을 할 수도 있지만, 개인적인 취향으로 분리를 하는 것을 선호하기에 이번 포스팅에서도 그렇게 진행해 보겠습니다.

lib/service/notification_service.dart 파일을 생성해 주세요.

먼저, 서비스 클래스의 뼈대를 만들어 줍니다.

import 'package:flutter_local_notifications/flutter_local_notifications.dart';

class NotificationService {
  final FlutterLocalNotificationsPlugin _notificationsPlugin =
      FlutterLocalNotificationsPlugin();
}

플러그인 객체를 클래스 변수로 선언을 해줬습니다.

 

다음으로 초기화 method를 구현합니다.

Future<void> initNotification() async {
  _notificationsPlugin
      .resolvePlatformSpecificImplementation<
          AndroidFlutterLocalNotificationsPlugin>()
      ?.requestPermission();

  AndroidInitializationSettings initializationSettingsAndroid =
      const AndroidInitializationSettings('@mipmap/ic_launcher');

  DarwinInitializationSettings initializationSettingsIOS =
      DarwinInitializationSettings(
    requestAlertPermission: true,
    requestBadgePermission: true,
    requestSoundPermission: true,
    onDidReceiveLocalNotification:
        (int id, String? title, String? body, String? payload) async {},
  );

  InitializationSettings initializationSettings = InitializationSettings(
    android: initializationSettingsAndroid,
    iOS: initializationSettingsIOS,
  );

  await _notificationsPlugin.initialize(
    initializationSettings,
    onDidReceiveNotificationResponse:
        (NotificationResponse notificationResponse) async {},
  );
}

안드로이드와 iOS의 초기화 설정을 만들어 이를 사용하여 초기화를 해 주었습니다.

안드로이드의 경우, 권한 요청 부분을 별도로 해줘야 합니다.

또한, 아이콘 설정을 해줄 수 있는데 알람에 임의의 아이콘을 사용하고 싶으면 android/app/src/main/res/drawable에 넣어주신 후, 아이콘 이름을 AndroidInitializationSettings()에 넣어주시면 됩니다.

 

만든 초기화 method를 App에서 호출합니다.

import 'package:example/service/notification_service.dart';

class _MyHomePageState extends State<MyHomePage> {
  ...
  @override
  void initState() {
    super.initState();

    NotificationService().initNotification();
  }
  ...
}

어차피 한 번만 초기화 되면 되는 것이라 main()에서 바로 호출해줘도 괜찮지만, context 객체를 사용하게 되는 경우도 있어서 나중을 위해 lifecycle method안에서 호출을 하도록 했습니다.

 

이제, 알람을 보여주는 method를 구현하겠습니다.

Future<void> showNotification({
  int id = 0,
  String? title,
  String? body,
}) async {
  NotificationDetails notificationDetails = const NotificationDetails(
    android: AndroidNotificationDetails(
      'channelId',
      'channelName',
      priority: Priority.high,
      importance: Importance.max,
      channelShowBadge: true,
    ),
    iOS: DarwinNotificationDetails(),
  );

  return _notificationsPlugin.show(id, title, body, notificationDetails);
}

iOS에 비해, 안드로이드는 설정이 많죠? ㅎㅎ

보면 쉽게 이해할 수 있는 것들이라 설명은 따로 필요없을 것 같아요.

 

마지막으로 floatingActionButton을 눌렀을 때, 알림이 오도록 해보겠습니다.

floatingActionButton: FloatingActionButton(
  onPressed: () {
    _incrementCounter();
    NotificationService().showNotification(
      title: 'My Alarm',
      body: 'You touched $_counter times.',
    );
  },
  tooltip: 'Increment',
  child: const Icon(Icons.add),
),

Testing

먼저, ios에서 실행을 해보겠습니다.

알림 권한을 요청을 하고, 허용을 하면 버튼을 누를 때 마다 알람이 옵니다.

다음은 안드로이드에서 실행해 보겠습니다.

마찬가지로, 권한을 주면 알람이 잘 오는 것을 확인할 수 있습니다.

안드로이드의 경우 아이콘에 제약 조건이 있는지 기본 플러터 아이콘을 다른 아이콘으로 변경할 경우, 잘 동작하지 않더라고요.

이 부분은 명시된 곳이 따로 없어서 확인해보지 못했습니다.

Wrap Up

이번 포스팅에서는 플러터 앱에서 알람을 보내는 방법에 대해 살펴보았습니다.

타이머를 사용하여 테스트해본 결과 기본적으로 앱이 동작하고 있는 상태에서만 알람을 보낼 수 있고, 앱을 완전히 종료한 상태에서는 알람을 보낼 수 없습니다.

안드로이드의 경우 설정할 것들이 좀 더 많아 복잡하지만, 그만큼 세세하게 컨트롤 할 수 있습니다.

다만, 아이콘의 경우처럼 뭔가의 제약으로 잘 동작하지 않는 부분도 있더라고요.

현재, 14.0.0 버전이 개발 중인데 크게 달라지는 부분은 없는 느낌입니다.