blob: 425dc886d29fe2c5641af7cc4b912a79a9d69e7d [file] [log] [blame]
// 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 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
/// Signature for a function provided by the [Link] widget that instructs it to
/// follow the link.
typedef FollowLink = Future<void> Function();
/// Signature for a builder function passed to the [Link] widget to construct
/// the widget tree under it.
typedef LinkWidgetBuilder = Widget Function(
BuildContext context,
FollowLink followLink,
);
/// Signature for a delegate function to build the [Link] widget.
typedef LinkDelegate = Widget Function(LinkInfo linkWidget);
final MethodCodec _codec = const JSONMethodCodec();
/// Defines where a Link URL should be open.
///
/// This is a class instead of an enum to allow future customizability e.g.
/// opening a link in a specific iframe.
class LinkTarget {
/// Const private constructor with a [debugLabel] to allow the creation of
/// multiple distinct const instances.
const LinkTarget._({this.debugLabel});
/// Used to distinguish multiple const instances of [LinkTarget].
final String debugLabel;
/// Use the default target for each platform.
///
/// On Android, the default is [blank]. On the web, the default is [self].
///
/// iOS, on the other hand, defaults to [self] for web URLs, and [blank] for
/// non-web URLs.
static const defaultTarget = LinkTarget._(debugLabel: 'defaultTarget');
/// On the web, this opens the link in the same tab where the flutter app is
/// running.
///
/// On Android and iOS, this opens the link in a webview within the app.
static const self = LinkTarget._(debugLabel: 'self');
/// On the web, this opens the link in a new tab or window (depending on the
/// browser and user configuration).
///
/// On Android and iOS, this opens the link in the browser or the relevant
/// app.
static const blank = LinkTarget._(debugLabel: 'blank');
}
/// Encapsulates all the information necessary to build a Link widget.
abstract class LinkInfo {
/// Called at build time to construct the widget tree under the link.
LinkWidgetBuilder get builder;
/// The destination that this link leads to.
Uri get uri;
/// The target indicating where to open the link.
LinkTarget get target;
/// Whether the link is disabled or not.
bool get isDisabled;
}
/// Pushes the [routeName] into Flutter's navigation system via a platform
/// message.
Future<ByteData> pushRouteNameToFramework(
BuildContext context,
String routeName, {
@visibleForTesting bool debugForceRouter = false,
}) {
final Completer<ByteData> completer = Completer<ByteData>();
if (debugForceRouter || _hasRouter(context)) {
SystemNavigator.routeInformationUpdated(location: routeName);
window.onPlatformMessage(
'flutter/navigation',
_codec.encodeMethodCall(
MethodCall('pushRouteInformation', <dynamic, dynamic>{
'location': routeName,
'state': null,
}),
),
completer.complete,
);
} else {
window.onPlatformMessage(
'flutter/navigation',
_codec.encodeMethodCall(MethodCall('pushRoute', routeName)),
completer.complete,
);
}
return completer.future;
}
bool _hasRouter(BuildContext context) {
try {
return Router.of(context) != null;
} on AssertionError {
// When a `Router` can't be found, an assertion error is thrown.
return false;
}
}