Flutter can absolutely talk to Java code—that’s how you access Android native features. The key concept you need is Platform Channels.
I’ll walk you through it step-by-step, from idea → code → common use cases.
Big picture (how Flutter talks to Java)
Flutter (Dart)
↓ Platform Channel
Android (Java)
Flutter sends a message → Java handles it → Java sends a result back.
Method 1: Using Platform Channels (Most Common & Recommended)
Step 1: Create a MethodChannel in Flutter (Dart)
import 'package:flutter/services.dart';
class NativeService {
static const platform = MethodChannel('com.example/native');
static Future<String> getDeviceName() async {
try {
final String result = await platform.invokeMethod('getDeviceName');
return result;
} catch (e) {
return "Failed to get device name";
}
}
}
Usage in UI:
TextButton(
onPressed: () async {
String name = await NativeService.getDeviceName();
print(name);
},
child: Text("Get Device Name"),
)
Step 2: Handle the call in Java (Android side)
Open:
android/app/src/main/java/.../MainActivity.java
package com.example.myapp;
import android.os.Build;
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.example/native";
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(
flutterEngine.getDartExecutor().getBinaryMessenger(),
CHANNEL
).setMethodCallHandler(
(call, result) -> {
if (call.method.equals("getDeviceName")) {
String deviceName = Build.MODEL;
result.success(deviceName);
} else {
result.notImplemented();
}
}
);
}
}
Now Flutter ↔ Java communication works 🎉
Method 2: Passing Data (Arguments) from Flutter to Java
Flutter
await platform.invokeMethod(
'addNumbers',
{"a": 5, "b": 10},
);
Java
if (call.method.equals("addNumbers")) {
int a = call.argument("a");
int b = call.argument("b");
result.success(a + b);
}
Method 3: Calling Java Code Continuously (EventChannel)
Use this when Java sends stream data (battery, sensors, GPS).
EventChannel('com.example/events')
This is useful for:
- Battery level updates
- Sensors
- Network state changes
Method 4: Write a Flutter Plugin (Clean Architecture)
If:
- You reuse Java code in many apps
- You want clean separation
- You’re building production-grade features
Create plugin:
flutter create --template=plugin my_native_plugin
This is how big plugins like camera, firebase, etc. are built.
When should you use Java with Flutter?
✅ Use Java when:
- You need Android-only APIs
- Flutter plugin doesn’t exist
- Performance-critical native code
- Existing Java SDK integration
❌ Avoid when:
- Pure UI logic
- Can be done in Dart easily
Common Real-World Examples
- Payment SDK (Razorpay, Paytm)
- Biometric authentication
- SIM / device info
- Background services
- Bluetooth / NFC
Quick comparison
| Task | Best Approach |
|---|---|
| Call Java once | MethodChannel |
| Stream data | EventChannel |
| Reusable native feature | Flutter Plugin |
