chrome_safari_browser.dart 4.82 KB
import 'dart:async';
import 'dart:io';

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

import 'types.dart';
import 'in_app_browser.dart';

///ChromeSafariBrowser class.
///
///This class uses native [Chrome Custom Tabs](https://developer.android.com/reference/android/support/customtabs/package-summary) on Android
///and [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS.
///
///[browserFallback] represents the [InAppBrowser] instance fallback in case `Chrome Custom Tabs`/`SFSafariViewController` is not available.
class ChromeSafariBrowser {
  String uuid;
  InAppBrowser browserFallback;
  bool _isOpened = false;
  MethodChannel _channel;
  static const MethodChannel _sharedChannel = const MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser');

  ///Initialize the [ChromeSafariBrowser] instance with an [InAppBrowser] fallback instance or `null`.
  ChromeSafariBrowser({bFallback}) {
    uuid = uuidGenerator.v4();
    browserFallback = bFallback;
    this._channel =
        MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser_$uuid');
    this._channel.setMethodCallHandler(handleMethod);
    _isOpened = false;
  }

  Future<dynamic> handleMethod(MethodCall call) async {
    switch (call.method) {
      case "onChromeSafariBrowserOpened":
        onOpened();
        break;
      case "onChromeSafariBrowserCompletedInitialLoad":
        onCompletedInitialLoad();
        break;
      case "onChromeSafariBrowserClosed":
        onClosed();
        this._isOpened = false;
        break;
      default:
        throw UnimplementedError("Unimplemented ${call.method} method");
    }
  }

  ///Opens an [url] in a new [ChromeSafariBrowser] instance.
  ///
  ///[url]: The [url] to load. Call [encodeUriComponent()] on this if the [url] contains Unicode characters.
  ///
  ///[options]: Options for the [ChromeSafariBrowser].
  ///
  ///[headersFallback]: The additional header of the [InAppBrowser] instance fallback to be used in the HTTP request for this URL, specified as a map from name to value.
  ///
  ///[optionsFallback]: Options used by the [InAppBrowser] instance fallback.
  Future<void> open(
      {@required String url,
      ChromeSafariBrowserClassOptions options,
      Map<String, String> headersFallback = const {},
      InAppBrowserClassOptions optionsFallback}) async {
    assert(url != null && url.isNotEmpty);
    this.throwIsAlreadyOpened(message: 'Cannot open $url!');

    Map<String, dynamic> optionsMap = {};
    if (Platform.isAndroid)
      optionsMap.addAll(options.android?.toMap() ?? {});
    else if (Platform.isIOS)
      optionsMap.addAll(options.ios?.toMap() ?? {});

    Map<String, dynamic> optionsFallbackMap = {};
    if (optionsFallback != null) {
      optionsFallbackMap
          .addAll(optionsFallback.crossPlatform?.toMap() ?? {});
      optionsFallbackMap.addAll(optionsFallback
              .inAppWebViewWidgetOptions?.crossPlatform
              ?.toMap() ??
          {});
      if (Platform.isAndroid) {
        optionsFallbackMap
            .addAll(optionsFallback.android?.toMap() ?? {});
        optionsFallbackMap.addAll(optionsFallback
                .inAppWebViewWidgetOptions?.android
                ?.toMap() ??
            {});
      } else if (Platform.isIOS) {
        optionsFallbackMap
            .addAll(optionsFallback.ios?.toMap() ?? {});
        optionsFallbackMap.addAll(optionsFallback
                .inAppWebViewWidgetOptions?.ios
                ?.toMap() ??
            {});
      }
    }

    Map<String, dynamic> args = <String, dynamic>{};
    args.putIfAbsent('uuid', () => uuid);
    args.putIfAbsent('url', () => url);
    args.putIfAbsent('options', () => optionsMap);
    args.putIfAbsent('uuidFallback',
            () => (browserFallback != null) ? browserFallback.uuid : '');
    args.putIfAbsent('headersFallback', () => headersFallback);
    args.putIfAbsent('optionsFallback', () => optionsFallbackMap);
    await _sharedChannel.invokeMethod('open', args);
    this._isOpened = true;
  }

  ///Event fires when the [ChromeSafariBrowser] is opened.
  void onOpened() {}

  ///Event fires when the initial URL load is complete.
  void onCompletedInitialLoad() {}

  ///Event fires when the [ChromeSafariBrowser] is closed.
  void onClosed() {}

  ///Returns `true` if the [ChromeSafariBrowser] instance is opened, otherwise `false`.
  bool isOpened() {
    return this._isOpened;
  }

  void throwIsAlreadyOpened({String message = ''}) {
    if (this.isOpened()) {
      throw Exception([
        'Error: ${(message.isEmpty) ? '' : message + ' '}The browser is already opened.'
      ]);
    }
  }

  void throwIsNotOpened({String message = ''}) {
    if (!this.isOpened()) {
      throw Exception([
        'Error: ${(message.isEmpty) ? '' : message + ' '}The browser is not opened.'
      ]);
    }
  }
}