Flutter: Intent

Viewed 335

I try to call an Intent request from my Flutter/Android project. As described, it should be done as follows

Intent intent = ​new ​Intent();
intent.setComponent(​new ​ComponentName(​"sk.co.xxx.yyy"​, "sk.co.xxx.yyy.MainActivity"​));
JSONObject jReq = ​new ​JSONObject();
String sReq=​""​;
jReq.put(​"Amount"​,​<Amount>​); 
jReq.put(​"Operation"​,​<Operation>​);
jReq.put(​"TransactionID"​,<can be generated e.g.getRandom()>); 
sReq = jReq.toString();

if​(sReq.isEmpty())​return​; 
intent.putExtra(​"POS_EMULATOR_EXTRA"​, sReq); try ​{
startActivityForResult(intent, ​<requestCode>​); }

How to implement this code under Flutter? I tried to use android_intent package from flutter.dev but i I get the following error message:

E/MethodChannel#plugins.flutter.io/android_intent(22815): Failed to handle method call
E/MethodChannel#plugins.flutter.io/android_intent(22815): android.content.ActivityNotFoundException: No Activity found to handle Intent 

Thanks for any answers!

my Flutter code, to replace the code above, is:

if (Platform.isAndroid) {
    Map data = {
      "Amount": "$amount",
      "Operation": "$operation"
    };
  AndroidIntent intent = AndroidIntent(
    componentName: "sk.co.xxx.yyy"​,
    data: data.toString()
  );
  await intent.launch();

I see, that "MainActivity" and putExtra(​"POS_EMULATOR_EXTRA" are not in my Flutter code, but i don't now, how i can implement...

3 Answers

The actual answer can be found in the AndroidManifest.xml, because ActivityNotFoundException can be taken fairly literal... and it means that either Java package or class name cannot be resolved.

String packageName = getApplicationContext().getPackageName(); // just for example
String className = packageName + ".SomeActivity";              // just for example
Intent intent = new Intent(Intent.ACTION_MAIN);                // just for example
intent.setComponent(new ComponentName(packageName, className));

try {
   startActivity(intent);
} catch (ActivityNotFoundException e) {
    Log.e(LOG_TAG, e.getMessage());

    // in here one still could perform a fall-back operation
}

The Activity to resolve for also must have android:exported="true" declared.
It's not wrong to use try / catch there, especially when it's not the own package.

The crucial part is the intent-filter element; when this doesn't match, it will not resolve for, no matter if the Activity exists and is exported - this all is based on the way how intent filters work.

In the Flutter code you wrote, the package argument is missing, and I guess that the data argument should be replaced by arguments

With the snippet below

 AndroidIntent intent = const AndroidIntent(
      package: 'sk.co.xxx.yyy',
      componentName: 'sk.co.xxx.yyy.MainActivity',
      action: 'action_main',
      arguments: {"POS_EMULATOR_EXTRA" : "{ 'yourJsonKey': 'yourJsonValue'"}
 );
 await intent.launch();

You should be good to go.

Just make sure that your MainActivity has the android:exported="true" flag set in the AndroidManifest.xml, and that your datas are correctly formatted (I'm not sure about your data.toString())

For the flutter portion, the original attempt wasn't too far off and most likely not the cause of the error. Here is an updated snippet that should construct the proper intent.

if (Platform.isAndroid) {
  final extras = json.encode({
    "Amount": amount,
    "Operation": operation,
    "TransactionID": transActionId,
  });
  final intent = AndroidIntent(
    action: 'action_view',
    package: 'sk.co.xxx.yyy',
    componentName: 'sk.co.xxx.yyy.MainActivity',
    arguments: {"POS_EMULATOR_EXTRA": extras},
  );
  await intent.launch();
}

The more likely cause is a problem with the destination app's AndroidManifest.xml. The activity must be exported so external applications can launch it, and it must also have an intent-filter that matches your incoming intent. For this example, I am using the action_view which maps to android.intent.action.VIEW since the intent is carrying data that will result in something being shown to the user. You can also use action_main to match with android.intent.action.MAIN if you prefer that instead.

<activity 
  android:name=".MainActivity"
  android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</activity>

Related