Is there a way to implement a Dialog for Location setting like the image below which gets triggered when app requires GPS location and doesn't find. Hitting OK will right away turn on the system GPS. This seems more convenient for users instead of taking them to location and manually turn on.
Is it possible to implement such thing in Flutter?
Expanded View of dialog:
Credits to Rajesh, as answered here. The plugin lets you add this native dialog for a quick location setting.
The implementation is quite simple as this:
import 'package:location/location.dart';
var location = Location();
Future _checkGps() async {
if(!await location.serviceEnabled()){
location.requestService();
}
}
#override
void initState() {
super.initState();
_checkGps();
}
In Kotlin, try this code:
class HomeActivity : AppCompatActivity() {
private val requestLocation = 199
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableLoc()
}
private fun enableLoc() {
val mLocationRequest = LocationRequest.create()
mLocationRequest.interval = 10000
mLocationRequest.fastestInterval = 5000
// mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
val builder = LocationSettingsRequest.Builder()
.addLocationRequest(mLocationRequest)
val client = LocationServices.getSettingsClient(this)
val task =
client.checkLocationSettings(builder.build())
task.addOnSuccessListener(this) {
// All location settings are satisfied. The client can initialize
// location requests here.
// ...
}
task.addOnFailureListener(this) { e ->
if (e is ResolvableApiException) {
// Location settings are not satisfied, but this can be fixed
// by showing the user a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
e.startResolutionForResult(
this,
requestLocation
)
} catch (sendEx: SendIntentException) {
// Ignore the error.
}
}
}
}
}
Related
I am trying to turn on a screen once an alarm fires. I am using this
[package][1] for the alarm. However, after time doing research I have not come any closer to achieving waking the screen on alarm firing.
The solutions either use deprecated APIs or are only specific to code within some contexts like activities and fragments, which is not the case for a flutter plugin.
Here is what I have tried
-Implementing ActivityAware interface but the onAttachedToActivity() never seems to fire
-Changing activity settings of my main activity in android manifes
android:showWhenLocked="true"
android:turnScreenOn="true"
I am currently trying to get an activity within the plugin to add flags to it to turn the screen on
if (Build.VERSION.SDK_INT < 27)
activity!!.window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
else
activity!!.setTurnScreenOn(true)
The problem is the activity variable is always null because for some reason, onAttachedToActivity doesn't run.
I have created an activity within the native code but I don't know how to get a reference to that activity and use it to turn the flags on. Remember, my screen is turned off so this is possibly what is complicating the matter.
Here is where I create the activity:
private fun startMainActivity(context: Context) {
val pm = context.packageManager
val intent = pm.getLaunchIntentForPackage(context.packageName)
context.startActivity(intent)
turnScreenON()
}
How can I create this plugin to wake the screen when an alarm fires?
Here is my code for the android kotlin side:
package com.example.wake_screen
import android.app.Activity
import android.content.Context
import android.os.Build
import android.util.Log
import android.view.WindowManager
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
/** WakeScreenPlugin */
class WakeScreenPlugin: ActivityAware, FlutterPlugin, MethodCallHandler{
private lateinit var channel : MethodChannel
private lateinit var mContext: Context
private var activity: Activity? = null
override fun onAttachedToEngine(#NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "wake_screen")
channel.setMethodCallHandler(this)
mContext = flutterPluginBinding.applicationContext
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
Log.d("We are attached",binding.lifecycle.toString())
}
override fun onDetachedFromActivity() {
//release resources
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onDetachedFromActivityForConfigChanges() {
//release resources
}
override fun onMethodCall(#NonNull call: MethodCall, #NonNull result: Result) {
if (call.method == "getPlatformVersion") {
startMainActivity(mContext)
result.success("Android ${android.os.Build.VERSION.RELEASE}")
}
else {
result.notImplemented()
}
}
private fun turnScreenON(){
if (Build.VERSION.SDK_INT < 27)
activity!!.window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
else
activity!!.setTurnScreenOn(true)
}
private fun startMainActivity(context: Context) {
val pm = context.packageManager
val intent = pm.getLaunchIntentForPackage(context.packageName)
context.startActivity(intent)
turnScreenON()
}
override fun onDetachedFromEngine(#NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
Here's my code on the flutter side:
//Provide instruction to wake device after duration
void setAlarm() async {
await AndroidAlarmManager.oneShot(
const Duration(seconds: _duration),
0,
wakeTheScreen,
wakeup: true,
allowWhileIdle: true,
);
}
//Function invoking platform code
static Future<void> wakeTheScreen() async {
String? version = await WakeScreen().getPlatformVersion();
print(version);
}
In summary, How do I wake the screen up using a flutter plugin that needs an activity to work
[1]: https://pub.dev/packages/android_alarm_manager_plus
I have an existing Flutter app that I use to prototype and test ideas before adding the idea to a project. I need a custom plugin to track location in the background for a project so I added plugin related code into the normal app project.
I am targeting android for a start. I have a Dart class representing the plugin that creates a method a channel to communicate with the platform code. On the platform side, I have created a class that extends FlutterPlugin
However, when I run the app and Dart native code calls methods on the Android side using the method channel, I get Unhandled Exception: MissingPluginException.
Here's the code
Dart code
class GeofencePlugin {
final MethodChannel _channel =
const MethodChannel('marcel/geofencing_plugin');
Future<bool> init() async {
//callbackDispatcher is a top level function that acts as entry point for background isolate
final callback = PluginUtilities.getCallbackHandle(callbackDispatcher);
await _channel
.invokeMethod('GeofencingPlugin.initialiseService', <dynamic>[callback!.toRawHandle()]);
return true;
}
Future<bool> registerGeofence(GeofenceRegion region) async {
return true;
}
Future<bool> removeGeofence(GeofenceRegion region) async {
return true;
}
}
Android code
class GeofencingPlugin : ActivityAware, FlutterPlugin, MethodChannel.MethodCallHandler {
private var mContext : Context? = null
private var mActivity : Activity? = null
private val geofencePendingIntent: PendingIntent by lazy {
val intent = Intent(mContext, GeofenceBroadcastReceiver::class.java)
PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
companion object {
#JvmStatic
private val TAG = "MARK_TAG"
#JvmStatic
val SHARED_PREFERENCES_KEY = "com.example.flutter_playground.geofencing"
#JvmStatic
val CALLBACK_DISPATCHER_HANDLE_KEY = "callback_dispatch_handler"
#JvmStatic
private fun initialiseService(context: Context, args: ArrayList<*>?) {
val callbackHandle = args!![0] as Long
context.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
.edit()
.putLong(CALLBACK_DISPATCHER_HANDLE_KEY, callbackHandle)
.commit()
}
}
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
mContext = binding.applicationContext
val channel = MethodChannel(binding.binaryMessenger, "marcel/geofencing_plugin")
channel.setMethodCallHandler(this)
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
mContext = null
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
mActivity = binding.activity
}
override fun onDetachedFromActivity() {
mActivity = null
}
override fun onDetachedFromActivityForConfigChanges() {
mActivity = null
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
mActivity = binding.activity
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
val args = call.arguments<ArrayList<*>>()
when(call.method) {
"GeofencingPlugin.initialiseService" -> {
initialiseService(mContext!!, args)
setupGeo()
result.success(true)
}
else -> result.notImplemented()
}
}
private fun setupGeo(){
val geofencingClient = mContext!!.getGeofenceClient()
val fence = Geofence.Builder()
.setRequestId("Mark")
.setCircularRegion(46.5422,14.4011,500f)
.setExpirationDuration(600000)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
.build()
val request = GeofencingRequest.Builder().apply {
setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_DWELL)
addGeofences(listOf(fence))
}.build()
geofencingClient.addGeofences(request, geofencePendingIntent)?.run {
addOnSuccessListener {
Toast.makeText(mContext,"Hahaha", Toast.LENGTH_LONG).show()
}
addOnFailureListener {
Log.d(TAG,it.message?:"Eoo")
Toast.makeText(mContext,"Heeerrrh", Toast.LENGTH_LONG).show()
}
}
}
}
I tried the following
Manually adding the android plugin class to the GeneratedPluginRegistrant.java file
Using flutterEngine.plugins.add('my plugin') in the MainActivity's onCreate method.
After digging into the pubsec.yaml of other plugins, I found out in order to have my app's plugin recognised by flutter I have to add the following to my pubsec.yaml under the flutter section:
plugin:
platforms:
android:
package: com.example.flutter_playground.geofencing
pluginClass: GeofencingPlugin
i have a Barrier in activity_constraint_layout, and want change it's type based some data from network, but setType(int type) is not work.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_constraint_layout)
clContainer = findViewById(R.id.clContainer)
// worked here
val barrier = findViewById<Barrier>(R.id.barrier)
barrier.type = Barrier.START
clContainer.setOnClickListener {
// do not work here
barrier.type = Barrier.END
}
}
The statement barrier.type = Barrier.END executes the following withing the Barrier class:
public void setType(int type) {
mIndicatedType = type;
}
As you can see, it only sets the value of a variable. You also need to request a layout as follows:
clContainer.setOnClickListener {
// do not work here
barrier.type = Barrier.END
barrier.requestLayout()
}
Once you do this, you should see the effect you want.
I'm building an Android app using purely Jetpack Compose. My entire application is wrapped under one scaffold, and has a ViewModel for each "screen" (which are composables) in my app. Because of that, I have some conditional statements in my scaffold to determine the floating action button (FAB) based on the route. However, one of the FABs needs access to a function in a ViewModel, which is only created when I navigate to the route that holds that composable, and I'm at a loss on the best way to give the FAB access to that viewmodel function.
Take the following example (based off my code), and note the FAB for the route "route3".
#AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp()
}
}
#Composable
fun MyApp() {
val navController = rememberNavController()
val backstackEntry = navController.currentBackStackEntryAsState()
val scaffoldState = rememberScaffoldState()
Surface(color = MaterialTheme.colors.background) {
Scaffold(
topBar = { ... },
bottomBar = { ... },
floatingActionButton = {
when (backstackEntry.value?.destination?.route) {
"route2" -> FAB2(navController)
"route3" -> FAB3(navController) // Needs to access function from viewModel3
}
},
scaffoldState = scaffoldState,
) {
MyNavHost(navController, scaffoldState)
}
}
}
#Composable
fun MyNavHost(navController: NavHostController, scaffoldState: ScaffoldState) {
NavHost(
navController = navController,
startDestination = "route1"
) {
composable("route1") { Destination1() }
composable("route2") { Destination2() }
composable("route3") { Destination3() }
}
}
#Composable
fun Destination1() { ...}
#Composable
fun Destination2() { ... }
#Composable
fun Destination3() {
val viewModel3: CustomViewModel = hiltViewModel()
Screen3(viewModel3)
}
}
So my main question is, if the FAB3 variable needs to access a function from viewModel3, how would I go about doing it?
I decided to just switch over to using a scaffold for every screen.
Honestly, this makes managing routes a lot easier, because in the single scaffold scenario, it was getting difficult managing all of the possible routes for things like the TopBar and FAB in large when() blocks.
But if anyone has a solution to the original question that would be appreciated!
Basically I'm invoking a payment sdk in native android(kotlin) using platform channel. SDK is initialised successfully. After payment transaction I will receive the payment status in a kotlin file which actually extends sdk class files (not an activity). From this callback method I need to pass the status to the flutter dart code. Everything runs successfully without any error but while passing the payment status flutter method is not invoked.
Flutter dart code.
#override
void initState() {
// TODO: implement initState
super.initState();
platform.setMethodCallHandler(_handlePaymentResponse); // To call from native android after payment response.
}
// Invoked from Native Android after payment response.
Future<dynamic> _handlePaymentResponse(MethodCall call) async {
print('_handlePaymentResponse method called');
switch(call.method) {
case "message":
debugPrint('From Native====' + call.arguments);
return new Future.value("");
}
}
// On button click this method will be invoked.
Future<void> _initializeSDK() async {
print('Token $token');
try {
await platform.invokeMethod('callPaymentSDK');
} on PlatformException catch (e) {
print("Failed to initialize SDK : '${e.message}'.");
}
}
Native Android : Paymentstatus.kt
class PaymentResponse() : LibraryPaymentStatusProtocol, Parcelable {
//val channel = MethodChannel(flutterView, MainActivity.CHANNEL)
var backgroundFlutterView: FlutterNativeView? = null
override fun paymentStatus(p0: String?, p1: Activity?) {
if (p1 != null) {
Handler(Looper.getMainLooper()).post {
Log.v("PaymentResponse", "Main thread called")
backgroundFlutterView = FlutterNativeView(p1, true)
val channel = MethodChannel(backgroundFlutterView, MainActivity.CHANNEL)
channel.invokeMethod("message", p0); // Invoking Flutter method
}
}
}
}
I ran in to the same issue, and in the end the solution that worked for me (not sure if this is the ideal approach) was to store a reference to the FlutterEngine inside my MainActivity like so:
class MainActivity: FlutterActivity() {
companion object {
var flutterEngineInstance: FlutterEngine? = null
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
flutterEngineInstance = flutterEngine
}
}
In my code I would then just invoke a method like this:
MethodChannel(
MainActivity.flutterEngineInstance.dartExecutor.binaryMessenger,
"com.example"
).invokeMethod("method", mapOf())
For my case i didn't receive data from android native to flutter when i called channel.invokeMethod from OnCreate() in MainActivity: FlutterActivity()
so i added channel.invokeMethod( ... ) inside onStart() method, its worked.
also you can use:
runOnUiThread { channel.invokeMethod( ... ) }
or
Handler(Looper.getMainLooper()).post { channel.invokeMethod( ... ) }