This guide describes how to connect your Flutter app to platform-specific or third-party services that are available on iOS and Android devices.

Introduction

Mobile devices offer many capabilities to extend your app through platform-specific APIs, or APIs from third-party vendors. Flutter offers the following ways to integrate platform-specific services when building your app using the Flutter framework.

  • First-party services for Flutter: For frequently used services, the Flutter SDK provides first-party APIs to access system services across platforms. You can call these APIs directly in your Flutter app by importing the ‘flutter/services’ package.
  • Platform and Third-party Services for Flutter: Flutter provides the PlatformMessages class which allows your Flutter user interface (UI) code to asynchronously exchange application-specific messages with the portions of your app that run on iOS or Android devices. This flexible approach lets you invoke platform-specific and third-party APIs on iOS and Android devices, while keeping a common UI codebase.

Understand how host message passing works

You can use the PlatformMessages class in the Flutter UI portion of your app to send messages to a host (that is, the iOS or Android portion of your app that uses a shared UI codebase built with Flutter). The PlatformMessages class lets you implement your own integrations with platform-specific services; you can send a request message to the host to call a platform API then asynchronously retrieve the service data via a response message from that host.

Your message must consist of string values. Message passing with the PlatformMessages class is asynchronous, bi-directional, and supports optional reply routing.

The services that you invoke using the PlatformMessages class must return non-graphical, data-only information. Examples include:

  • Geolocation
  • Contacts
  • File system
  • Network information
  • Sensors

You can also use the PlatformMessages class to invoke APIs from third-party SDKs that run on iOS and Android devices.

To communicate between the PlatformMessages class and platform-specific APIs in the host, you must add the following Flutter components into the host’s view hierarchy.

Add a service to your app

To create additional platform-specific or third-party service integrations using message passing, follow these steps:

  1. Download the hello_services example. If your app needs to access platform-specific APIs on iOS or Android, you can download the hello_services example and use it as the starter code to build your own service integration. This reference example demonstrates how to connect to the geolocation service within an iOS or Android application by using the PlatformMessages class.

    The following diagram shows how the hello_services app accesses platform-specific services on iOS and Android.

    hello_services architecture

    Fig 1. Architecture of the hello_services example.

    The Flutter UI and host-specific components are related as follows:

    • In the Flutter UI portion of the app, the main app imports the flutter/services.dart package, which defines the PlatformMessages class. In the State class, the app makes a service request by calling the static methods of the PlatformMessages class. If the service request is successful, the app updates the UI accordingly with the data that the service returns.
    • On the iOS portion of the app, the AppDelegate class sets an instance of FlutterViewController as the root view controller of its UIWindow. The FlutterViewController class provides a bridge connecting the UIKit view hierarchy to the Flutter UI code. To use the FlutterViewController class, the iOS portion of the app must import the Flutter.h library.
    • On the Android portion of the app, the main activity sets as its content view an instance of the FlutterView class, which provides a bridge connecting the Android view hierarchy to the Flutter UI code. To use the FlutterView class, the Android portion of the app must specify the flutter gradle plugin dependency in its build.gradle file.
  2. Establish the service bindings. The PlatformMessages class establishes connections with services.

    You can register a callback for receiving messages through the PlatformMessages class by calling the setJSONMessageHandler() or setStringMessageHandler() static methods.

    For example, this snippet shows how you might initialize a service binding and register a message handler callback in the main() function.

    import 'dart:async';
    import 'dart:math';
    
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    final Random random = new Random();
    
    void main() {
      runApp(new Center(child: new Text('Random Number Generator')));
      PlatformMessages.setJSONMessageHandler('getRandom', handleGetRandom);
    }
    
    Future<dynamic> handleGetRandom(Map<String, dynamic> message) async {
      final double min = message['min'].toDouble();
      final double max = message['max'].toDouble();
    
      return <String, double>{
        'value': (random.nextDouble() * (max - min)) + min
      };
    }
    
  3. Add a service invocation call in the Flutter UI. In your State class, use the PlatformMessages class to call a target service on the host. You’ll typically peform this operation when the user performs a UI interaction.

    You can construct your service invocation call as a JSON-encoded string, then call the sendJSON method and pass this string as a parameter.

    For example, the following snippet shows how you might asynchronously invoke the geolocation service.

    class HelloServices extends StatefulWidget {
      @override
      _HelloServicesState createState() => new _HelloServicesState();
    }
    
    class _HelloServicesState extends State<HelloServices> {
      double _latitude;
      double _longitude;
    
      Future<Null> _getLocation() async {
        final Map<String, String> message
          = <String, String>{'provider': 'network'};
        final Map<String, dynamic> reply
          = await PlatformMessages.sendJSON('getLocation', message);
        // If the widget was removed from the tree while the message was
        // in flight, we want to discard the reply rather than
        // calling setState to update our non-existant appearance.
        if (!mounted)
          return;
        setState(() {
          _latitude = reply['latitude'].toDouble();
          _longitude = reply['longitude'].toDouble();
        });
      }
    
      @override
      Widget build(BuildContext context) {
        // ...
        return new Text('Latitude: $latitude, Longitude: $longitude');
      }
    }
    
  4. Add a service provider in the host (iOS). Follow these steps if your app needs to invoke a platform-specific or third-party API on iOS:

    1. In the example project, the ios/HelloServices/Runner folder contains the Objective-C source files for the iOS-specific part of the app. In Xcode, open this folder as an iOS project.
    2. In your iOS project, create a service provider to handle the service request message, locally invoke the service, and return a response message to the Flutter UI portion of the app. Your service provider should conform to one of these protocols:

      • FlutterMessageListener: Implement this protocol if the request message does not require a reply, or if the Flutter UI portion of your app can reply to the message synchronously.
      • FlutterAsyncMessageListener: Implement this protocol if the request message requires a reply and the code that creates the reply is asynchronous.

      For an example of how to implement a service provider, see the LocationProvider.m and AppDelegate.m classes from the example project.

    3. Create or modify a Flutter/Generated.xcconfig file to specify the configuration settings for building your iOS app. See the project README to learn more about the iOS-specific properties you can configure.
  5. Add a service provider in the host (Android). Follow these steps if your app needs to invoke a platform-specific or third-party API on Android:

    1. In the example project, the android/app folder contains the Java source files for the Android-specific portion of the app. The file organization in this folder conforms to the Android app module format for Gradle. In Android Studio, open the app folder as an Android project.
    2. In your Android project, create a service provider to handle the service request message, locally invoke the service, and return a response message to the Flutter UI portion of the app. For each service you want to invoke, attach a message listener to the FlutterView object and specify the service provider it calls when the onReply() event is triggered. For an example of how to implement a service provider, see the ExampleActivity.java class from the example project.
    3. Modify the AndroidManifest.xml file to add any permissions necessary to invoke your target service (for example, the geolocation service needs the ACCESS_FINE_LOCATION permission).
    4. Create or modify a local.properties file to specify the configuration settings for building your Android app. See the project README to learn more about the Android-specific properties you can configure.
  6. Build your app. You should build your app directly from the platform-specific tools.

    • On iOS: Build your app using Xcode.
    • On Android: Build your app using Android Studio or Gradle from the command-line.

    To learn how to build the app for a specific platform, see the hello_services README.

    The platform-specific build system creates the application package in the normal way. This approach eases the integration of third-party SDKs, which are designed to be integrated into the normal build process for each platform they support.