Flutter Intents

Intents in Flutter OR Flutter Intents:

In Android, there are two main use cases for Intents: navigating between Activities within the app and communicating with components within the device. Flutter,  there is no concept of intents, But we can start intents functionality in flutter through native integrations by using plugins.

Flutter doesn’t really have a direct equivalent to activities and fragments. In Flutter you can navigate between screens, using a Navigator and Routes, all within the same Activity.

A Route is an abstraction for a “screen” or “page” of an app, and a Navigator is a widget that manages routes. A route roughly maps to an Activity, but the meaning is different it just maps an activity. A navigator can push and pop routes between the  screens. Navigators work like a stack on which you can push() new routes you want to navigate to, and from which you can pop() routes when you want to “go back”.

In Android, you declare your activities inside the app’s AndroidManifest.xml.

In Flutter, you have a couple options to navigate between pages:

  • Specify a Map of route names. (MaterialApp)
  • Directly navigate to a route. (WidgetApp)

The below  example builds a Map.

void main() {
runApp(MaterialApp(
home: MyAppHome(), // becomes the route named '/'
routes: <String, WidgetBuilder> {
'/a': (BuildContext context) => MyPage(title: 'page A'),
'/b': (BuildContext context) => MyPage(title: 'page B'),
'/c': (BuildContext context) => MyPage(title: 'page C'),
},
));
}

Navigate to a route by pushing its name to the Navigator

Navigator.of(context).pushNamed('/b');

The Intent for external activity like we use  Intents   to call external components such as a Camera or File picker. For this, you would need to create a native platform integration by using existing plugins.

Handling incoming intents from external applications in Flutter?

Flutter will handle incoming intents from Android directly by talking to the Android layer and requesting the data that was shared.

The below example registers a text share intent filter on the native activity and  runs our Flutter code, then other apps can share text with our Flutter app.

It is importent that first we need to handle the shared text data on the Android native side means in our Activity class and then wait until Flutter requests for the data to provide it using a MethodChannel.

 

First In, AndroidManifest.xml we are registering all the intent filter :

<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- ... -->
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>

Then in MainActivity, we are handling  the intent, extract the text that was shared from the intent, and hold onto it. Until  Flutter is ready to process, Flutter requests the data using a platform channel, and then  it’s sent across from the native side:

package com.example.shared;

import android.content.Intent;
import android.os.Bundle;

import java.nio.ByteBuffer;

import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.ActivityLifecycleListener;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {

private String sharedText;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();

if (Intent.ACTION_SEND.equals(action) && type != null) {
if ("text/plain".equals(type)) {
handleSendText(intent); // Handle text being sent
}
}

MethodChannel(getFlutterView(), "app.channel.shared.data")
.setMethodCallHandler(MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
if (methodCall.method.contentEquals("getSharedText")) {
result.success(sharedText);
sharedText = null;
}
}
});
}

void handleSendText(Intent intent) {
sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
}
}

Lastly, request the data from the Flutter App side when the widget is rendered:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
runApp(SampleApp());
}

class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample Shared App Handler',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}

class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);

@override
_SampleAppPageState createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
static const platform = const MethodChannel('app.channel.shared.data');
String dataShared = "No data";

@override
void initState() {
super.initState();
getSharedText();
}

@override
Widget build(BuildContext context) {
return Scaffold(body: Center(child: Text(dataShared)));
}

getSharedText() async {
var sharedData = await platform.invokeMethod("getSharedText");
if (sharedData != null) {
setState(() {
dataShared = sharedData;
});
}
}
}

_____________________________________________________________________________

Flutter Intents Example 2:

 

import 'package:android_intent/android_intent.dart';
import 'package:flutter/material.dart';
import 'package:platform/platform.dart';

void main() {
runApp(new MyApp());
}

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
routes: <String, WidgetBuilder>{
ExplicitIntentsWidget.routeName: (BuildContext context) =>
const ExplicitIntentsWidget()
},

);
}
}

class MyHomePage extends StatelessWidget {
void _createAlarm() {
final AndroidIntent intent = const AndroidIntent(
action: 'android.intent.action.SET_ALARM',
arguments: const <String, dynamic>{
'android.intent.extra.alarm.DAYS': const <int>[2, 3, 4, 5, 6],
'android.intent.extra.alarm.HOUR': 21,
'android.intent.extra.alarm.MINUTES': 30,
'android.intent.extra.alarm.SKIP_UI': true,
'android.intent.extra.alarm.MESSAGE': 'Create a Flutter app',
},
);
intent.launch();
}

void _openExplicitIntentsView(BuildContext context) {
Navigator.of(context).pushNamed(ExplicitIntentsWidget.routeName);
}

@override
Widget build(BuildContext context) {
Widget body;
if (const LocalPlatform().isAndroid) {
body = new Padding(
padding: const EdgeInsets.symmetric(vertical: 15.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new RaisedButton(
child: const Text(
'Tap here to set an alarm\non weekdays at 9:30pm.'),
onPressed: _createAlarm,
),
new RaisedButton(
child: const Text('Tap here to test explicit intents.'),
onPressed: () => _openExplicitIntentsView(context)),
],
),
);
}
else {
body = const Text('This plugin only works with Android');
}

return new Scaffold(
appBar: new AppBar(
title: const Text('Plugin example app'),
),
body: new Center(child: body),
);
}
}

class ExplicitIntentsWidget extends StatelessWidget {
static const String routeName = "/explicitIntents";

const ExplicitIntentsWidget();

void _openGoogleMapsStreetView() {
final AndroidIntent intent = new AndroidIntent(
action: 'action_view',
data: Uri.encodeFull('google.streetview:cbll=46.414382,10.013988'),
package: 'com.google.android.apps.maps');
intent.launch();
}

void _displayMapInGoogleMaps({int zoomLevel: 12}) {
final AndroidIntent intent = new AndroidIntent(
action: 'action_view',
data: Uri.encodeFull('geo:37.7749,-122.4194?z=$zoomLevel'),
package: 'com.google.android.apps.maps');
intent.launch();
}

void _launchTurnByTurnNavigationInGoogleMaps() {
final AndroidIntent intent = new AndroidIntent(
action: 'action_view',
data: Uri.encodeFull(
'google.navigation:q=Taronga+Zoo,+Sydney+Australia&avoid=tf'),
package: 'com.google.android.apps.maps');
intent.launch();
}

void _openLinkInGoogleChrome() {
final AndroidIntent intent = new AndroidIntent(
action: 'action_view',
data: Uri.encodeFull('https://flutter.io'),
package: 'com.android.chrome');
intent.launch();
}

void _testExplicitIntentFallback() {
final AndroidIntent intent = new AndroidIntent(
action: 'action_view',
data: Uri.encodeFull('https://flutter.io'),
package: 'com.android.chrome.implicit.fallback');
intent.launch();
}

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: const Text('Test explicit intents'),
),

body: new Center(
child: new Padding(
padding: const EdgeInsets.symmetric(vertical: 15.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new RaisedButton(
child: const Text(
'Tap here to display panorama\nimagery in Google Street View.'),
onPressed: _openGoogleMapsStreetView,
),

new RaisedButton(
child: const Text('Tap here to display\na map in Google Maps.'),
onPressed: _displayMapInGoogleMaps,
),

new RaisedButton(
child: const Text(
'Tap here to launch turn-by-turn\nnavigation in Google Maps.'),
onPressed: _launchTurnByTurnNavigationInGoogleMaps,
),
new RaisedButton(
child: const Text('Tap here to open link in Google Chrome.'),
onPressed: _openLinkInGoogleChrome,
),

new RaisedButton(
child: const Text(
'Tap here to test explicit intent fallback to implicit.'),
onPressed: _testExplicitIntentFallback,
),

],
),
),
),
);
}
}

Congratulations You Have Learned About Flutter Intents

Leave a Reply

Categories