Skip to content

Commit

Permalink
Added focus() method, focus and blur events to Elevated, Outlin…
Browse files Browse the repository at this point in the history
…ed, Text and Icon buttons (flet-dev#1079)

* Focusable ElevatedButton

Close flet-dev#1048

* Added focus/blur to outlined, text and icon buttons

* Re-do `focus()` in `TextField` and `Dropdown`
  • Loading branch information
FeodorFitsner authored and zrr1999 committed Jul 17, 2024
1 parent 990134e commit e1298f1
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 109 deletions.
13 changes: 4 additions & 9 deletions package/lib/src/controls/dropdown.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';

Expand Down Expand Up @@ -36,6 +34,7 @@ class _DropdownControlState extends State<DropdownControl> {
String? _value;
bool _focused = false;
late final FocusNode _focusNode;
String? _lastFocusValue;

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

var focusValue = widget.control.attrString("focus");
if (focusValue != null) {
debugPrint("Focus JSON value: $focusValue");
var jv = json.decode(focusValue);
var focus = jv["d"] as bool;
if (focus) {
_focusNode.requestFocus();
}
if (focusValue != null && focusValue != _lastFocusValue) {
_lastFocusValue = focusValue;
_focusNode.requestFocus();
}

var borderRadius = parseBorderRadius(widget.control, "borderRadius");
Expand Down
80 changes: 61 additions & 19 deletions package/lib/src/controls/elevated_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import '../utils/icons.dart';
import 'create_control.dart';
import 'error.dart';

class ElevatedButtonControl extends StatelessWidget {
class ElevatedButtonControl extends StatefulWidget {
final Control? parent;
final Control control;
final List<Control> children;
Expand All @@ -22,45 +22,76 @@ class ElevatedButtonControl extends StatelessWidget {
required this.parentDisabled})
: super(key: key);

@override
State<ElevatedButtonControl> createState() => _ElevatedButtonControlState();
}

class _ElevatedButtonControlState extends State<ElevatedButtonControl> {
late final FocusNode _focusNode;
String? _lastFocusValue;

@override
void initState() {
super.initState();
_focusNode = FocusNode();
_focusNode.addListener(_onFocusChange);
}

@override
void dispose() {
_focusNode.removeListener(_onFocusChange);
_focusNode.dispose();
super.dispose();
}

void _onFocusChange() {
FletAppServices.of(context).server.sendPageEvent(
eventTarget: widget.control.id,
eventName: _focusNode.hasFocus ? "focus" : "blur",
eventData: "");
}

@override
Widget build(BuildContext context) {
debugPrint("Button build: ${control.id}");
debugPrint("Button build: ${widget.control.id}");

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

String text = control.attrString("text", "")!;
IconData? icon = getMaterialIcon(control.attrString("icon", "")!);
String text = widget.control.attrString("text", "")!;
IconData? icon = getMaterialIcon(widget.control.attrString("icon", "")!);
Color? iconColor = HexColor.fromString(
Theme.of(context), control.attrString("iconColor", "")!);
var contentCtrls = children.where((c) => c.name == "content");
bool onHover = control.attrBool("onHover", false)!;
bool onLongPress = control.attrBool("onLongPress", false)!;
bool autofocus = control.attrBool("autofocus", false)!;
bool disabled = control.isDisabled || parentDisabled;
Theme.of(context), widget.control.attrString("iconColor", "")!);
var contentCtrls = widget.children.where((c) => c.name == "content");
bool onHover = widget.control.attrBool("onHover", false)!;
bool onLongPress = widget.control.attrBool("onLongPress", false)!;
bool autofocus = widget.control.attrBool("autofocus", false)!;
bool disabled = widget.control.isDisabled || widget.parentDisabled;

Function()? onPressed = !disabled
? () {
debugPrint("Button ${control.id} clicked!");
debugPrint("Button ${widget.control.id} clicked!");
server.sendPageEvent(
eventTarget: control.id, eventName: "click", eventData: "");
eventTarget: widget.control.id,
eventName: "click",
eventData: "");
}
: null;

Function()? onLongPressHandler = onLongPress && !disabled
? () {
debugPrint("Button ${control.id} long pressed!");
debugPrint("Button ${widget.control.id} long pressed!");
server.sendPageEvent(
eventTarget: control.id,
eventTarget: widget.control.id,
eventName: "long_press",
eventData: "");
}
: null;

Function(bool)? onHoverHandler = onHover && !disabled
? (state) {
debugPrint("Button ${control.id} hovered!");
debugPrint("Button ${widget.control.id} hovered!");
server.sendPageEvent(
eventTarget: control.id,
eventTarget: widget.control.id,
eventName: "hover",
eventData: state.toString());
}
Expand All @@ -70,7 +101,7 @@ class ElevatedButtonControl extends StatelessWidget {

var theme = Theme.of(context);

var style = parseButtonStyle(Theme.of(context), control, "style",
var style = parseButtonStyle(Theme.of(context), widget.control, "style",
defaultForegroundColor: theme.colorScheme.primary,
defaultBackgroundColor: theme.colorScheme.surface,
defaultOverlayColor: theme.colorScheme.primary.withOpacity(0.08),
Expand All @@ -91,6 +122,7 @@ class ElevatedButtonControl extends StatelessWidget {
button = ElevatedButton.icon(
style: style,
autofocus: autofocus,
focusNode: _focusNode,
onPressed: onPressed,
onLongPress: onLongPressHandler,
onHover: onHoverHandler,
Expand All @@ -103,19 +135,29 @@ class ElevatedButtonControl extends StatelessWidget {
button = ElevatedButton(
style: style,
autofocus: autofocus,
focusNode: _focusNode,
onPressed: onPressed,
onLongPress: onLongPressHandler,
onHover: onHoverHandler,
child: createControl(control, contentCtrls.first.id, disabled));
child:
createControl(widget.control, contentCtrls.first.id, disabled));
} else {
button = ElevatedButton(
style: style,
autofocus: autofocus,
focusNode: _focusNode,
onPressed: onPressed,
onLongPress: onLongPressHandler,
onHover: onHoverHandler,
child: Text(text));
}

return constrainedControl(context, button, parent, control);
var focusValue = widget.control.attrString("focus");
if (focusValue != null && focusValue != _lastFocusValue) {
_lastFocusValue = focusValue;
_focusNode.requestFocus();
}

return constrainedControl(context, button, widget.parent, widget.control);
}
}
75 changes: 57 additions & 18 deletions package/lib/src/controls/icon_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import '../utils/icons.dart';
import 'create_control.dart';
import 'error.dart';

class IconButtonControl extends StatelessWidget {
class IconButtonControl extends StatefulWidget {
final Control? parent;
final Control control;
final List<Control> children;
Expand All @@ -22,39 +22,70 @@ class IconButtonControl extends StatelessWidget {
required this.parentDisabled})
: super(key: key);

@override
State<IconButtonControl> createState() => _IconButtonControlState();
}

class _IconButtonControlState extends State<IconButtonControl> {
late final FocusNode _focusNode;
String? _lastFocusValue;

@override
void initState() {
super.initState();
_focusNode = FocusNode();
_focusNode.addListener(_onFocusChange);
}

@override
void dispose() {
_focusNode.removeListener(_onFocusChange);
_focusNode.dispose();
super.dispose();
}

void _onFocusChange() {
FletAppServices.of(context).server.sendPageEvent(
eventTarget: widget.control.id,
eventName: _focusNode.hasFocus ? "focus" : "blur",
eventData: "");
}

@override
Widget build(BuildContext context) {
debugPrint("Button build: ${control.id}");
debugPrint("Button build: ${widget.control.id}");

IconData? icon = getMaterialIcon(control.attrString("icon", "")!);
IconData? icon = getMaterialIcon(widget.control.attrString("icon", "")!);
IconData? selectedIcon =
getMaterialIcon(control.attrString("selectedIcon", "")!);
getMaterialIcon(widget.control.attrString("selectedIcon", "")!);
Color? iconColor = HexColor.fromString(
Theme.of(context), control.attrString("iconColor", "")!);
Theme.of(context), widget.control.attrString("iconColor", "")!);
Color? selectedIconColor = HexColor.fromString(
Theme.of(context), control.attrString("selectedIconColor", "")!);
Theme.of(context), widget.control.attrString("selectedIconColor", "")!);
Color? bgColor = HexColor.fromString(
Theme.of(context), control.attrString("bgColor", "")!);
double? iconSize = control.attrDouble("iconSize");
var tooltip = control.attrString("tooltip");
var contentCtrls = children.where((c) => c.name == "content");
bool autofocus = control.attrBool("autofocus", false)!;
bool selected = control.attrBool("selected", false)!;
bool disabled = control.isDisabled || parentDisabled;
Theme.of(context), widget.control.attrString("bgColor", "")!);
double? iconSize = widget.control.attrDouble("iconSize");
var tooltip = widget.control.attrString("tooltip");
var contentCtrls = widget.children.where((c) => c.name == "content");
bool autofocus = widget.control.attrBool("autofocus", false)!;
bool selected = widget.control.attrBool("selected", false)!;
bool disabled = widget.control.isDisabled || widget.parentDisabled;

Function()? onPressed = disabled
? null
: () {
debugPrint("Button ${control.id} clicked!");
debugPrint("Button ${widget.control.id} clicked!");
FletAppServices.of(context).server.sendPageEvent(
eventTarget: control.id, eventName: "click", eventData: "");
eventTarget: widget.control.id,
eventName: "click",
eventData: "");
};

Widget? button;

var theme = Theme.of(context);

var style = parseButtonStyle(Theme.of(context), control, "style",
var style = parseButtonStyle(Theme.of(context), widget.control, "style",
defaultForegroundColor: theme.colorScheme.primary,
defaultBackgroundColor: Colors.transparent,
defaultOverlayColor: Colors.transparent,
Expand All @@ -70,6 +101,7 @@ class IconButtonControl extends StatelessWidget {
if (icon != null) {
button = IconButton(
autofocus: autofocus,
focusNode: _focusNode,
icon: Icon(
icon,
color: iconColor,
Expand All @@ -85,6 +117,7 @@ class IconButtonControl extends StatelessWidget {
} else if (contentCtrls.isNotEmpty) {
button = IconButton(
autofocus: autofocus,
focusNode: _focusNode,
onPressed: onPressed,
iconSize: iconSize,
style: style,
Expand All @@ -93,7 +126,7 @@ class IconButtonControl extends StatelessWidget {
selectedIcon: selectedIcon != null
? Icon(selectedIcon, color: selectedIconColor)
: null,
icon: createControl(control, contentCtrls.first.id, disabled));
icon: createControl(widget.control, contentCtrls.first.id, disabled));
} else {
return const ErrorControl(
"Icon button does not have an icon neither content specified.");
Expand All @@ -107,6 +140,12 @@ class IconButtonControl extends StatelessWidget {
);
}

return constrainedControl(context, button, parent, control);
var focusValue = widget.control.attrString("focus");
if (focusValue != null && focusValue != _lastFocusValue) {
_lastFocusValue = focusValue;
_focusNode.requestFocus();
}

return constrainedControl(context, button, widget.parent, widget.control);
}
}
Loading

0 comments on commit e1298f1

Please sign in to comment.