🔌 Integrating Native Android Features in Flutter – My Real Project Example
✨ “Flutter is cross-platform, but what if you need something that only Android can do?”
That’s the exact question I faced in one of my recent Flutter projects. Our app needed to monitor battery level and trigger actions when the device was charging — something that’s not directly available via Flutter packages.
So, I rolled up my sleeves and integrated native Android code using platform channels. If you're wondering how to bridge that gap between Flutter and native Android, here’s how I did it — step-by-step.
🚧 The Challenge
Flutter is great, but some device-level features (like battery info, sensors, or background services) still require native code.
We needed to:
There were some packages, but they didn’t cover all our needs — and we wanted more control and efficiency.
🧠 The Solution: Platform Channels
Flutter provides a powerful way to communicate between Dart and native code through platform channels.
📱 Android Native Code (Kotlin)
Step 1: Create a MethodChannel in MainActivity
class MainActivity: FlutterActivity() {
private val CHANNEL = "battery_channel"
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
if (call.method == "getBatteryLevel") {
val batteryLevel = getBatteryLevel()
if (batteryLevel != -1) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery level not available.", null)
}
} else {
result.notImplemented()
}
}
}
private fun getBatteryLevel(): Int {
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
}
}
💻 Flutter (Dart) Side
Step 2: Invoke Native Code from Dart
import 'package:flutter/services.dart';
class BatteryService {
static const platform = MethodChannel('battery_channel');
Future<int?> getBatteryLevel() async {
try {
final batteryLevel = await platform.invokeMethod<int>('getBatteryLevel');
return batteryLevel;
} catch (e) {
print("Failed to get battery level: $e");
return null;
}
}
}
✅ How I Used It in UI
final batteryService = BatteryService();
ElevatedButton(
onPressed: () async {
final level = await batteryService.getBatteryLevel();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Battery Level: $level%")),
);
},
child: Text("Check Battery"),
)
🧪 Bonus: Testing & Safety
🎯 Takeaway
Flutter doesn’t limit you — it empowers you to tap into native layers when needed. Whether it’s camera, background tasks, sensors, or deep device features, platform channels let you have the best of both worlds.
If you’re building a real-world app, don’t hesitate to dive into native when Flutter packages fall short. It’s not as scary as it seems!
🔁 Over to you: Have you integrated native code in your Flutter project? Would you like me to share the iOS version too?
Follow me for more real-world Flutter experiences! 🚀