plugin_registry.dart 4.74 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'dart:ui' as ui;

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

typedef _MessageHandler = Future<ByteData> Function(ByteData);

/// This class registers web platform plugins.
class PluginRegistry {
  /// Creates a plugin registry.
  PluginRegistry(this._binaryMessenger);

  final BinaryMessenger _binaryMessenger;

  /// Creates a registrar for the given plugin implementation class.
  Registrar registrarFor(Type key) => Registrar(_binaryMessenger);

  /// Registers this plugin handler with the engine, so that unrecognized
  /// platform messages are forwarded to the registry, where they can be
  /// correctly dispatched to one of the registered plugins.
  void registerMessageHandler() {
    // The function below is only defined in the Web dart:ui.
    // ignore: undefined_function
    ui.webOnlySetPluginHandler(_binaryMessenger.handlePlatformMessage);
  }
}

/// A registrar for a particular plugin.
///
/// Gives access to a [BinaryMessenger] which has been configured to receive
/// platform messages from the framework side.
class Registrar {
  /// Creates a registrar with the given [BinaryMessenger].
  Registrar(this.messenger);

  /// A [BinaryMessenger] configured to receive platform messages from the
  /// framework side.
  ///
  /// Use this [BinaryMessenger] when creating platform channels in order for
  /// them to receive messages from the platform side. For example:
  ///
  ///
  ///     class MyPlugin {
  ///       static void registerWith(Registrar registrar) {
  ///         final MethodChannel channel = MethodChannel(
  ///             'com.my_plugin/my_plugin',
  ///             const StandardMethodCodec(),
  ///             registrar.messenger);
  ///         final MyPlugin instance = MyPlugin();
  ///         channel.setMethodCallHandler(instance.handleMethodCall);
  ///       }
  ///       ...
  ///     }
  final BinaryMessenger messenger;
}

/// The default plugin registry for the web.
final PluginRegistry webPluginRegistry = PluginRegistry(pluginBinaryMessenger);

/// A [BinaryMessenger] which does the inverse of the default framework
/// messenger.
///
/// Instead of sending messages from the framework to the engine, this
/// receives messages from the framework and dispatches them to registered
/// plugins.
class _PlatformBinaryMessenger extends BinaryMessenger {
  final Map<String, _MessageHandler> _handlers = <String, _MessageHandler>{};

  /// Receives a platform message from the framework.
  @override
  Future<void> handlePlatformMessage(String channel, ByteData data,
      ui.PlatformMessageResponseCallback callback) async {
    ByteData response;
    try {
      final MessageHandler handler = _handlers[channel];
      if (handler != null) {
        response = await handler(data);
gaaclarke's avatar
gaaclarke committed
83 84
      } else {
        ui.channelBuffers.push(channel, data, callback);
85
        callback = null;
86 87 88 89 90 91 92 93 94
      }
    } catch (exception, stack) {
      FlutterError.reportError(FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'flutter web shell',
        context: ErrorDescription('during a plugin platform message call'),
      ));
    } finally {
95 96 97
      if (callback != null) {
        callback(response);
      }
98 99 100 101 102 103
    }
  }

  /// Sends a platform message from the platform side back to the framework.
  @override
  Future<ByteData> send(String channel, ByteData message) {
104 105 106 107 108 109 110 111 112 113 114 115 116 117
    final Completer<ByteData> completer = Completer<ByteData>();
    ui.window.onPlatformMessage(channel, message, (ByteData reply) {
      try {
        completer.complete(reply);
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'flutter web shell',
          context: ErrorDescription('during a plugin-to-framework message'),
        ));
      }
    });
    return completer.future;
118 119 120 121 122 123 124 125 126
  }

  @override
  void setMessageHandler(
      String channel, Future<ByteData> Function(ByteData message) handler) {
    if (handler == null)
      _handlers.remove(channel);
    else
      _handlers[channel] = handler;
gaaclarke's avatar
gaaclarke committed
127 128 129
    ui.channelBuffers.drain(channel, (ByteData data, ui.PlatformMessageResponseCallback callback) async {
      await handlePlatformMessage(channel, data, callback);
    });
130 131 132 133 134 135 136 137 138 139 140 141
  }

  @override
  void setMockMessageHandler(
      String channel, Future<ByteData> Function(ByteData message) handler) {
    throw FlutterError(
        'Setting mock handlers is not supported on the platform side.');
  }
}

/// The default [BinaryMessenger] for Flutter Web plugins.
final BinaryMessenger pluginBinaryMessenger = _PlatformBinaryMessenger();