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

Commit 4e0d8a1

Browse files
committed
Support Link using platform views
1 parent 6ca0171 commit 4e0d8a1

5 files changed

Lines changed: 463 additions & 6 deletions

File tree

packages/url_launcher/url_launcher/example/lib/main.dart

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
// ignore_for_file: public_member_api_docs
66

77
import 'dart:async';
8+
import 'dart:math';
89

9-
import 'package:flutter/material.dart';
10+
import 'package:flutter/material.dart' hide Link;
11+
import 'package:flutter/rendering.dart';
1012
import 'package:url_launcher/url_launcher.dart';
1113

1214
void main() {
@@ -137,14 +139,47 @@ class _MyHomePageState extends State<MyHomePage> {
137139
Padding(
138140
padding: const EdgeInsets.all(16.0),
139141
child: TextField(
140-
onChanged: (String text) => _phone = text,
142+
onChanged: (String text) => setState(() {
143+
_phone = text;
144+
}),
141145
decoration: const InputDecoration(
142146
hintText: 'Input the phone number to launch')),
143147
),
144-
RaisedButton(
145-
onPressed: () => setState(() {
146-
_launched = _makePhoneCall('tel:$_phone');
147-
}),
148+
Link(
149+
uri: Uri.parse('https://google.com'),
150+
target: LinkTarget.blank,
151+
builder: (context, onPressed) => Padding(
152+
padding: EdgeInsets.all(20.0),
153+
child: RaisedButton(
154+
mouseCursor: SystemMouseCursors.forbidden,
155+
onPressed: () {
156+
// Do extra logic here (e.g. logging/analytics, etc).
157+
onPressed();
158+
},
159+
child: Container(
160+
color: Colors.pink,
161+
child: Text('{Link} Google'),
162+
),
163+
),
164+
),
165+
),
166+
// Link(
167+
// uri: Uri.parse('https://google.com'),
168+
// builder: (context, onPressed) => FloatingActionButton(
169+
// shape: ShapeBorder(),
170+
// onPressed: () {
171+
// // Do extra logic here (e.g. logging/analytics, etc).
172+
// onPressed();
173+
// },
174+
// child: Container(
175+
// color: Colors.pink,
176+
// child: Text('{Link} Google'),
177+
// ),
178+
// ),
179+
// ),
180+
Link.raisedButton(
181+
uri: Uri.parse('tel:$_phone'),
182+
color: Colors.pink,
148183
child: const Text('Make phone call'),
149184
),
150185
const Padding(

packages/url_launcher/url_launcher/lib/url_launcher.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import 'package:flutter/services.dart';
99
import 'package:flutter/widgets.dart';
1010
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
1111

12+
export 'package:url_launcher_platform_interface/link.dart' show Link, LinkTarget, LinkWidgetBuilder;
13+
1214
/// Parses the specified URL string and delegates handling of it to the
1315
/// underlying platform.
1416
///
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
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

Comments
 (0)