|
| 1 | +// Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style license that can be |
| 3 | +// found in the LICENSE file. |
| 4 | + |
| 5 | +import 'package:flutter/foundation.dart'; |
| 6 | +import 'package:flutter/material.dart'; |
| 7 | +import 'package:flutter/rendering.dart'; |
| 8 | +import 'package:flutter/services.dart'; |
| 9 | + |
| 10 | +typedef LinkWidgetBuilder = Widget Function( |
| 11 | + BuildContext context, |
| 12 | + VoidCallback onPressed, |
| 13 | +); |
| 14 | + |
| 15 | +typedef LinkDelegate = Widget Function(Link linkWidget); |
| 16 | + |
| 17 | +/// Defines where a Link URL should be open. |
| 18 | +/// |
| 19 | +/// This is a class instead of an enum to allow future customizability e.g. |
| 20 | +/// opening a link in a specific iframe. |
| 21 | +class LinkTarget { |
| 22 | + /// |
| 23 | + const LinkTarget({this.debugLabel}); |
| 24 | + |
| 25 | + /// |
| 26 | + final String debugLabel; |
| 27 | + |
| 28 | + /// Use the default target for each platform. |
| 29 | + /// |
| 30 | + /// On Android, the default is [blank]. On the web, the default is [self]. |
| 31 | + /// |
| 32 | + /// iOS, on the other hand, defaults to [self] for web URLs, and [blank] for |
| 33 | + /// non-web URLs. |
| 34 | + static const defaultTarget = LinkTarget(debugLabel: 'defaultTarget'); |
| 35 | + |
| 36 | + /// On the web, this opens the link in the same tab where the flutter app is |
| 37 | + /// running. |
| 38 | + /// |
| 39 | + /// On Android and iOS, this opens the link in a webview within the app. |
| 40 | + static const self = LinkTarget(debugLabel: 'self'); |
| 41 | + |
| 42 | + /// On the web, this opens the link in a new tab or window (depending on the |
| 43 | + /// browser and user configuration). |
| 44 | + /// |
| 45 | + /// On Android and iOS, this opens the link in the browser or the relevant |
| 46 | + /// app. |
| 47 | + static const blank = LinkTarget(debugLabel: 'blank'); |
| 48 | +} |
| 49 | + |
| 50 | +/// Used to override the delegate that builds the link. |
| 51 | +set linkDelegate(LinkDelegate delegate) { |
| 52 | + Link._linkDelegate = delegate; |
| 53 | +} |
| 54 | + |
| 55 | +/// |
| 56 | +/// When linking to an external [Uri] make sure to include the [Uri.scheme] as |
| 57 | +/// demonstrated in the example below. |
| 58 | +/// |
| 59 | +/// ```dart |
| 60 | +/// Link( |
| 61 | +/// uri: Uri.parse('https://flutter.dev'), |
| 62 | +/// // uri: Uri(scheme: 'https', host: 'flutter.dev'), |
| 63 | +/// builder: (BuildContext context, VoidCallback onPressed) { |
| 64 | +/// return MyCustomButton( |
| 65 | +/// onPressed: onPressed, |
| 66 | +/// // ... other properties here ... |
| 67 | +/// ); |
| 68 | +/// }, |
| 69 | +/// ); |
| 70 | +/// ``` |
| 71 | +class Link extends StatelessWidget { |
| 72 | + /// |
| 73 | + final LinkWidgetBuilder builder; |
| 74 | + |
| 75 | + /// |
| 76 | + final Uri uri; |
| 77 | + |
| 78 | + /// |
| 79 | + final LinkTarget target; |
| 80 | + |
| 81 | + /// |
| 82 | + bool get isDisabled => uri == null; |
| 83 | + |
| 84 | + static LinkDelegate _linkDelegate = (Link link) => _DefaultLinkDelegate(link); |
| 85 | + |
| 86 | + /// |
| 87 | + Link({ |
| 88 | + Key key, |
| 89 | + @required this.uri, |
| 90 | + LinkTarget target, |
| 91 | + @required this.builder, |
| 92 | + }) : target = target ?? LinkTarget.defaultTarget, |
| 93 | + super(key: key); |
| 94 | + |
| 95 | + /// |
| 96 | + factory Link.raisedButton({ |
| 97 | + Key key, |
| 98 | + Uri uri, |
| 99 | + LinkTarget target, |
| 100 | + ValueChanged<bool> onHighlightChanged, |
| 101 | + ButtonTextTheme textTheme, |
| 102 | + Color textColor, |
| 103 | + Color disabledTextColor, |
| 104 | + Color color, |
| 105 | + Color disabledColor, |
| 106 | + Color focusColor, |
| 107 | + Color hoverColor, |
| 108 | + Color highlightColor, |
| 109 | + Color splashColor, |
| 110 | + Brightness colorBrightness, |
| 111 | + double elevation, |
| 112 | + double focusElevation, |
| 113 | + double hoverElevation, |
| 114 | + double highlightElevation, |
| 115 | + double disabledElevation, |
| 116 | + EdgeInsetsGeometry padding, |
| 117 | + VisualDensity visualDensity, |
| 118 | + ShapeBorder shape, |
| 119 | + Clip clipBehavior = Clip.none, |
| 120 | + FocusNode focusNode, |
| 121 | + bool autofocus = false, |
| 122 | + MaterialTapTargetSize materialTapTargetSize, |
| 123 | + Duration animationDuration, |
| 124 | + Widget child, |
| 125 | + }) { |
| 126 | + return Link( |
| 127 | + key: key, |
| 128 | + uri: uri, |
| 129 | + target: target, |
| 130 | + builder: (context, onPressed) => RaisedButton( |
| 131 | + onPressed: onPressed, |
| 132 | + onHighlightChanged: onHighlightChanged, |
| 133 | + textTheme: textTheme, |
| 134 | + textColor: textColor, |
| 135 | + disabledTextColor: disabledTextColor, |
| 136 | + color: color, |
| 137 | + disabledColor: disabledColor, |
| 138 | + focusColor: focusColor, |
| 139 | + hoverColor: hoverColor, |
| 140 | + highlightColor: highlightColor, |
| 141 | + splashColor: splashColor, |
| 142 | + colorBrightness: colorBrightness, |
| 143 | + elevation: elevation, |
| 144 | + focusElevation: focusElevation, |
| 145 | + hoverElevation: hoverElevation, |
| 146 | + highlightElevation: highlightElevation, |
| 147 | + disabledElevation: disabledElevation, |
| 148 | + padding: padding, |
| 149 | + visualDensity: visualDensity, |
| 150 | + shape: shape, |
| 151 | + clipBehavior: clipBehavior, |
| 152 | + focusNode: focusNode, |
| 153 | + autofocus: autofocus, |
| 154 | + materialTapTargetSize: materialTapTargetSize, |
| 155 | + animationDuration: animationDuration, |
| 156 | + child: child, |
| 157 | + ), |
| 158 | + ); |
| 159 | + } |
| 160 | + |
| 161 | + @override |
| 162 | + Widget build(BuildContext context) { |
| 163 | + return _linkDelegate(this); |
| 164 | + } |
| 165 | +} |
| 166 | + |
| 167 | +class _DefaultLinkDelegate extends StatelessWidget { |
| 168 | + const _DefaultLinkDelegate(this.link); |
| 169 | + |
| 170 | + final Link link; |
| 171 | + |
| 172 | + void _onPressed() { |
| 173 | + // The default link delegate uses url launcher when it's pressed. |
| 174 | + // TODO: use url_launcher. |
| 175 | + } |
| 176 | + |
| 177 | + @override |
| 178 | + Widget build(BuildContext context) { |
| 179 | + return link.builder(context, _onPressed); |
| 180 | + } |
| 181 | +} |
0 commit comments