📱 Pocket Mode in Flutter – Proximity Sensor Integration (Android)
Use your phone’s proximity sensor in Flutter to detect whether the device is near your pocket, face, or another object. This project demonstrates how to communicate between Flutter and native Android using MethodChannel
and control screen behavior using Android’s PROXIMITY_SCREEN_OFF_WAKE_LOCK
.
✅ Real-world use cases: screen auto-lock in pockets, ambient interaction, call behavior, gesture control.
🔥 Features
- ✅ Detect proximity sensor state (near/far)
- 🔁 Real-time updates from Android to Flutter
- 🌙 Automatically manage screen wake state
- 🔗 Uses
MethodChannel
for Flutter ↔ Android communication - 📦 Clean, modular code structure
🏗️ Project Structure
lib/ ├── services/ │ └── proximity_service.dart # Flutter bridge to native Android ├── views/ │ └── home_screen.dart # UI to observe proximity state └── main.dart # Entry point with lifecycle handling
android/ └── MainActivity.kt # Kotlin sensor + wake lock logic
✅ Requirements
- Flutter SDK
- Real Android device (proximity sensor required)
- Kotlin configured in Android module
🚀 Getting Started
1. main.dart
– App Entry & Lifecycle Observer
import 'package:flutter/material.dart';
import 'package:pocket_mode/services/proximity_service.dart';
import 'package:pocket_mode/views/home_screen.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatelessWidget with WidgetsBindingObserver {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Pocket Mode',
home: const HomeScreen(),
);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.detached) {
ProximityService.stopListening();
}
}
}
2. home_screen.dart – UI to Observe Proximity
import 'package:flutter/material.dart';
import 'package:pocket_mode/services/proximity_service.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
void initState() {
super.initState();
ProximityService.startListening((isNear) {
debugPrint('isNear: $isNear');
});
}
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(child: Text('Pocket Mode (Proximity Sensor)')),
);
}
}
###. 3. proximity_service.dart – Flutter ↔ Android Communication
import 'package:flutter/services.dart';
class ProximityService {
static const _channel = MethodChannel('pocket_mode');
static Future<void> startListening(Function(bool isNear) onChange) async {
_channel.setMethodCallHandler((call) async {
if (call.method == 'onProximityChanged') {
final bool isNear = call.arguments == true;
onChange(isNear);
}
});
try {
await _channel.invokeMethod('startProximity');
} catch (e) {
print('Error starting proximity sensor: $e');
}
}
static Future<void> stopListening() async {
try {
await _channel.invokeMethod('stopProximity');
} catch (e) {
print('Error stopping proximity sensor: $e');
}
}
}
4. MainActivity.kt – Native Android Sensor & Wake Lock
package com.example.pocket_mode
import android.content.Context
import android.hardware.*
import android.os.PowerManager
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity : FlutterActivity(), SensorEventListener {
private val CHANNEL = "pocket_mode"
private val WAKE_LOCK_TAG = "pocket_mode:WakeLock"
private lateinit var sensorManager: SensorManager
private var proximitySensor: Sensor? = null
private var powerManager: PowerManager? = null
private var wakeLock: PowerManager.WakeLock? = null
private var methodChannel: MethodChannel? = null
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
wakeLock = powerManager?.newWakeLock(
PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, WAKE_LOCK_TAG
)
methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
methodChannel?.setMethodCallHandler { call, result ->
when (call.method) {
"startProximity" -> startProximity(result)
"stopProximity" -> stopProximity(result)
else -> result.notImplemented()
}
}
}
private fun startProximity(result: MethodChannel.Result) {
proximitySensor?.let {
sensorManager.registerListener(this, it, SensorManager.SENSOR_DELAY_NORMAL)
if (wakeLock?.isHeld == false) wakeLock?.acquire()
result.success(null)
} ?: result.error("UNAVAILABLE", "Proximity sensor not available", null)
}
private fun stopProximity(result: MethodChannel.Result) {
sensorManager.unregisterListener(this)
if (wakeLock?.isHeld == true) wakeLock?.release()
result.success(null)
}
override fun onSensorChanged(event: SensorEvent?) {
if (event?.sensor?.type != Sensor.TYPE_PROXIMITY) return
val isNear = event.values[0] < (proximitySensor?.maximumRange ?: 0f)
methodChannel?.invokeMethod("onProximityChanged", isNear)
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
}
🛡 Required Permission
In your AndroidManifest.xml:
<uses-permission android:name="android.permission.WAKE_LOCK" />
🧪 How to Test
-
Deploy the app to a physical Android device (emulator won’t work).
-
Cover the top of your device (where the proximity sensor is).
-
Observe logs in the console:
isNear: true
⚠️ Notes
-
Works only on Android.
-
PROXIMITY_SCREEN_OFF_WAKE_LOCK is deprecated in some Android versions but still functional.
-
iOS doesn't support proximity sensor access via Flutter directly.
🙌 Credits Built with ❤️ using Flutter + native Android integration.
📦 Source Code: GitHub Repository