I've got an Android app written in Dart using the Flutter framework. When it's first opened up, it requests permission to access location in the background. When the user opens the app using the launcher icon, this all works fine.
I use the background_fetch package to configure a headless task that runs on boot. This works, but it appears the context this runs in doesn't have the same permissions as the app. I check the permissions like this:
status = await Permission.location.status;
locationGranted = status.isGranted;
status = await Permission.locationAlways.status;
locationAlwaysGranted = status.isGranted;
if (locationAlwaysGranted) {
...
}
If I run this code from the app's main() then the body of the if statement is executed; when it runs from the headless task after the phone restarts, the body of the if block is not executed.
Is there a way to get the headless task to have the same permissions as the app? Or is there a better way to restart background location tracking after a phone restart?
The background-fetch task is configured like this:
BackgroundFetch.registerHeadlessTask(headlessTask);
BackgroundFetch.configure(BackgroundFetchConfig(
minimumFetchInterval: 15,
stopOnTerminate: false,
enableHeadless: true,
startOnBoot: true,
requiresBatteryNotLow: false,
requiresStorageNotLow: false,
requiresCharging: false,
requiresDeviceIdle: false,
requiredNetworkType: NetworkType.NONE
), (String taskId) {
...
BackgroundFetch.finish(taskId);
});
BackgroundFetch.scheduleTask(TaskConfig(
delay: 0,
periodic: false,
startOnBoot: true,
taskId: "gfz_on_boot",
enableHeadless: true
));
Edited to add this is the stack trace that appears in the flutter logs:
E/flutter ( 6464): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: PlatformException(PermissionHandler.PermissionManager, Unable to detect current Android Activity., null, null)
E/flutter ( 6464): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:607:7)
E/flutter ( 6464): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:167:18)
E/flutter ( 6464): <asynchronous suspension>
E/flutter ( 6464): #2 MethodChannelPermissionHandler.requestPermissions (package:permission_handler_platform_interface/src/method_channel/method_channel_permission_handler.dart:71:9)
E/flutter ( 6464): <asynchronous suspension>
E/flutter ( 6464): #3 GFZModel.updatePermissions (package:gfz_app/model.dart:349:24)
E/flutter ( 6464): <asynchronous suspension>
This makes sense at one level - there is no current activity in the background task - but how can we use permissions in background tasks?
Related
I try to run a url using url lancher package
https://pub.dev/packages/url_launcher
and i got this error , i use a package to display my app over apps , using that package https://pub.dev/packages/flutter_overlay_window , and i added a button inside the overlay and when i press it it supposed to send a message to whatsapp using url lancher package , but the problem is that when your app is over apps you will get that error :
E/flutter (24166): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: PlatformException(NO_ACTIVITY, Launching a URL requires a foreground activity., null, null)
E/flutter (24166): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:607:7)
E/flutter (24166): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:177:18)E/flutter (24166): <asynchronous suspension>
E/flutter (24166): #2 launchUrlString (package:url_launcher/src/url_launcher_string.dart:39:10)
E/flutter (24166): <asynchronous suspension>
E/flutter (24166): #3 launchUrl (package:url_launcher/src/url_launcher_uri.dart:57:10)
Flutter ble - write characteristic error
I'm not being able to write on ble pheriperal device, using flutter_blue library, it throws this exception:
Unhandled Exception: PlatformException(write_characteristic_error, service (0000ff06-0000-1000-8000-00805f9b34fb) could not be located on the device, null, null)
E/flutter (29808): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:607:7)
E/flutter (29808): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:167:18)
E/flutter (29808): <asynchronous suspension>
E/flutter (29808): #2 BluetoothCharacteristic.write (package:flutter_blue/src/bluetooth_characteristic.dart:120:18)
E/flutter (29808): <asynchronous suspension>
E/flutter (29808): #3 DeviceScreen._buildServiceTiles.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:untitled/main.dart:176:23)
or another error saying : Write is not permited.
I'm using example code provided by flutter blue library, here is the code that im trying to write after device is connected:
onWritePressed: () async {
await c.write(_getRandomBytes(), withoutResponse: true);
await c.read();
},
onNotificationPressed: () async {
await c.setNotifyValue(!c.isNotifying);
if (c.properties.read) {
await c.read();
}
List<int> _getRandomBytes(){
String command = "55 AA 00 01 50 3C 00";
return command.codeUnits;
}
The hardware device is working fine in Kotlin, data is sent and received.
I'm creating a Flutter application.
If I run this application on debug mode webview works well
but if I run this application on release mode it shows blank screen. It's showing PlatformException
I tried to get help from github issues but couldn't find the solution.
Manifest
<uses-permission android:name="android.permission.INTERNET"/>
build.gradle
minSdkVersion 20
targetSdkVersion 29
pubspec.yaml
webview_flutter: ^1.0.7
Error Log
E/flutter (16872): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: PlatformException(error, java.lang.IllegalStateException: Trying to create a platform view of unregistered type: plugins.flutter.io/webview
E/flutter (16872): at io.flutter.plugin.platform.j$a.g(Unknown Source:229)
E/flutter (16872): at io.flutter.embedding.engine.i.j$a.b(Unknown Source:152)
E/flutter (16872): at io.flutter.embedding.engine.i.j$a.j(Unknown Source:144)
E/flutter (16872): at e.a.c.a.j$a.a(Unknown Source:17)
E/flutter (16872): at io.flutter.embedding.engine.e.b.d(Unknown Source:57)
E/flutter (16872): at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(Unknown Source:4)
E/flutter (16872): at android.os.MessageQueue.nativePollOnce(Native Method)
E/flutter (16872): at android.os.MessageQueue.next(MessageQueue.java:335)
E/flutter (16872): at android.os.Looper.loop(Looper.java:183)
E/flutter (16872): at android.app.ActivityThread.main(ActivityThread.java:7660)
E/flutter (16872): at java.lang.reflect.Method.invoke(Native Method)
E/flutter (16872): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
E/flutter (16872): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
E/flutter (16872): , null, null)
E/flutter (16872): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:581)
E/flutter (16872): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:158)
E/flutter (16872): <asynchronous suspension>
E/flutter (16872): #2 TextureAndroidViewController._sendCreateMessage (package:flutter/src/services/platform_views.dart:1036)
E/flutter (16872): <asynchronous suspension>
E/flutter (16872): #3 AndroidViewController.create (package:flutter/src/services/platform_views.dart:742)
E/flutter (16872): <asynchronous suspension>
E/flutter (16872): #4 RenderAndroidView._sizePlatformView (package:flutter/src/rendering/platform_view.dart:195)
E/flutter (16872): <asynchronous suspension>
Please check for "android.permission.INTERNET" pemission in your Manifest file. In "app/src/main/AndroidManifest.xml" folder.
I use flutter_inappwebview: ^5.3.2 plugin and faced same issue in release mode.
I think you did not give internet permission in android/app/src/main/AndroidManifest.xml
add this line into the <manifest of android/app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
and more information for Flutter Insecure http is not allowed by platform you can see this discussion
<uses-permission android:name="android.permission.INTERNET" /> //This line add
<application
android:name="io.demo.app.test"
android:label="ProjectName"
android:usesCleartextTraffic="true" //This line add
android:icon="#mipmap/ic_launcher">
I believe I ran into this same issue and what was happening was that there was both a cached flutter engine that had been initialised as well as a new flutter engine created later.
From that I went with a cached flutter engine and used a method channel to navigate as required.
So at some point somewhere(probably in your MainActivity or iOS equivalent) setup your Cached Flutter Engine:
val flutterEngine = FlutterEngine(this)
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache
.getInstance()
.put(INSERT_YOUR_DESIRED_FLUTTER_ENGINE_ID_HERE, flutterEngine)
Then when you want to open your FlutterFragment(Android example):
var flutterEngine = FlutterEngineCache
.getInstance()
.get(INSERT_YOUR_FLUTTER_ENGINE_ID_FROM_BEFORE_HERE)
var flutterFragment = FlutterFragment
.withCachedEngine(INSERT_YOUR_FLUTTER_ENGINE_ID_FROM_BEFORE_HERE)
.build()
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
INSERT_YOUR_DESIRED_METHOD_CHANNEL_NAME_HERE
).invokeMethod("setRoute", INSERT_ROUTE_YOU_WANT_HERE)
openFragment(flutterFragment!!)
And an example of how you might use this method channel in your Flutter Dart code
class _MyViewState extends State<MyView> {
static const myMethodChannel = const MethodChannel(INSERT_YOUR_DESIRED_METHOD_CHANNEL_NAME_HERE);
Future<void> _handleMethodCalls(MethodCall call) async {
switch (call.method) {
case "setRoute":
Navigator.pushReplacmentNamed(context, INSERT_YOUR_DESIRED_ROUTE_NAME_HERE)
...
}
}
...
#override
void initState() {
super.initState();
platform.setMethodCallHandler(_handleMethodCalls);
}
}
My project involves connecting to a Bluetooth device and reading constant data from the device.
I am using the plugin FlutterBleLib to connect to the Bluetooth device. The process of connecting to the device and reading data is working fine.
The issue I am having is when the user closes the app.
Found the following article: The Tricky Task of Keeping Flutter Running, which allows us to run the code in the background even after the app is closed. The sample runs a counter service in the background. My idea was to replace the counter service with the Bluetooth service.
The sample code was built on pre 1.12 Android project. So, I upgraded the code to embedding version 2 using the following resource.
I was able to build the project and everything is working fine on the main UI, but as soon as the app is closed, the code is starting the FlutterNativeView and then when it is trying to start the bluetooth service it is failing with the following error:
I/flutter: ══╡ EXCEPTION CAUGHT BY SERVICES LIBRARY ╞══════════════════════════════════════════════════════════
The following MissingPluginException was thrown while activating platform stream on channel
flutter_ble_lib/stateRestoreEvents:
MissingPluginException(No implementation found for method listen on channel
I/flutter: flutter_ble_lib/stateRestoreEvents)
When the exception was thrown, this was the stack:
#0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:159:7)
<asynchronous suspension>
#1 MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:334:12)
#2 EventChannel.receiveBroadcastStream.<anonymous closure> (package:flutter/src/services/platform_channel.dart:542:29)
#3 EventChannel.receiveBroadcastStream.<anonymous closure> (package:flutter/src/services/platform_channel.dart:528:64)
#16 FlutterBleLib.restoredState (package:flutter_ble_lib/src/bridge/lib_core.dart:52:8)
#17 InternalBleManager.createClient (package:flutter_ble_lib/src/internal_ble_manager.dart:23:15)
#18 DevicesBloc.init (package:in_field/modules/eld_ble/devices_bloc.dart:54:10)
#19 BluetoothService.startBluetoothService (package:in_field/modules/eld_ble/background/bluetooth_service.dart:60:18)
#20 backgroundMain (package:in_field/modules/eld_ble/background/background_main.dart:11:22)
#21 _runMainZoned.<anonymous closure>.<anonymous closure> (dart:ui/hooks.dart:233:25)
#26 _runMainZoned.<anonymous closure> (dart:ui/hooks.dart:225:5)
#27 _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
(elided 17 frames from class _RawReceivePortImpl and dart:async)
════════════════════════════════════════════════════════════════════════════════════════════════════
E/flutter: [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: MissingPluginException(No implementation found for method getState on channel flutter_ble_lib)
#0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:159:7)
<asynchronous suspension>
#1 MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:334:12)
#2 BluetoothStateMixin.state (package:flutter_ble_lib/src/bridge/bluetooth_state_mixin.dart:29:8)
#3 BluetoothStateMixin.observeBluetoothState (package:flutter_ble_lib/src/bridge/bluetooth_state_mixin.dart:34:43)
<asynchronous suspension>
The issue is happening as soon the FlutterNativeView had been started.
Here is the code from the sample before the upgrade (BackgroundService.kt):
private fun startFlutterNativeView() {
Log.i("BackgroundService", "Starting FlutterNativeView")
FlutterMain.ensureInitializationComplete(this, null)
getCallbackInformation()?.let { flutterCallbackInformation ->
flutterNativeView = FlutterNativeView(this, true).apply {
GeneratedPluginRegistrant.registerWith(pluginRegistry)
val args = FlutterRunArguments().apply {
bundlePath = FlutterMain.findAppBundlePath()
entrypoint = flutterCallbackInformation.callbackName
libraryPath = flutterCallbackInformation.callbackLibraryPath
}
runFromBundle(args)
}
}
}
Here is the code after the upgrade:
private fun startFlutterNativeView() {
Log.i("BackgroundService", "Starting FlutterNativeView")
FlutterMain.ensureInitializationComplete(this, null)
getCallbackInformation()?.let { flutterCallbackInformation ->
flutterNativeView = FlutterNativeView(this, true).apply {
//GeneratedPluginRegistrant.registerWith(FlutterEngineCache.getInstance().get("test")!!)
//GeneratedPluginRegistrant.registerWith("com.polidea.flutter_ble_lib.FlutterBleLibPlugin");
//FlutterBleLibPlugin.registerWith(registry?.registrarFor("com.polidea.flutter_ble_lib.FlutterBleLibPlugin"))
//FlutterBleLibPlugin.registerWith(registrarFor("com.polidea.flutter_ble_lib.FlutterBleLibPlugin"))
//GeneratedPluginRegistrant.registerWith(FlutterEngine(applicationContext))
val args = FlutterRunArguments().apply {
bundlePath = FlutterMain.findAppBundlePath()
entrypoint = flutterCallbackInformation.callbackName
libraryPath = flutterCallbackInformation.callbackLibraryPath
}
runFromBundle(args)
}
}
}
The commented lines are the place where I am trying to register the Bluetooth plugin but it is failing with errors for all the above trials.
Can anyone tell me where I am doing wrong? Any help is greatly appreciated.
See float button overlay
Register your background service above the main method,
and call registerCallback() inside the main method.
I want to have a dart background service running forever (isolates) which will communicate with a server through websockets. And I have an API for Android which gather information to send to the server.
How can I invoke that Android methods which use callbacks and everything from the background isolate?
EDIT
So far in dart I created an Isolate to periodically invoke the poolSong method in background even if the user is using another app or has the screen turned off.
But that gives me the error below...on the github issues they say that I can't send a platform message from a different Isolate unless it's the main. But if I do it from the main isolate, when the user exit the app that isolate will terminate too.
MainDart
class _MyHomePageState extends State<MyHomePage> {
final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
static const platform = const MethodChannel('mainService');
static _poolSong(SendPort sendPort) async {
const oneSec = const Duration(seconds:1);
new Timer.periodic(oneSec, (Timer t) => platform.invokeMethod('poolSong'));
}
#override
void initState() {
super.initState();
ReceivePort receivePort = ReceivePort();
Isolate.spawn(_poolSong, receivePort.sendPort);
}
·
·
·
MainActivityJava
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
(call, result) -> {
if (call.method.equals("startService"))
startService();
if (call.method.equals("poolSong"))
poolSong();
}
);
}
ERROR
E/flutter (25412): [ERROR:flutter/runtime/dart_isolate.cc(717)] Isolate (765499726) 'main.dart:_poolSong()' exited with an error
E/flutter (25412): [ERROR:flutter/shell/common/shell.cc(186)] Dart Error: Unhandled exception:
E/flutter (25412): error: native function 'Window_sendPlatformMessage' (4 arguments) cannot be found
E/flutter (25412): #0 Window.sendPlatformMessage (dart:ui/window.dart:811:9)
E/flutter (25412): #1 BinaryMessages._sendPlatformMessage (package:flutter/src/services/platform_messages.dart:39:15)
E/flutter (25412): #2 BinaryMessages.send (package:flutter/src/services/platform_messages.dart:87:12)
E/flutter (25412): #3 MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:286:49)
E/flutter (25412): <asynchronous suspension>
E/flutter (25412): #4 _MyHomePageState._poolSong.<anonymous closure> (package:musictie/main.dart:37:54)
E/flutter (25412): #5 _Timer._runTimers (dart:isolate/runtime/libtimer_impl.dart:382:19)
E/flutter (25412): #6 _Timer._handleMessage (dart:isolate/runtime/libtimer_impl.dart:416:5)
E/flutter (25412): #7 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)
This error happens when trying to run dart code when the app is in the garbage (app closed), Until now the best way for dealing with background service is to do what you need in native code, and dont try to run anything from dart when app closed.
Since Flutter 3.7 plugins and platform channels can be used from any isolate, not only from the root one.
References:
https://medium.com/flutter/introducing-background-isolate-channels-7a299609cad8
https://github.com/flutter/flutter/issues/13937