Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
Implement Link for native platforms (#3177)
Browse files Browse the repository at this point in the history
  • Loading branch information
mdebbar committed Oct 22, 2020
1 parent 30ee306 commit 0870628
Show file tree
Hide file tree
Showing 5 changed files with 418 additions and 3 deletions.
4 changes: 4 additions & 0 deletions packages/url_launcher/url_launcher/CHANGELOG.md
@@ -1,3 +1,7 @@
## 5.7.7

* Introduce the Link widget with an implementation for native platforms.

## 5.7.6

* Suppress deprecation warning on the `shouldOverrideUrlLoading` method on Android of the `FlutterWebChromeClient` class.
Expand Down
7 changes: 7 additions & 0 deletions packages/url_launcher/url_launcher/lib/link.dart
@@ -0,0 +1,7 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

export 'src/link.dart' show Link;
export 'package:url_launcher_platform_interface/link.dart'
show FollowLink, LinkTarget, LinkWidgetBuilder;
132 changes: 132 additions & 0 deletions packages/url_launcher/url_launcher/lib/src/link.dart
@@ -0,0 +1,132 @@
// Copyright 2017 The Chromium 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/widgets.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';

/// 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) => RaisedButton(
/// 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) => RaisedButton(
/// 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,
LinkTarget target,
@required this.builder,
}) : target = target ?? LinkTarget.defaultTarget,
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 null;
}

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();
return pushRouteNameToFramework(context, routeName);
}

// 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'),
));
}
return Future<void>.value(null);
}

@override
Widget build(BuildContext context) {
return link.builder(
context,
link.isDisabled ? null : () => _followLink(context),
);
}
}
6 changes: 3 additions & 3 deletions packages/url_launcher/url_launcher/pubspec.yaml
Expand Up @@ -2,7 +2,7 @@ name: url_launcher
description: Flutter plugin for launching a URL on Android and iOS. Supports
web, phone, SMS, and email schemes.
homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher
version: 5.7.6
version: 5.7.7

flutter:
plugin:
Expand All @@ -24,13 +24,13 @@ flutter:
dependencies:
flutter:
sdk: flutter
url_launcher_platform_interface: ^1.0.8
url_launcher_platform_interface: ^1.0.9
# The design on https://flutter.dev/go/federated-plugins was to leave
# this constraint as "any". We cannot do it right now as it fails pub publish
# validation, so we set a ^ constraint.
# TODO(amirh): Revisit this (either update this part in the design or the pub tool).
# https://github.com/flutter/flutter/issues/46264
url_launcher_web: ^0.1.3
url_launcher_web: ^0.1.5
url_launcher_linux: ^0.0.1
url_launcher_macos: ^0.0.1
url_launcher_windows: ^0.0.1
Expand Down

0 comments on commit 0870628

Please sign in to comment.