Skip to content

Commit 8a98b19

Browse files
Added focus() method, focus and blur events to Elevated, Outlined, Text and Icon buttons (#1079)
* Focusable ElevatedButton Close #1048 * Added focus/blur to outlined, text and icon buttons * Re-do `focus()` in `TextField` and `Dropdown`
1 parent e63290d commit 8a98b19

File tree

13 files changed

+378
-109
lines changed

13 files changed

+378
-109
lines changed

package/lib/src/controls/dropdown.dart

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import 'dart:convert';
2-
31
import 'package:flutter/material.dart';
42
import 'package:flutter_redux/flutter_redux.dart';
53

@@ -36,6 +34,7 @@ class _DropdownControlState extends State<DropdownControl> {
3634
String? _value;
3735
bool _focused = false;
3836
late final FocusNode _focusNode;
37+
String? _lastFocusValue;
3938

4039
@override
4140
void initState() {
@@ -130,13 +129,9 @@ class _DropdownControlState extends State<DropdownControl> {
130129
.where((c) => c.name == "suffix" && c.isVisible);
131130

132131
var focusValue = widget.control.attrString("focus");
133-
if (focusValue != null) {
134-
debugPrint("Focus JSON value: $focusValue");
135-
var jv = json.decode(focusValue);
136-
var focus = jv["d"] as bool;
137-
if (focus) {
138-
_focusNode.requestFocus();
139-
}
132+
if (focusValue != null && focusValue != _lastFocusValue) {
133+
_lastFocusValue = focusValue;
134+
_focusNode.requestFocus();
140135
}
141136

142137
var borderRadius = parseBorderRadius(widget.control, "borderRadius");

package/lib/src/controls/elevated_button.dart

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import '../utils/icons.dart';
88
import 'create_control.dart';
99
import 'error.dart';
1010

11-
class ElevatedButtonControl extends StatelessWidget {
11+
class ElevatedButtonControl extends StatefulWidget {
1212
final Control? parent;
1313
final Control control;
1414
final List<Control> children;
@@ -22,45 +22,76 @@ class ElevatedButtonControl extends StatelessWidget {
2222
required this.parentDisabled})
2323
: super(key: key);
2424

25+
@override
26+
State<ElevatedButtonControl> createState() => _ElevatedButtonControlState();
27+
}
28+
29+
class _ElevatedButtonControlState extends State<ElevatedButtonControl> {
30+
late final FocusNode _focusNode;
31+
String? _lastFocusValue;
32+
33+
@override
34+
void initState() {
35+
super.initState();
36+
_focusNode = FocusNode();
37+
_focusNode.addListener(_onFocusChange);
38+
}
39+
40+
@override
41+
void dispose() {
42+
_focusNode.removeListener(_onFocusChange);
43+
_focusNode.dispose();
44+
super.dispose();
45+
}
46+
47+
void _onFocusChange() {
48+
FletAppServices.of(context).server.sendPageEvent(
49+
eventTarget: widget.control.id,
50+
eventName: _focusNode.hasFocus ? "focus" : "blur",
51+
eventData: "");
52+
}
53+
2554
@override
2655
Widget build(BuildContext context) {
27-
debugPrint("Button build: ${control.id}");
56+
debugPrint("Button build: ${widget.control.id}");
2857

2958
final server = FletAppServices.of(context).server;
3059

31-
String text = control.attrString("text", "")!;
32-
IconData? icon = getMaterialIcon(control.attrString("icon", "")!);
60+
String text = widget.control.attrString("text", "")!;
61+
IconData? icon = getMaterialIcon(widget.control.attrString("icon", "")!);
3362
Color? iconColor = HexColor.fromString(
34-
Theme.of(context), control.attrString("iconColor", "")!);
35-
var contentCtrls = children.where((c) => c.name == "content");
36-
bool onHover = control.attrBool("onHover", false)!;
37-
bool onLongPress = control.attrBool("onLongPress", false)!;
38-
bool autofocus = control.attrBool("autofocus", false)!;
39-
bool disabled = control.isDisabled || parentDisabled;
63+
Theme.of(context), widget.control.attrString("iconColor", "")!);
64+
var contentCtrls = widget.children.where((c) => c.name == "content");
65+
bool onHover = widget.control.attrBool("onHover", false)!;
66+
bool onLongPress = widget.control.attrBool("onLongPress", false)!;
67+
bool autofocus = widget.control.attrBool("autofocus", false)!;
68+
bool disabled = widget.control.isDisabled || widget.parentDisabled;
4069

4170
Function()? onPressed = !disabled
4271
? () {
43-
debugPrint("Button ${control.id} clicked!");
72+
debugPrint("Button ${widget.control.id} clicked!");
4473
server.sendPageEvent(
45-
eventTarget: control.id, eventName: "click", eventData: "");
74+
eventTarget: widget.control.id,
75+
eventName: "click",
76+
eventData: "");
4677
}
4778
: null;
4879

4980
Function()? onLongPressHandler = onLongPress && !disabled
5081
? () {
51-
debugPrint("Button ${control.id} long pressed!");
82+
debugPrint("Button ${widget.control.id} long pressed!");
5283
server.sendPageEvent(
53-
eventTarget: control.id,
84+
eventTarget: widget.control.id,
5485
eventName: "long_press",
5586
eventData: "");
5687
}
5788
: null;
5889

5990
Function(bool)? onHoverHandler = onHover && !disabled
6091
? (state) {
61-
debugPrint("Button ${control.id} hovered!");
92+
debugPrint("Button ${widget.control.id} hovered!");
6293
server.sendPageEvent(
63-
eventTarget: control.id,
94+
eventTarget: widget.control.id,
6495
eventName: "hover",
6596
eventData: state.toString());
6697
}
@@ -70,7 +101,7 @@ class ElevatedButtonControl extends StatelessWidget {
70101

71102
var theme = Theme.of(context);
72103

73-
var style = parseButtonStyle(Theme.of(context), control, "style",
104+
var style = parseButtonStyle(Theme.of(context), widget.control, "style",
74105
defaultForegroundColor: theme.colorScheme.primary,
75106
defaultBackgroundColor: theme.colorScheme.surface,
76107
defaultOverlayColor: theme.colorScheme.primary.withOpacity(0.08),
@@ -91,6 +122,7 @@ class ElevatedButtonControl extends StatelessWidget {
91122
button = ElevatedButton.icon(
92123
style: style,
93124
autofocus: autofocus,
125+
focusNode: _focusNode,
94126
onPressed: onPressed,
95127
onLongPress: onLongPressHandler,
96128
onHover: onHoverHandler,
@@ -103,19 +135,29 @@ class ElevatedButtonControl extends StatelessWidget {
103135
button = ElevatedButton(
104136
style: style,
105137
autofocus: autofocus,
138+
focusNode: _focusNode,
106139
onPressed: onPressed,
107140
onLongPress: onLongPressHandler,
108141
onHover: onHoverHandler,
109-
child: createControl(control, contentCtrls.first.id, disabled));
142+
child:
143+
createControl(widget.control, contentCtrls.first.id, disabled));
110144
} else {
111145
button = ElevatedButton(
112146
style: style,
147+
autofocus: autofocus,
148+
focusNode: _focusNode,
113149
onPressed: onPressed,
114150
onLongPress: onLongPressHandler,
115151
onHover: onHoverHandler,
116152
child: Text(text));
117153
}
118154

119-
return constrainedControl(context, button, parent, control);
155+
var focusValue = widget.control.attrString("focus");
156+
if (focusValue != null && focusValue != _lastFocusValue) {
157+
_lastFocusValue = focusValue;
158+
_focusNode.requestFocus();
159+
}
160+
161+
return constrainedControl(context, button, widget.parent, widget.control);
120162
}
121163
}

package/lib/src/controls/icon_button.dart

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import '../utils/icons.dart';
88
import 'create_control.dart';
99
import 'error.dart';
1010

11-
class IconButtonControl extends StatelessWidget {
11+
class IconButtonControl extends StatefulWidget {
1212
final Control? parent;
1313
final Control control;
1414
final List<Control> children;
@@ -22,39 +22,70 @@ class IconButtonControl extends StatelessWidget {
2222
required this.parentDisabled})
2323
: super(key: key);
2424

25+
@override
26+
State<IconButtonControl> createState() => _IconButtonControlState();
27+
}
28+
29+
class _IconButtonControlState extends State<IconButtonControl> {
30+
late final FocusNode _focusNode;
31+
String? _lastFocusValue;
32+
33+
@override
34+
void initState() {
35+
super.initState();
36+
_focusNode = FocusNode();
37+
_focusNode.addListener(_onFocusChange);
38+
}
39+
40+
@override
41+
void dispose() {
42+
_focusNode.removeListener(_onFocusChange);
43+
_focusNode.dispose();
44+
super.dispose();
45+
}
46+
47+
void _onFocusChange() {
48+
FletAppServices.of(context).server.sendPageEvent(
49+
eventTarget: widget.control.id,
50+
eventName: _focusNode.hasFocus ? "focus" : "blur",
51+
eventData: "");
52+
}
53+
2554
@override
2655
Widget build(BuildContext context) {
27-
debugPrint("Button build: ${control.id}");
56+
debugPrint("Button build: ${widget.control.id}");
2857

29-
IconData? icon = getMaterialIcon(control.attrString("icon", "")!);
58+
IconData? icon = getMaterialIcon(widget.control.attrString("icon", "")!);
3059
IconData? selectedIcon =
31-
getMaterialIcon(control.attrString("selectedIcon", "")!);
60+
getMaterialIcon(widget.control.attrString("selectedIcon", "")!);
3261
Color? iconColor = HexColor.fromString(
33-
Theme.of(context), control.attrString("iconColor", "")!);
62+
Theme.of(context), widget.control.attrString("iconColor", "")!);
3463
Color? selectedIconColor = HexColor.fromString(
35-
Theme.of(context), control.attrString("selectedIconColor", "")!);
64+
Theme.of(context), widget.control.attrString("selectedIconColor", "")!);
3665
Color? bgColor = HexColor.fromString(
37-
Theme.of(context), control.attrString("bgColor", "")!);
38-
double? iconSize = control.attrDouble("iconSize");
39-
var tooltip = control.attrString("tooltip");
40-
var contentCtrls = children.where((c) => c.name == "content");
41-
bool autofocus = control.attrBool("autofocus", false)!;
42-
bool selected = control.attrBool("selected", false)!;
43-
bool disabled = control.isDisabled || parentDisabled;
66+
Theme.of(context), widget.control.attrString("bgColor", "")!);
67+
double? iconSize = widget.control.attrDouble("iconSize");
68+
var tooltip = widget.control.attrString("tooltip");
69+
var contentCtrls = widget.children.where((c) => c.name == "content");
70+
bool autofocus = widget.control.attrBool("autofocus", false)!;
71+
bool selected = widget.control.attrBool("selected", false)!;
72+
bool disabled = widget.control.isDisabled || widget.parentDisabled;
4473

4574
Function()? onPressed = disabled
4675
? null
4776
: () {
48-
debugPrint("Button ${control.id} clicked!");
77+
debugPrint("Button ${widget.control.id} clicked!");
4978
FletAppServices.of(context).server.sendPageEvent(
50-
eventTarget: control.id, eventName: "click", eventData: "");
79+
eventTarget: widget.control.id,
80+
eventName: "click",
81+
eventData: "");
5182
};
5283

5384
Widget? button;
5485

5586
var theme = Theme.of(context);
5687

57-
var style = parseButtonStyle(Theme.of(context), control, "style",
88+
var style = parseButtonStyle(Theme.of(context), widget.control, "style",
5889
defaultForegroundColor: theme.colorScheme.primary,
5990
defaultBackgroundColor: Colors.transparent,
6091
defaultOverlayColor: Colors.transparent,
@@ -70,6 +101,7 @@ class IconButtonControl extends StatelessWidget {
70101
if (icon != null) {
71102
button = IconButton(
72103
autofocus: autofocus,
104+
focusNode: _focusNode,
73105
icon: Icon(
74106
icon,
75107
color: iconColor,
@@ -85,6 +117,7 @@ class IconButtonControl extends StatelessWidget {
85117
} else if (contentCtrls.isNotEmpty) {
86118
button = IconButton(
87119
autofocus: autofocus,
120+
focusNode: _focusNode,
88121
onPressed: onPressed,
89122
iconSize: iconSize,
90123
style: style,
@@ -93,7 +126,7 @@ class IconButtonControl extends StatelessWidget {
93126
selectedIcon: selectedIcon != null
94127
? Icon(selectedIcon, color: selectedIconColor)
95128
: null,
96-
icon: createControl(control, contentCtrls.first.id, disabled));
129+
icon: createControl(widget.control, contentCtrls.first.id, disabled));
97130
} else {
98131
return const ErrorControl(
99132
"Icon button does not have an icon neither content specified.");
@@ -107,6 +140,12 @@ class IconButtonControl extends StatelessWidget {
107140
);
108141
}
109142

110-
return constrainedControl(context, button, parent, control);
143+
var focusValue = widget.control.attrString("focus");
144+
if (focusValue != null && focusValue != _lastFocusValue) {
145+
_lastFocusValue = focusValue;
146+
_focusNode.requestFocus();
147+
}
148+
149+
return constrainedControl(context, button, widget.parent, widget.control);
111150
}
112151
}

0 commit comments

Comments
 (0)