I have a Flutter app that lunch a native activity that contains a form (first and last name fields), when I fill in the fields and click the finish button I want to get these data back to Flutter widget.
Basically, all what I did, was lunch the FormActivity, how can I get the form data from FormActivity and display them on my Flutter widget?
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
if (call.method == "lunchActivity") {
val intent = Intent(this, FormActivity::class.java)
startActivity(intent)
}
}
}
Use startActivityForResult to launch an activity, return a result from the activity with setResult, implement ActivityResultListener to handle the result and finally call success on a channel to return the result to Flutter side.
See details for Flutter - Android communication.
Related
I did a plugin in native code(Kotlin) and tried to use that in a Flutter application. I used a method channel for Flutter to native code communication and an event channel for native code to Flutter. Method channel communication is working properly, but event channel isn't.
FLUTTER:
// will execute this in initState
EventChannel _eventChannel = EventChannel("STREAM_CHANNEL");
_eventChannel.receiveBroadcastStream().map((event) => {
// some actions
})
// will execute this by button click (happens after widget got built)
result =
await MethodChannel("METHOD_CHANNEL").invokeMethod("functionToInvoke", {
// payload
})
KOTLIN:
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
startEventChannel(flutterEngine)
startMethodChannel(flutterEngine.dartExecutor.binaryMessenger)
}
fun startMethodChannel(#NonNull flutterEngine: FlutterEngine) {
MethodChannel(flutterEngine.dartExecutor.binaryMessenger,
"METHOD_CHANNEL").setMethodCallHandler {
call, result -> when(call.method) {
"functionToInvoke" -> {
// some action
}
}
}
// method not called inspite of calling from configureFlutterEngine
fun startEventChannel(messenger: BinaryMessenger) {
eventChannel = EventChannel(messenger, "STREAM_CHANNEL");
eventChannel.setStreamHandler(
object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, eventSink: EventChannel.EventSink)
{
eventSink.success("success")
}
}
)
}
The startEventChannel method is not even called but startMethodChannel has been called and methods are registered properly.
There is no issue with the channel names (checked both sides).
Am I missing anything?
Problems:
Stream not listened to:
The stream returned by _eventChannel.receiveBroadcastStream() is not being listened to. You're only mapping the stream but this callback won't be triggered until you get data in the stream.
And from your Kotlin EventChannel code, data is not added into the stream until you listen to the event channel.
Argument Mismatch:
The startEventChannel method requires an argument of type BinaryMessenger but you're sending it flutterEngine which is of type FlutterEngine.
Solution:
Add a listener to the stream:
Call .listen on the mapped stream from _eventChannel.receiveBroadcastStream() like shown below:
_eventChannel.receiveBroadcastStream().map((event) {}).listen((event) {
// some actions
});
Pass the right argument to startEventChannel:
Pass the BinaryMessenger to the startEventChannel instead of the FlutterEngine.
startEventChannel(flutterEngine.dartExecutor.binaryMessenger)
I am trying to open FlutterActivity in my existing android application. Before I was creating new flutter engine every time I was opening activity like this:
FlutterActivity
.withNewEngine()
.build(context)
And everything was working fine besides a little lag while opening the activity. To get rid of the lag I wanted to switch to using cached engine. I followed this official tutorial: LINK
And ended up with smething like this:
In my Application class:
class App : Application() {
lateinit var flutterEngine: FlutterEngine
override fun onCreate() {
...
flutterEngine = FlutterEngine(this)
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache
.getInstance()
.put("myEngineId", flutterEngine)
}
}
And later in my application on the button click, in the same place that I was successfully opening FlutterActivity:
FlutterActivity
.withCachedEngine("myEngineId")
.build(context)
So I basically followed the all the instructions but the effect that I get now is after the button click there is even longer lag than before and then there is only black screen being displayed. My flutter screen is not displayed and application is kind of frozen I can't go back or do anything. There is also no error or any useful info in the logs. I have no idea what is going on. What am I doing wrong?
To Use cached FlutterEngine
In FlutterActivity you must declare provideFlutterEngine method.
class DemoActivity : FlutterActivity() {
override fun provideFlutterEngine(context: Context): FlutterEngine? =
FlutterEngineCache.getInstance().get(FlutterConstants.ENGINE_ID)
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "demo-channel")
.setMethodCallHandler { call, result ->
if (call.method == "demo-method") {
demoMethod()
result.success(null)
} else {
result.notImplemented()
}
}
}
private fun demoMethod() {
// Do native code
}
}
I want to send data with intent from native android app to flutter app, So if flutter app is closed below code working fine to fetch intent data in main.dart file. If flutter app is in foreground and i tries to send data from native app to flutter app nothing happens. Is their anything else to need implement for this case?
Native app code to start flutter app
var intent = getPackageManager().getLaunchIntentForPackage("com.flutterapp");
intent.putString( "MapParams", jsonObj.toString())
startActivity(intent)
Flutter app code
class MainActivity: FlutterActivity()
var sharedData="";
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
GeneratedPluginRegistrant.registerWith(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger,
"share_channel").setMethodCallHandler { call, result ->
if (call.method == "MapParams") {
handleIntent()
result.success(sharedData)
sharedData = ""
}
}
}
private fun handleIntent() {
if (getIntent().hasExtra("MapParams")) {
getIntent().getSerializableExtra("MapParams")?.let { intentData ->
sharedData = intentData.toString()
}
}
}
Main.Dart file
Future<String> getSharedData() async {
return await MethodChannel('share_channel')
.invokeMethod("MapParams") ??
"";
}
I am building a Flutter Application, and for one of the API's I am using, it does not have Flutter support, only Android and iOS. My solution to this was to use Platform Channels, but how would I pass an Image as an argument?
To explain a little further, I am picking an image from the gallery with ImagePicker().getImage in the dart file, and I want to send the image selected to the method in the Kotlin file that will do something with the image on its end and return a string.
After looking at the docs, I was able to make a channel like this:
static const platform = const MethodChannel('app.dev/channel');
final string result = await platform.invokeMethod('returnStringfromImage');
And in the Kotlin file:
private val CHANNEL = "app.dev/channel"
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
// Note: this method is invoked on the main thread.
call, result ->
if (call.method == "returnStringfromImage") {
val return = returnStringfromImage(call.arguments)
}
else {
result.notImplemented()
}
}
}
How would I send the image over, and pass it as an argument for the returnStringfromImage() method? Thank you!
You need to convert the picked image to any of these supported types, most probably as bytes (Uint8list) to send over to the platform side.
ImagePicker would return a File, so You can call readAsBytes on it to get Uint8list. Then you can pass it via arguments of invokeMethod.
So I am making a Flutter plugin and I am attempting to run Kotlin code on Android. The problem is, this code runs a method which attempts to start an activity without the FLAG_ACTIVITY_NEW_TASK flag on the intent. The problem with this is that it also does NOT have a way to give it an intent instance as it attempts to instantiate an instance inside the method itself. The method expects to be called from a button or other method that is stored on the UI and called from it. However, since it is called from the onMethodCall method in the Flutter plugin, it does not seem to work. I have attempted many workarounds such as adding a method inside the Activity and running the code inside while calling it from the flutter plugin class. I have also tried using the UIThread and no luck either. Any workarounds?
Note: I have not provided any code due to keeping this API hidden. It should only be known that I am running the code from the onMethodCall event.
Error: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
You can extend your plugin to implement ActivityAware in your plugin class, when you implement it, you get a couple of callbacks that gives you the current activity. Like this :
lateinit activity: Activity? = null
override fun onDetachedFromActivity() {
activity = null
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onDetachedFromActivityForConfigChanges() {
activity = null
}
After that you can just startActivity from the assigned activity variable.
Let me know if you need further help.
As you mentioned, For Flutter plugin any platform-dependent logics should be kept in the subclass of FlutterActivity which was used to show flutter module/screens inside a native module. Now you can launch intent from that subclass without any additional flags.
#note - Subclass of FlutterActvity should be kept in the native module.
class FlutterResponseActivity : FlutterActivity() {
private var methodResult: Result? = null
override fun provideFlutterEngine(context: Context): FlutterEngine? {
return MyApplication.mContext.flutterEngine //Pre-warmed flutter engine
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
"startMainActivity" -> {
startMainActivity()
result.success(true)
}
else -> result.notImplemented()
}
}
}
private fun startMainActivity() {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
}