| // 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:url_launcher_platform_interface/link.dart'; |
| import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; |
| |
| import 'types.dart'; |
| import 'url_launcher_uri.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 { |
| /// Creates a widget that renders a real link on the web, and uses WebViews in |
| /// native platforms to open links. |
| const Link({ |
| super.key, |
| required this.uri, |
| this.target = LinkTarget.defaultTarget, |
| required this.builder, |
| }); |
| |
| /// Called at build time to construct the widget tree under the link. |
| @override |
| final LinkWidgetBuilder builder; |
| |
| /// The destination that this link leads to. |
| @override |
| final Uri? uri; |
| |
| /// The target indicating where to open the link. |
| @override |
| final LinkTarget target; |
| |
| /// Whether the link is disabled or not. |
| @override |
| bool get isDisabled => uri == null; |
| |
| 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, {super.key}); |
| |
| /// 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 { |
| final Uri url = link.uri!; |
| if (!url.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 |
| // `launchUrl` API to open the link. |
| bool success; |
| try { |
| success = await launchUrl( |
| url, |
| mode: _useWebView |
| ? LaunchMode.inAppWebView |
| : LaunchMode.externalApplication, |
| ); |
| } on PlatformException { |
| success = false; |
| } |
| if (!success) { |
| FlutterError.reportError(FlutterErrorDetails( |
| exception: 'Could not launch link $url', |
| 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), |
| ); |
| } |
| } |