- 모바일 애플리케이션 내에서 기기 자체적으로 생성 및 표시되는 알림을 의미한다.
- 서버나 외부 서비스 없이 앱 내부에서 알림을 직접 스케줄링하고 관리할 수 있는 것이 특징이다.
flutter_local_notifications
- Android / iOS 모두 지원
Android
AndroidManifest.xml에 권한을 추가한다.- 알림 아이콘을 설정한다.
- 중요도, 소리, 진동 패턴 등 알림 채널을 생성한다.
iOS
AppDelegate.swift또는main.dart내에서 알림 권한 요청 코드를 추가한다.- 배너 • 소리 • 배지 표시 권한을 명시한다.
FlutterLocalNotificationPlugin인스턴스를 생성한다.initialize()메서드로 초기화한다.- 플랫폼별 설정을 적용한다.
show
- 즉시 알림 표시
schedule()
- 특정 시간에 알림 예약
PeriodicallyShow()
- 주기적으로 반복 알림 표시
각 알림은 다음 속성을 포함한다.
ID 고유 식별자 제목 title 본문 body payload 데이터 전달용 플랫폼별 세부 설정 Android / iOS 스타일 등
Android
- 중요도, 소리, LED 색상 제어 등 알림 채널을 설정할 수 있다.
BigTextStyle(긴 텍스트 스타일),BigPictureStyle(이미지 포함 알림) 스타일을 지정할 수 있다.
iOS
- 배너 표시 방식, 소리 및 진동 제어, 배지 카운트 조정을 설정할 수 있다.
- 알림 클릭 시 특정 동작 수행 가능하며 초기화 시
onDidReceiveNotificationResponse콜백을 통해 처리한다.Navigator.push사용하여payload데이터로 특정 화면으로 이동한다.- 읽음 처리, 새로고침 등 특정 기능을 실행한다.
timeZone패키지와 함께 사용하여 특정 날짜 • 시간대에 정확하게 알림 예약이 가능하다.
서버 통신이 불필요하다.
- 네트워크 지연 없이 즉시 알림이 가능하다.
개인화가 가능하다.
- 사용자 맞춤 알림 스케줄링이 가능하다.
앱 참여율이 향상된다.
- 이벤트나 리마인드 알림으로 사용자 재참여가 유도된다.
비용이 절감된다.
- 서버 유지비 없이 알림이 운영된다.
- 실무에서는
flutter_local_notifications를 FCM 푸시와 함께 하이브리드로 사용하는 경우가 많다.- 예를 들어 iOS에서 포그라운드 푸시가 표시되지 않을 때 로컬 알림으로 대체 표시하는 식이다.
class LocalNotificationsService { // 싱글톤 패턴 : 앱 전체에서 하나의 인스턴스만 존재하도록 만드는 디자인 패턴 // 알림 서비스는 여러 개 만들 필요가 없고 하나만 있으면 되기 때문에 이렇게 구현 // _internal()은 외부에서 직접 호출할 수 없는 프라이빗 생성자 LocalNotificationsService._internal(); // _instance는 이 클래스의 유일한 인스턴스를 저장하는 변수 static final LocalNotificationsService _instance = LocalNotificationsService._internal(); // instance() 메서드를 통해서만 이 인스턴스에 접근 가능 factory LocalNotificationsService.instance() => _instance; late FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin; // 안드로이드 알림 아이콘 설정 final _androidInitializationSettings = const AndroidInitializationSettings( '@mipmap/ic_launcher', ); // iOS 알림 권한 설정 // iOS에서는 알림을 보내기 전에 사용자에게 권한을 요청해야 함 final _iosInitializationSettings = const DarwinInitializationSettings( requestAlertPermission: true, // 알림 표시 권한 requestBadgePermission: true, // 앱 아이콘 배지 권한 requestSoundPermission: true, // 소리 재생 권한 ); // 안드로이드 알림 채널 정의 final _androidChannel = const AndroidNotificationChannel( 'Knockie', 'Knockie', description: 'Knockie push notification channel', importance: Importance.high, ); // 상태 관리 변수들 // 초기화가 완료되었는지 추적하는 플래그 bool _isFlutterLocalNotificationInitialized = false; // 각 알림에 고유한 ID를 부여하기 위한 카운터 int _notificationIdCounter = 0; // 초기화 메서드는 앱이 시작될 때 한 번만 실행되어야 함 // 이미 초기화했으면 그냥 종료 // onTap 매개변수는 사용자가 알림을 탭했을 때 실행할 함수 받음 Future<void> init({NotificationTapHandler? onTap}) async { if (_isFlutterLocalNotificationInitialized) { return; } // 알림 플러그인 객체 생성 // 이 객체를 통해 모든 알림 기능 사용함 _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); // 안드로이드와 Ios 설정 하나로 묶음 final initializationSettings = InitializationSettings( android: _androidInitializationSettings, iOS: _iosInitializationSettings, ); // 플러그인 초기화 + 알림 탭 이벤트 처리 await _flutterLocalNotificationsPlugin.initialize( initializationSettings, // 사용자가 알림을 탭했을 때 실행되는 함수 onDidReceiveNotificationResponse: (NotificationResponse response) { // 특정 화면 이동시키는 처리 final payload = response.payload; if (payload != null && onTap != null) { // payload (알림과 함께 전달된 데이터)를 JSON으로 파싱 final message = RemoteMessage(data: jsonDecode(payload)); // Firebase 메시징 서비스를 통해 딥 링크로 변환한 후 해당 링크로 이동시킴 final link = FirebaseMessagingService.instance().mapMessageToDeepLink(message); if(link != null) onTap(link); } }, ); // 정의한 알림 채널을 시스템에 등록 await _flutterLocalNotificationsPlugin // 안드로이드 전용 기능에 접근 // 특정 플랫폼의 구현체를 가져오는 메서드 .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin () // 위에서 정의한 채널 생성 // ?.는 null일 수도 있으니 안전하게 호출하겠다는 의미 // iOS에서는 null 반환 ?.createNotificationChannel(_androidChannel); // 초기화 완료 _isFlutterLocalNotificationInitialized = true; } // 실제로 알림을 화면에 표시하는 역할을 함 Future<void> showNotification( String? title, // 제목 String? body, // 본문 String? payload, // 나중에 알림을 탭했을 때 어떤 화면으로 이동할지 결정하는데 사용 ) async { // 안드로이드에서 알림이 어떻게 표시될지 세부 설정 // 어떤 채널을 사용할지, 중요도와 우선순위 어떻게 할지 정함 AndroidNotificationDetails androidDetails = AndroidNotificationDetails( _androidChannel.id, _androidChannel.name, channelDescription: _androidChannel.description, importance: Importance.high, // 알림이 소리와 함께 화면 상단에 팝업으로 나타남 priority: Priority.high, // 알림이 소리와 함께 화면 상단에 팝업으로 나타남 ); // ios 알림 설정 const iosDetails = DarwinNotificationDetails(); // 안드로이드와 ios 설정 하나로 통합 final notificationDetails = NotificationDetails( android: androidDetails, iOS: iosDetails, ); // 알림 표시 // 같은 ID로 보내면 이전 알림이 업데이트되고 다른 ID면 새 알림이 생김 await _flutterLocalNotificationsPlugin.show( // 현재 카운터 값을 사용하고 1 증가 시킴 // 각 알림마다 고유한 ID 보유 _notificationIdCounter++, title, body, notificationDetails, payload: payload, ); } }