Skip to main content

Native api call flutter in terminated state

Calling Native Android Code from Flutter (Step-by-Step for Beginners)

When you need to run platform-specific features (like background services, Bluetooth, or sensors) that Flutter doesn’t handle out of the box, you can use MethodChannels to talk to native Android or iOS code.

In this tutorial, we’ll:

  1. Create a MethodChannel in Flutter.

  2. Set up a foreground service in Android.

  3. Trigger that service from Flutter—even when the app is closed.


1. What Is a MethodChannel?

Think of a MethodChannel as a telephone line between Flutter (Dart) and the platform’s native code (Java/Kotlin for Android, Swift/Objective-C for iOS).

  • Flutter side: “📞 Hey Android, please start the service!”

  • Android side: “✅ Got it, I’m starting it now.”


2. Create the Channel in Flutter

Create a new file: lib/native_service.dart

import 'package:flutter/services.dart';

class NativeService {
  // ✅ A generic channel name you can reuse in any project
  static const _channel = MethodChannel('com.example/native_service');

  /// Start the Android foreground service
  static Future<void> startService({
    required String url,
    required String token,
    bool isOnline = true,
  }) async {
    try {
      await _channel.invokeMethod('startService', {
        'url': url,
        'token': token,
        'isOnline': isOnline,
      });
    } catch (e) {
      print('❌ Failed to start service: $e');
    }
  }
}

Key points:

  • MethodChannel('com.example/native_service'): Use a simple, descriptive name.

  • invokeMethod('startService'): Sends a message to Android.


3. Call It from Your First Screen

In your main screen’s initState, trigger the service as soon as the app starts:


@override
void initState() {
  super.initState();
  NativeService.startService(
    url: 'https://your-api-endpoint.com/status',
    token: 'your-auth-token',
  );
}

4. Listen for Calls on Android

Open android/app/src/main/kotlin/.../MainActivity.kt and configure the MethodChannel:


import android.content.Intent
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
    private val CHANNEL = "com.example/native_service"

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

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
            .setMethodCallHandler { call, result ->
                when (call.method) {
                    "startService" -> {
                        val url = call.argument<String>("url") ?: ""
                        val token = call.argument<String>("token") ?: ""
                        val isOnline = call.argument<Boolean>("isOnline") ?: false

                        val intent = Intent(this, MyService::class.java).apply {
                            putExtra("url", url)
                            putExtra("token", token)
                            putExtra("isOnline", isOnline)
                        }

                        // ✅ Use foreground service so it keeps running
                        startForegroundService(intent)
                        result.success("Service started: $url")
                    }
                    else -> result.notImplemented()
                }
            }
    }
}


5. Build the Foreground Service

Create MyService.kt in the same package:

import android.app.*
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import org.json.JSONObject

class MyService : Service() {

    private var url = ""
    private var token = ""
    private val CHANNEL_ID = "MyServiceChannel"
    private val NOTIFICATION_ID = 1

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

    override fun onCreate() {
        super.onCreate()
        createNotificationChannel()
        startForeground(NOTIFICATION_ID, createNotification())
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        url = intent?.getStringExtra("url") ?: ""
        token = intent?.getStringExtra("token") ?: ""
        return START_STICKY
    }

    override fun onTaskRemoved(rootIntent: Intent?) {
        super.onTaskRemoved(rootIntent)

        if (url.isEmpty() || token.isEmpty()) {
            Log.e("MyService", "❌ Missing URL or Token")
            stopSelf()
            return
        }

        // 🔗 Send a POST request when app is removed
        val queue = Volley.newRequestQueue(this)
        val jsonBody = JSONObject().apply { put("isOnline", false) }

        val request = object : StringRequest(
            Method.POST, url,
            { response ->
                Log.d("MyService", "✅ Response: $response")
                stopForeground(true); stopSelf()
            },
            { error ->
                Log.e("MyService", "❌ Error: ${error.message}", error)
                stopForeground(true); stopSelf()
            }
        ) {
            override fun getBody() = jsonBody.toString().toByteArray(Charsets.UTF_8)
            override fun getBodyContentType() = "application/json; charset=utf-8"
            override fun getHeaders() = hashMapOf(
                "Content-Type" to "application/json",
                "Authorization" to "Bearer $token"
            )
        }

        queue.add(request)
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                CHANNEL_ID, "My Service", NotificationManager.IMPORTANCE_LOW
            )
            getSystemService(NotificationManager::class.java).createNotificationChannel(channel)
        }
    }

    private fun createNotification(): Notification =
        NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("Status Service")
            .setContentText("Running in background…")
            .setSmallIcon(android.R.drawable.ic_dialog_info)
            .build()
}

6. Update Android Manifest

Inside <application> in AndroidManifest.xml:

<service
    android:name=".MyService"
    android:enabled="true"
    android:exported="false"
    android:foregroundServiceType="dataSync" />

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />

How It All Works

  1. Flutter sends a message through the MethodChannel.

  2. Android (MainActivity) receives it and starts MyService.

  3. MyService becomes a foreground service, ensuring it runs even when your app is closed or swiped away.

  4. The service performs a task (like sending a network request) and then stops itself.


Takeaways for Beginners

  • MethodChannel is the bridge between Flutter and native code.

  • Always use startForegroundService for tasks that need to survive app termination.

  • Keep the channel name generic (com.example/native_service) so it can be reused.

  • Foreground services must show a notification—this is an Android requirement.

Comments

Popular posts from this blog

Referral & Deep linking

  Plugins :  app_links: android_play_install_referrer: Link to share :  https://play.google.com/store/apps/details?id=com.erer&referrer=referral_code%3DTEST123 Flutter Code :  import 'package:app_links/app_links.dart' ; import 'package:android_play_install_referrer/android_play_install_referrer.dart' ; import 'package:flutter/foundation.dart' ; import 'package:shared_preferences/shared_preferences.dart' ; class DeepLinkService {   static const String _referralKey = 'referral_code' ;   static final AppLinks _appLinks = AppLinks ();   static Future < void > initDeepLinks () async {     debugPrint ( 'Initializing deep links...' );     // 1️⃣ Handle real-time deep/link stream     try {       final Uri ? initialUri = await _appLinks . getInitialAppLink ();       if ( initialUri != null ) {         debugPrint ( 'Initial deep link: $initialUri'...

API security best practices

 API with a focus on security best practices : Key Security Practices Included: Input Validation and Sanitization : All inputs are validated and sanitized to prevent SQL injection and other attacks. Prepared Statements : All database queries use prepared statements to avoid SQL injection. Password Hashing : Passwords are hashed using password_hash() and verified using password_verify() . Token-Based Authentication : JSON Web Tokens (JWTs) are used for secure API authentication. Error Hiding : Error details are logged but not exposed to users in production. Strict Content-Type Header : Ensures only JSON payloads are processed. Rate Limiting and Throttling : Optional mechanisms to prevent abuse. Validation for IDs : Integer inputs (like user_id or exam_id ) are explicitly validated.