Skip to content

Commit da53ef2

Browse files
Add url and url_target to controls with on_click event (#1337)
* All buttons have `url` and `url_target` props * `Markdown.auto_follow_links`
1 parent 0c523b0 commit da53ef2

22 files changed

+329
-44
lines changed

package/lib/src/controls/container.dart

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import '../utils/colors.dart';
1818
import '../utils/edge_insets.dart';
1919
import '../utils/gradient.dart';
2020
import '../utils/images.dart';
21+
import '../utils/launch_url.dart';
2122
import 'create_control.dart';
2223
import 'error.dart';
2324

@@ -45,6 +46,8 @@ class ContainerControl extends StatelessWidget {
4546
children.where((c) => c.name == "content" && c.isVisible);
4647
bool ink = control.attrBool("ink", false)!;
4748
bool onClick = control.attrBool("onclick", false)!;
49+
String url = control.attrString("url", "")!;
50+
String? urlTarget = control.attrString("urlTarget");
4851
bool onLongPress = control.attrBool("onLongPress", false)!;
4952
bool onHover = control.attrBool("onHover", false)!;
5053
bool disabled = control.isDisabled || parentDisabled;
@@ -126,26 +129,33 @@ class ContainerControl extends StatelessWidget {
126129

127130
Widget? result;
128131

129-
if ((onClick || onLongPress || onHover) && ink && !disabled) {
132+
if ((onClick || url != "" || onLongPress || onHover) &&
133+
ink &&
134+
!disabled) {
130135
var ink = Ink(
131136
decoration: boxDecor,
132137
child: InkWell(
133138
// Dummy callback to enable widget
134139
// see https://github.com/flutter/flutter/issues/50116#issuecomment-582047374
135140
// and https://github.com/flutter/flutter/blob/eed80afe2c641fb14b82a22279d2d78c19661787/packages/flutter/lib/src/material/ink_well.dart#L1125-L1129
136141
onTap: onHover ? () {} : null,
137-
onTapDown: onClick
142+
onTapDown: onClick || url != ""
138143
? (details) {
139144
debugPrint("Container ${control.id} clicked!");
140-
server.sendPageEvent(
141-
eventTarget: control.id,
142-
eventName: "click",
143-
eventData: json.encode(ContainerTapEvent(
144-
localX: details.localPosition.dx,
145-
localY: details.localPosition.dy,
146-
globalX: details.globalPosition.dx,
147-
globalY: details.globalPosition.dy)
148-
.toJson()));
145+
if (url != "") {
146+
openWebBrowser(url, webWindowName: urlTarget);
147+
}
148+
if (onClick) {
149+
server.sendPageEvent(
150+
eventTarget: control.id,
151+
eventName: "click",
152+
eventData: json.encode(ContainerTapEvent(
153+
localX: details.localPosition.dx,
154+
localY: details.localPosition.dy,
155+
globalX: details.globalPosition.dx,
156+
globalY: details.globalPosition.dy)
157+
.toJson()));
158+
}
149159
}
150160
: null,
151161
onLongPress: onLongPress
@@ -230,9 +240,11 @@ class ContainerControl extends StatelessWidget {
230240
: null,
231241
child: child);
232242

233-
if ((onClick || onLongPress || onHover) && !disabled) {
243+
if ((onClick || onLongPress || onHover || url != "") && !disabled) {
234244
result = MouseRegion(
235-
cursor: onClick ? SystemMouseCursors.click : MouseCursor.defer,
245+
cursor: onClick || url != ""
246+
? SystemMouseCursors.click
247+
: MouseCursor.defer,
236248
onEnter: onHover
237249
? (value) {
238250
debugPrint(
@@ -254,18 +266,23 @@ class ContainerControl extends StatelessWidget {
254266
}
255267
: null,
256268
child: GestureDetector(
257-
onTapDown: onClick
269+
onTapDown: onClick || url != ""
258270
? (details) {
259271
debugPrint("Container ${control.id} clicked!");
260-
server.sendPageEvent(
261-
eventTarget: control.id,
262-
eventName: "click",
263-
eventData: json.encode(ContainerTapEvent(
264-
localX: details.localPosition.dx,
265-
localY: details.localPosition.dy,
266-
globalX: details.globalPosition.dx,
267-
globalY: details.globalPosition.dy)
268-
.toJson()));
272+
if (url != "") {
273+
openWebBrowser(url, webWindowName: urlTarget);
274+
}
275+
if (onClick) {
276+
server.sendPageEvent(
277+
eventTarget: control.id,
278+
eventName: "click",
279+
eventData: json.encode(ContainerTapEvent(
280+
localX: details.localPosition.dx,
281+
localY: details.localPosition.dy,
282+
globalX: details.globalPosition.dx,
283+
globalY: details.globalPosition.dy)
284+
.toJson()));
285+
}
269286
}
270287
: null,
271288
onLongPress: onLongPress

package/lib/src/controls/elevated_button.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import '../models/control.dart';
55
import '../utils/buttons.dart';
66
import '../utils/colors.dart';
77
import '../utils/icons.dart';
8+
import '../utils/launch_url.dart';
89
import 'create_control.dart';
910
import 'error.dart';
1011

@@ -58,6 +59,7 @@ class _ElevatedButtonControlState extends State<ElevatedButtonControl> {
5859
final server = FletAppServices.of(context).server;
5960

6061
String text = widget.control.attrString("text", "")!;
62+
String url = widget.control.attrString("url", "")!;
6163
IconData? icon = getMaterialIcon(widget.control.attrString("icon", "")!);
6264
Color? iconColor = HexColor.fromString(
6365
Theme.of(context), widget.control.attrString("iconColor", "")!);
@@ -70,6 +72,10 @@ class _ElevatedButtonControlState extends State<ElevatedButtonControl> {
7072
Function()? onPressed = !disabled
7173
? () {
7274
debugPrint("Button ${widget.control.id} clicked!");
75+
if (url != "") {
76+
openWebBrowser(url,
77+
webWindowName: widget.control.attrString("urlTarget"));
78+
}
7379
server.sendPageEvent(
7480
eventTarget: widget.control.id,
7581
eventName: "click",

package/lib/src/controls/floating_action_button.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import '../models/control.dart';
55
import '../utils/borders.dart';
66
import '../utils/colors.dart';
77
import '../utils/icons.dart';
8+
import '../utils/launch_url.dart';
89
import 'create_control.dart';
910
import 'error.dart';
1011

@@ -28,6 +29,8 @@ class FloatingActionButtonControl extends StatelessWidget {
2829

2930
String? text = control.attrString("text");
3031
IconData? icon = getMaterialIcon(control.attrString("icon", "")!);
32+
String url = control.attrString("url", "")!;
33+
String? urlTarget = control.attrString("urlTarget");
3134
Color? bgColor = HexColor.fromString(
3235
Theme.of(context), control.attrString("bgColor", "")!);
3336
OutlinedBorder? shape = parseOutlinedBorder(control, "shape");
@@ -41,6 +44,9 @@ class FloatingActionButtonControl extends StatelessWidget {
4144
? null
4245
: () {
4346
debugPrint("FloatingActionButtonControl ${control.id} clicked!");
47+
if (url != "") {
48+
openWebBrowser(url, webWindowName: urlTarget);
49+
}
4450
FletAppServices.of(context).server.sendPageEvent(
4551
eventTarget: control.id, eventName: "click", eventData: "");
4652
};

package/lib/src/controls/icon_button.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import '../models/control.dart';
55
import '../utils/buttons.dart';
66
import '../utils/colors.dart';
77
import '../utils/icons.dart';
8+
import '../utils/launch_url.dart';
89
import 'create_control.dart';
910
import 'error.dart';
1011

@@ -69,12 +70,17 @@ class _IconButtonControlState extends State<IconButtonControl> {
6970
var contentCtrls = widget.children.where((c) => c.name == "content");
7071
bool autofocus = widget.control.attrBool("autofocus", false)!;
7172
bool selected = widget.control.attrBool("selected", false)!;
73+
String url = widget.control.attrString("url", "")!;
74+
String? urlTarget = widget.control.attrString("urlTarget");
7275
bool disabled = widget.control.isDisabled || widget.parentDisabled;
7376

7477
Function()? onPressed = disabled
7578
? null
7679
: () {
7780
debugPrint("Button ${widget.control.id} clicked!");
81+
if (url != "") {
82+
openWebBrowser(url, webWindowName: urlTarget);
83+
}
7884
FletAppServices.of(context).server.sendPageEvent(
7985
eventTarget: widget.control.id,
8086
eventName: "click",

package/lib/src/controls/list_tile.dart

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
33
import '../flet_app_services.dart';
44
import '../models/control.dart';
55
import '../utils/edge_insets.dart';
6+
import '../utils/launch_url.dart';
67
import 'create_control.dart';
78

89
class ListTileControl extends StatelessWidget {
@@ -38,15 +39,22 @@ class ListTileControl extends StatelessWidget {
3839
bool isThreeLine = control.attrBool("isThreeLine", false)!;
3940
bool autofocus = control.attrBool("autofocus", false)!;
4041
bool onclick = control.attrBool("onclick", false)!;
42+
String url = control.attrString("url", "")!;
43+
String? urlTarget = control.attrString("urlTarget");
4144
bool disabled = control.isDisabled || parentDisabled;
4245

43-
Function()? onPressed = disabled || !onclick
44-
? null
45-
: () {
46+
Function()? onPressed = (onclick || url != "") && !disabled
47+
? () {
4648
debugPrint("ListTile ${control.id} clicked!");
47-
server.sendPageEvent(
48-
eventTarget: control.id, eventName: "click", eventData: "");
49-
};
49+
if (url != "") {
50+
openWebBrowser(url, webWindowName: urlTarget);
51+
}
52+
if (onclick) {
53+
server.sendPageEvent(
54+
eventTarget: control.id, eventName: "click", eventData: "");
55+
}
56+
}
57+
: null;
5058

5159
Function()? onLongPress = disabled
5260
? null

package/lib/src/controls/markdown.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import '../flet_app_services.dart';
88
import '../models/app_state.dart';
99
import '../models/control.dart';
1010
import '../models/page_args_model.dart';
11+
import '../utils/launch_url.dart';
1112
import '../utils/text.dart';
1213
import '../utils/uri.dart';
1314
import 'create_control.dart';
@@ -49,6 +50,9 @@ class MarkdownControl extends StatelessWidget {
4950
.bodyMedium!
5051
.copyWith(fontFamily: "monospace"));
5152

53+
var autoFollowLinks = control.attrBool("autoFollowLinks", false)!;
54+
var autoFollowLinksTarget = control.attrString("autoFollowLinksTarget");
55+
5256
return StoreConnector<AppState, PageArgsModel>(
5357
distinct: true,
5458
converter: (store) => PageArgsModel.fromStore(store),
@@ -67,6 +71,9 @@ class MarkdownControl extends StatelessWidget {
6771
styleSheet: mdStyleSheet,
6872
onTapLink: (String text, String? href, String title) {
6973
debugPrint("Markdown link tapped ${control.id} clicked: $href");
74+
if (autoFollowLinks && href != null) {
75+
openWebBrowser(href, webWindowName: autoFollowLinksTarget);
76+
}
7077
FletAppServices.of(context).server.sendPageEvent(
7178
eventTarget: control.id,
7279
eventName: "tap_link",

package/lib/src/controls/outlined_button.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import '../models/control.dart';
55
import '../utils/buttons.dart';
66
import '../utils/colors.dart';
77
import '../utils/icons.dart';
8+
import '../utils/launch_url.dart';
89
import 'create_control.dart';
910

1011
class OutlinedButtonControl extends StatefulWidget {
@@ -61,6 +62,8 @@ class _OutlinedButtonControlState extends State<OutlinedButtonControl> {
6162
Color? iconColor = HexColor.fromString(
6263
Theme.of(context), widget.control.attrString("iconColor", "")!);
6364
var contentCtrls = widget.children.where((c) => c.name == "content");
65+
String url = widget.control.attrString("url", "")!;
66+
String? urlTarget = widget.control.attrString("urlTarget");
6467
bool onHover = widget.control.attrBool("onHover", false)!;
6568
bool onLongPress = widget.control.attrBool("onLongPress", false)!;
6669
bool autofocus = widget.control.attrBool("autofocus", false)!;
@@ -69,6 +72,9 @@ class _OutlinedButtonControlState extends State<OutlinedButtonControl> {
6972
Function()? onPressed = !disabled
7073
? () {
7174
debugPrint("Button ${widget.control.id} clicked!");
75+
if (url != "") {
76+
openWebBrowser(url, webWindowName: urlTarget);
77+
}
7278
server.sendPageEvent(
7379
eventTarget: widget.control.id,
7480
eventName: "click",

package/lib/src/controls/text_button.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import '../models/control.dart';
55
import '../utils/buttons.dart';
66
import '../utils/colors.dart';
77
import '../utils/icons.dart';
8+
import '../utils/launch_url.dart';
89
import 'create_control.dart';
910

1011
class TextButtonControl extends StatefulWidget {
@@ -63,12 +64,17 @@ class _TextButtonControlState extends State<TextButtonControl> {
6364
var contentCtrls = widget.children.where((c) => c.name == "content");
6465
bool onHover = widget.control.attrBool("onHover", false)!;
6566
bool onLongPress = widget.control.attrBool("onLongPress", false)!;
67+
String url = widget.control.attrString("url", "")!;
68+
String? urlTarget = widget.control.attrString("urlTarget");
6669
bool autofocus = widget.control.attrBool("autofocus", false)!;
6770
bool disabled = widget.control.isDisabled || widget.parentDisabled;
6871

6972
Function()? onPressed = !disabled
7073
? () {
7174
debugPrint("Button ${widget.control.id} clicked!");
75+
if (url != "") {
76+
openWebBrowser(url, webWindowName: urlTarget);
77+
}
7278
server.sendPageEvent(
7379
eventTarget: widget.control.id,
7480
eventName: "click",

package/lib/src/reducers.dart

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -195,12 +195,15 @@ AppState appReducer(AppState state, dynamic action) {
195195
closeInAppWebView();
196196
break;
197197
case "launchUrl":
198-
openWebBrowser(
199-
action.payload.args["url"]!,
200-
action.payload.args["web_window_name"],
201-
action.payload.args["web_popup_window"]?.toLowerCase() == "true",
202-
int.tryParse(action.payload.args["window_width"] ?? ""),
203-
int.tryParse(action.payload.args["window_height"] ?? ""));
198+
openWebBrowser(action.payload.args["url"]!,
199+
webWindowName: action.payload.args["web_window_name"],
200+
webPopupWindow:
201+
action.payload.args["web_popup_window"]?.toLowerCase() ==
202+
"true",
203+
windowWidth:
204+
int.tryParse(action.payload.args["window_width"] ?? ""),
205+
windowHeight:
206+
int.tryParse(action.payload.args["window_height"] ?? ""));
204207
break;
205208
case "canLaunchUrl":
206209
canLaunchUrl(Uri.parse(action.payload.args["url"]!)).then((value) =>

package/lib/src/utils/launch_url.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ import 'package:url_launcher/url_launcher.dart';
33
import 'platform_utils_non_web.dart'
44
if (dart.library.js) "platform_utils_web.dart";
55

6-
Future openWebBrowser(String url, String? webWindowName, bool webPopupWindow,
7-
int? windowWidth, int? windowHeight) async {
8-
if (webPopupWindow) {
6+
Future openWebBrowser(String url,
7+
{String? webWindowName,
8+
bool? webPopupWindow,
9+
int? windowWidth,
10+
int? windowHeight}) async {
11+
if (webPopupWindow == true) {
912
openPopupBrowserWindow(
1013
url, webWindowName ?? "Flet", windowWidth ?? 1200, windowHeight ?? 800);
1114
} else {

0 commit comments

Comments
 (0)