Using
Flutter 3.13.1 • channel stable • https://github.com/flutter/flutter.git
Framework • revision e1e47221e8 (3 months ago) • 2023-08-22 21:43:18 -0700
Engine • revision b20183e040
Tools • Dart 3.1.0 • DevTools 2.25.0
flutter_foreground_task: ^6.0.0+1
fl_location: ^3.0.0
Code:
`import 'dart:async';
import 'dart:isolate';
import 'package:fl_location/fl_location.dart';
import 'package:flutter/material.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
void main() => runApp(const ExampleApp());
// The callback function should always be a top-level function.
void startCallback() {
// The setTaskHandler function must be called to handle the task in the background.
FlutterForegroundTask.setTaskHandler(MyTaskHandler());
}
class MyTaskHandler extends TaskHandler {
StreamSubscription? _streamSubscription;
@OverRide
Future onStart(DateTime timestamp, SendPort? sendPort) async {
_streamSubscription = FlLocation.getLocationStream().listen((event) {
FlutterForegroundTask.updateService(
notificationTitle: 'My Location',
notificationText: '${event.latitude}, ${event.longitude}',
);
// Send data to the main isolate.
sendPort?.send(event);
});
}
@OverRide
Future onDestroy(DateTime timestamp, SendPort? sendPort) async {
await _streamSubscription?.cancel();
}
@OverRide
Future onRepeatEvent(DateTime timestamp, SendPort? sendPort) async {}
}
class ExampleApp extends StatelessWidget {
const ExampleApp({Key? key}) : super(key: key);
@OverRide
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: ExamplePage(),
);
}
}
class ExamplePage extends StatefulWidget {
const ExamplePage({Key? key}) : super(key: key);
@OverRide
State createState() => _ExamplePageState();
}
class _ExamplePageState extends State {
ReceivePort? _receivePort;
Future _checkAndRequestPermission({bool? background}) async {
if (!await FlLocation.isLocationServicesEnabled) {
// Location services are disabled.
return false;
}
var locationPermission = await FlLocation.checkLocationPermission();
if (locationPermission == LocationPermission.deniedForever) {
// Cannot request runtime permission because location permission is denied forever.
return false;
} else if (locationPermission == LocationPermission.denied) {
// Ask the user for location permission.
locationPermission = await FlLocation.requestLocationPermission();
if (locationPermission == LocationPermission.denied ||
locationPermission == LocationPermission.deniedForever) return false;
}
// Location permission must always be allowed (LocationPermission.always)
// to collect location data in the background.
if (background == true &&
locationPermission == LocationPermission.whileInUse) return false;
// Location services has been enabled and permission have been granted.
return true;
}
Future _initForegroundTask() async {
FlutterForegroundTask.init(
androidNotificationOptions: AndroidNotificationOptions(
channelId: 'foreground_service',
channelName: 'Foreground Service Notification',
channelDescription:
'This notification appears when the foreground service is running.',
channelImportance: NotificationChannelImportance.LOW,
priority: NotificationPriority.LOW,
iconData: const NotificationIconData(
resType: ResourceType.mipmap,
resPrefix: ResourcePrefix.ic,
name: 'routica_launcher_icon',
),
),
iosNotificationOptions: const IOSNotificationOptions(
showNotification: true,
playSound: false,
),
foregroundTaskOptions: const ForegroundTaskOptions(
interval: 5000,
isOnceEvent: false,
autoRunOnBoot: true,
allowWakeLock: true,
allowWifiLock: true,
),
);
}
Future _startForegroundTask() async {
if (await _checkAndRequestPermission(background: true) == false) {
print('Location permission denied!!');
return false;
}
ReceivePort? receivePort = FlutterForegroundTask.receivePort;
if (await FlutterForegroundTask.isRunningService) {
await FlutterForegroundTask.restartService();
} else {
await FlutterForegroundTask.startService(
notificationTitle: 'Foreground Service is running',
notificationText: 'Tap to return to the app',
callback: startCallback,
);
}
return _registerReceivePort(receivePort);
}
Future _stopForegroundTask() async {
return await FlutterForegroundTask.stopService();
}
bool _registerReceivePort(ReceivePort? receivePort) {
_closeReceivePort();
if (receivePort != null) {
_receivePort = receivePort;
_receivePort?.listen((message) {
if (message is Location) {
print('location: ${message.toJson()}');
}
});
return true;
}
return false;
}
void _closeReceivePort() {
_receivePort?.close();
_receivePort = null;
}
@OverRide
void initState() {
super.initState();
initForegroundTask();
WidgetsBinding.instance.addPostFrameCallback(() async {
// You can get the previous ReceivePort without restarting the service.
if (await FlutterForegroundTask.isRunningService) {
final newReceivePort = FlutterForegroundTask.receivePort;
_registerReceivePort(newReceivePort);
}
});
}
@OverRide
void dispose() {
_closeReceivePort();
super.dispose();
}
@OverRide
Widget build(BuildContext context) {
// A widget that prevents the app from closing when the foreground service is running.
// This widget must be declared above the [Scaffold] widget.
return WithForegroundTask(
child: Scaffold(
appBar: AppBar(
title: const Text('Flutter Foreground Task'),
centerTitle: true,
),
body: _buildContentView(),
),
);
}
Widget _buildContentView() {
buttonBuilder(String text, {VoidCallback? onPressed}) {
return ElevatedButton(
child: Text(text),
onPressed: onPressed,
);
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
buttonBuilder('start', onPressed: _startForegroundTask),
buttonBuilder('stop', onPressed: _stopForegroundTask),
],
),
);
}
}
`
Getting the following error:
════════ Exception caught by services library ══════════════════════════════════
The following MissingPluginException was thrown while activating platform stream on channel plugins.pravera.com/fl_location/updates:
MissingPluginException(No implementation found for method listen on channel plugins.pravera.com/fl_location/updates)
When the exception was thrown, this was the stack
#0 MethodChannel._invokeMethod
platform_channel.dart:308
#1 EventChannel.receiveBroadcastStream.
platform_channel.dart:652
════════════════════════════════════════════════════════════════════════════════
any idea? @Dev-hwang
Note: It works fine if calling on the main isolate.