How to open Flutter app from foreground mode?

I am making an alarm application with Flutter, I coded a foreground service with PlatformChannel and MethodChannel. When countdown finishes I want to show an Alarm page but I can’t deal with it. The problem is When alarm finishes android shows a notification but couldn’t open the actual dart page, when I tap the notification firstly shows a white page after shows my apps home page. On the other hand notification can deletable but when alarm ticks notification comeback.

Flutter side code :

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:gap/gap.dart';
import 'package:get/get.dart';
import 'package:kirinti/controllers/kirintila_controller.dart';
import 'package:kirinti/controllers/timer_controller.dart';

class CountDownScreen extends StatefulWidget {
  const CountDownScreen({Key? key}) : super(key: key);

  @override
  State<CountDownScreen> createState() => _CountDownScreenState();
}

class _CountDownScreenState extends State<CountDownScreen> {
  static const MethodChannel _channel = MethodChannel("KirintilaChannel");
  static const EventChannel _eventChannel =
      EventChannel("KirintilaEventChannel");
  StreamSubscription<dynamic>? _timerSubscription;

  final KirintilaController kirintilaController = Get.find();
  final TimerController timerController = Get.find();

  Future<void> _startKirintila() async {
    debugPrint("Servis başlatılmaya çalışılıyor");
    if (kirintilaController.kirintiModel?.duration != null) {
      try {
        await _channel.invokeMethod("StartKirintila", {
          "duration": kirintilaController.kirintiModel!.duration,
        });
        debugPrint("Servis başlatma çağrısı yapıldı");
      } on PlatformException catch (e) {
        debugPrint("Servis başlatma hatası: ${e.message}");
      }
    } else {
      debugPrint("Kirintila duration boş");
    }
  }

  Future<void> _stopKirintila() async {
    debugPrint("Servis durdurulmaya çalışılıyor");
    try {
      await _channel.invokeMethod('StopKirintila');
      Get.back();
      debugPrint("Servis durduruldu");
    } on PlatformException catch (e) {
      debugPrint("Servis durdurma hatası: ${e.message}");
    }
  }

  void _listenToTimerUpdates() {
    _timerSubscription = _eventChannel.receiveBroadcastStream().listen(
      (event) {
        if (event is Map) {
          setState(() {
            timerController.remainingMinutes.value = event['minutes'] as int;
            timerController.remainingSeconds.value = event['seconds'] as int;
          });
        }
      },
      onError: (error) {
        debugPrint("Timer update error: $error");
      },
    );
  }

  @override
  void initState() {
    super.initState();
    _startKirintila();
    _listenToTimerUpdates();
  }

  @override
  void dispose() {
    _timerSubscription?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey.shade100,
      appBar: AppBar(
        backgroundColor: Colors.grey.shade100,
        automaticallyImplyLeading: false,
        title: const Text("Geri Sayım", style: TextStyle(color: Colors.black)),
        centerTitle: true,
      ),
      body: SafeArea(
        child: Center(
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 20),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Obx(() {
                  int hours = timerController.remainingMinutes.value ~/ 60;
                  int minutes = timerController.remainingMinutes.value % 60;
                  int seconds = timerController.remainingSeconds.value;
                  return Text(
                    "${hours.toString().padLeft(2, '0')}:"
                    "${minutes.toString().padLeft(2, '0')}:"
                    "${seconds.toString().padLeft(2, '0')}",
                    style: const TextStyle(
                        fontSize: 64, fontWeight: FontWeight.bold),
                  );
                }),
                const Gap(50),
                InfoWidget(),
                const Gap(30),
                SizedBox(
                  width: double.infinity,
                  child: ElevatedButton(
                    onPressed: _stopKirintila,
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.green,
                      padding: const EdgeInsets.symmetric(vertical: 15),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(10),
                      ),
                    ),
                    child: const Text(
                      "Güvendeyim",
                      style: TextStyle(
                          fontSize: 18,
                          fontWeight: FontWeight.bold,
                          color: Colors.white),
                    ),
                  ),
                ),
                const Gap(10),
                SizedBox(
                  width: double.infinity,
                  child: ElevatedButton(
                    onPressed: _stopKirintila,
                    style: ElevatedButton.styleFrom(
                      padding: const EdgeInsets.symmetric(vertical: 15),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(10),
                      ),
                    ),
                    child: const Text(
                      "İptal Et",
                      style: TextStyle(
                          fontSize: 18,
                          fontWeight: FontWeight.bold,
                          color: Colors.white),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget InfoWidget() {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(20),
        boxShadow: [
          BoxShadow(
            color: Colors.grey.withOpacity(0.1),
            spreadRadius: 1,
            blurRadius: 5,
            offset: const Offset(0, 3),
          ),
        ],
      ),
      child: Obx(() => Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                kirintilaController.kirintiModel!.activity,
                style:
                    const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
              const Gap(8),
              Text(
                "Acil durum kişisi: ${kirintilaController.kirintiModel?.person?.fullName}",
                style: const TextStyle(fontSize: 16),
              ),
              const Gap(4),
              Text(
                "Ayarlanan süre: ${kirintilaController.kirintiModel?.duration} dakika",
                style: const TextStyle(fontSize: 16),
              ),
            ],
          )),
    );
  }
}

MainActivity.kt :

package com.example.kirinti

import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.media.RingtoneManager
import android.os.Build
import android.os.Bundle
import android.os.PowerManager
import android.util.Log
import android.view.WindowManager
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodChannel

class MainActivity : FlutterActivity() {
    companion object {
        private const val CHANNEL_NAME = "KirintilaChannel"
        private const val EVENT_CHANNEL_NAME = "KirintilaEventChannel"
        private const val TAG = "MainActivity"
        private const val NOTIFICATION_PERMISSION_CODE = 1
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d(TAG, "onCreate called")
        checkNotificationPermission()
        handleIntent(intent)
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        Log.d(TAG, "onNewIntent called")
        handleIntent(intent)
    }

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_NAME).setMethodCallHandler { call, result ->
            when (call.method) {
                "StartKirintila" -> {
                    val duration = call.argument<Int>("duration")
                    if (duration != null) {
                        startKirintilaService(duration)
                        result.success(null)
                    } else {
                        result.error("INVALID_ARGUMENT", "Duration is null", null)
                    }
                }
                "StopKirintila" -> {
                    stopKirintilaService()
                    result.success(null)
                }
                "stopAlarm" -> {
                    KirintilaService.stopTimer()
                    result.success(null)
                }
                else -> result.notImplemented()
            }
        }

        EventChannel(flutterEngine.dartExecutor.binaryMessenger, EVENT_CHANNEL_NAME).setStreamHandler(
            object : EventChannel.StreamHandler {
                override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
                    KirintilaService.setEventSink(events)
                }

                override fun onCancel(arguments: Any?) {
                    KirintilaService.setEventSink(null)
                }
            }
        )
    }

    private fun checkNotificationPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.POST_NOTIFICATIONS), NOTIFICATION_PERMISSION_CODE)
            }
        }
    }

    private fun startKirintilaService(duration: Int) {
        val serviceIntent = Intent(this, KirintilaService::class.java).apply {
            putExtra("duration", duration)
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(serviceIntent)
        } else {
            startService(serviceIntent)
        }
        Log.d(TAG, "Kirintila service started with duration: $duration minutes")
    }

    private fun stopKirintilaService() {
        val serviceIntent = Intent(this, KirintilaService::class.java)
        stopService(serviceIntent)
        KirintilaService.stopTimer()
        Log.d(TAG, "Kirintila service stopped")
    }

    private fun handleIntent(intent: Intent?) {
        Log.d(TAG, "handleIntent called with action: ${intent?.action}")
        when (intent?.action) {
            "ALARM_TRIGGERED" -> handleAlarmTriggered()
            "SAFE_ACTION" -> handleSafeAction()
            "HELP_ACTION" -> handleHelpAction()
        }
    }

    private fun handleAlarmTriggered() {
        Log.d(TAG, "Alarm triggered, opening app")

        // Ekranı aç
        val pm = getSystemService(Context.POWER_SERVICE) as PowerManager
        val wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP, "MyApp:WakeLock")
        wakeLock.acquire(10*60*1000L /*10 minutes*/)

        // Alarm sesi çal
        val notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
        val r = RingtoneManager.getRingtone(applicationContext, notification)
        r.play()

        // Kilit ekranını kaldır
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
            setShowWhenLocked(true)
            setTurnScreenOn(true)
            val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as android.app.KeyguardManager
            keyguardManager.requestDismissKeyguard(this, null)
        } else {
            window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
                    WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
                    WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
                    WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
        }

        wakeLock.release()
    }

    private fun handleSafeAction() {
        Log.d(TAG, "User is safe")
        stopKirintilaService()
    }

    private fun handleHelpAction() {
        Log.d(TAG, "User needs help")
        // Yardım İste butonuna basıldığında yapılacak işlemler
        // Örneğin: Acil durum kontaklarına mesaj gönderme, yardım çağrısı başlatma, vs.
    }
} 

KirintilaService.kt :


import android.app.*
import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo
import android.graphics.Color
import android.media.RingtoneManager
import android.os.Build
import android.os.CountDownTimer
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
import io.flutter.plugin.common.EventChannel

class KirintilaService : Service() {
    companion object {
        private const val CHANNEL_ID = "KirintilaChannel"
        private const val NOTIFICATION_ID = 1
        private const val URGENT_NOTIFICATION_ID = 2
        private const val TAG = "KirintilaService"
        private var eventSink: EventChannel.EventSink? = null
        private var timer: CountDownTimer? = null

        fun setEventSink(sink: EventChannel.EventSink?) {
            eventSink = sink
        }

        fun stopTimer() {
            timer?.cancel()
            timer = null
        }
    }

    override fun onCreate() {
        super.onCreate()
        createNotificationChannel()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val minutes = intent?.getIntExtra("duration", 1) ?: 1
        val millisInFuture = minutes * 60 * 1000L
        val notification = createNotification("Kirintila Başladı")

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
            startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
        } else {
            startForeground(NOTIFICATION_ID, notification)
        }

        startCountDownTimer(millisInFuture)

        return START_STICKY
    }

    private fun startCountDownTimer(millisInFuture: Long) {
        timer?.cancel()
        timer = object : CountDownTimer(millisInFuture, 1000) {
            override fun onTick(millisUntilFinished: Long) {
                val remainingMinutes = millisUntilFinished / 60000
                val remainingSeconds = (millisUntilFinished % 60000) / 1000
                updateNotification("Kalan süre: $remainingMinutes dakika $remainingSeconds saniye")

                eventSink?.success(mapOf("minutes" to remainingMinutes, "seconds" to remainingSeconds))
                Log.d(TAG, "Timer tick: $remainingMinutes:$remainingSeconds")
            }

            override fun onFinish() {
                Log.d(TAG, "Timer finished")
                showUrgentNotification()
                stopSelf()
            }
        }.start()
    }

    private fun showUrgentNotification() {
        Log.d(TAG, "Showing urgent notification")

        val intent = Intent(this, MainActivity::class.java).apply {
            action = "ALARM_TRIGGERED"
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        }
        val pendingIntent = PendingIntent.getActivity(this, 100, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)

        val notification = NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("KIRINTI - Acil Durum")
            .setContentText("Alarm Süresi bitti")
            .setSmallIcon(android.R.drawable.ic_dialog_alert)
            .setPriority(NotificationCompat.PRIORITY_MAX)
            .setCategory(NotificationCompat.CATEGORY_ALARM)
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
            .setContentIntent(pendingIntent)
            .setFullScreenIntent(pendingIntent, true)
            .setAutoCancel(false)
            .build()

        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(URGENT_NOTIFICATION_ID, notification)

        Log.d(TAG, "Urgent notification has been sent")
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(CHANNEL_ID, "Kirintila Channel", NotificationManager.IMPORTANCE_HIGH).apply {
                description = "Kirintila bildirim kanalı"
                enableLights(true)
                lightColor = Color.RED
                enableVibration(true)
                vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
            }
            val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(channel)
        }
    }

    private fun createNotification(content: String): Notification {
        val intent = Intent(this, MainActivity::class.java)
        val pendingIntent = PendingIntent.getActivity(this, 3, intent, PendingIntent.FLAG_IMMUTABLE)
        return NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("KIRINTI")
            .setContentText(content)
            .setSmallIcon(android.R.drawable.ic_dialog_info)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setOngoing(true)
            .setOnlyAlertOnce(true)
            .setContentIntent(pendingIntent)
            .setAutoCancel(false)
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
            .build()
    }

    private fun updateNotification(content: String) {
        val notification = createNotification(content)
        val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(NOTIFICATION_ID, notification)
    }

    override fun onBind(intent: Intent?): IBinder? = null

    override fun onDestroy() {
        super.onDestroy()
        stopTimer()
        stopForeground(true)
        Log.d(TAG, "Service destroyed")
    }
} ```


Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật