mirrored_repositories/awesome-flutter/examples/plugins/packages/url_launcher/url_launcher/lib
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher_platform_interface/link.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';

/// The function used to push routes to the Flutter framework.
@visibleForTesting
Future<ByteData> Function(Object?, String) pushRouteToFrameworkFunction =
    pushRouteNameToFramework;

/// A widget that renders a real link on the web, and uses WebViews in native
/// platforms to open links.
///
/// Example link to an external URL:
///
/// ```dart
/// Link(
///   uri: Uri.parse('https://flutter.dev'),
///   builder: (BuildContext context, FollowLink followLink) => ElevatedButton(
///     onPressed: followLink,
///     // ... other properties here ...
///   )},
/// );
/// ```
///
/// Example link to a route name within the app:
///
/// ```dart
/// Link(
///   uri: Uri.parse('/home'),
///   builder: (BuildContext context, FollowLink followLink) => ElevatedButton(
///     onPressed: followLink,
///     // ... other properties here ...
///   )},
/// );
/// ```
class Link extends StatelessWidget implements LinkInfo {
  /// Called at build time to construct the widget tree under the link.
  final LinkWidgetBuilder builder;

  /// The destination that this link leads to.
  final Uri? uri;

  /// The target indicating where to open the link.
  final LinkTarget target;

  /// Whether the link is disabled or not.
  bool get isDisabled => uri == null;

  /// Creates a widget that renders a real link on the web, and uses WebViews in
  /// native platforms to open links.
  Link({
    Key? key,
    required this.uri,
    this.target = LinkTarget.defaultTarget,
    required this.builder,
  }) : super(key: key);

  LinkDelegate get _effectiveDelegate {
    return UrlLauncherPlatform.instance.linkDelegate ??
        DefaultLinkDelegate.create;
  }

  @override
  Widget build(BuildContext context) {
    return _effectiveDelegate(this);
  }
}

/// The default delegate used on non-web platforms.
///
/// For external URIs, it uses url_launche APIs. For app route names, it uses
/// event channel messages to instruct the framework to push the route name.
class DefaultLinkDelegate extends StatelessWidget {
  /// Creates a delegate for the given [link].
  const DefaultLinkDelegate(this.link);

  /// Given a [link], creates an instance of [DefaultLinkDelegate].
  ///
  /// This is a static method so it can be used as a tear-off.
  static DefaultLinkDelegate create(LinkInfo link) {
    return DefaultLinkDelegate(link);
  }

  /// Information about the link built by the app.
  final LinkInfo link;

  bool get _useWebView {
    if (link.target == LinkTarget.self) return true;
    if (link.target == LinkTarget.blank) return false;
    return false;
  }

  Future<void> _followLink(BuildContext context) async {
    if (!link.uri!.hasScheme) {
      // A uri that doesn't have a scheme is an internal route name. In this
      // case, we push it via Flutter's navigation system instead of letting the
      // browser handle it.
      final String routeName = link.uri.toString();
      await pushRouteToFrameworkFunction(context, routeName);
      return;
    }

    // At this point, we know that the link is external. So we use the `launch`
    // API to open the link.
    final String urlString = link.uri.toString();
    if (await canLaunch(urlString)) {
      await launch(
        urlString,
        forceSafariVC: _useWebView,
        forceWebView: _useWebView,
      );
    } else {
      FlutterError.reportError(FlutterErrorDetails(
        exception: 'Could not launch link $urlString',
        stack: StackTrace.current,
        library: 'url_launcher',
        context: ErrorDescription('during launching a link'),
      ));
    }
  }

  @override
  Widget build(BuildContext context) {
    return link.builder(
      context,
      link.isDisabled ? null : () => _followLink(context),
    );
  }
}
