mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-09-17 12:37:02 +00:00
feat: implement keyboard triggering on buttons and add focus state (#7724)
* feat: implement keyboard triggering on buttons and add focus state * chore: pass isFocused to builders
This commit is contained in:
parent
f6e3290aa4
commit
d8401e09c9
@ -7,6 +7,13 @@ typedef AFBaseButtonColorBuilder = Color Function(
|
|||||||
bool disabled,
|
bool disabled,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
typedef AFBaseButtonBorderColorBuilder = Color Function(
|
||||||
|
BuildContext context,
|
||||||
|
bool isHovering,
|
||||||
|
bool disabled,
|
||||||
|
bool isFocused,
|
||||||
|
);
|
||||||
|
|
||||||
class AFBaseButton extends StatefulWidget {
|
class AFBaseButton extends StatefulWidget {
|
||||||
const AFBaseButton({
|
const AFBaseButton({
|
||||||
super.key,
|
super.key,
|
||||||
@ -16,40 +23,83 @@ class AFBaseButton extends StatefulWidget {
|
|||||||
required this.borderRadius,
|
required this.borderRadius,
|
||||||
this.borderColor,
|
this.borderColor,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
|
this.ringColor,
|
||||||
this.disabled = false,
|
this.disabled = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
final AFBaseButtonColorBuilder? borderColor;
|
final AFBaseButtonBorderColorBuilder? borderColor;
|
||||||
|
final AFBaseButtonBorderColorBuilder? ringColor;
|
||||||
final AFBaseButtonColorBuilder? backgroundColor;
|
final AFBaseButtonColorBuilder? backgroundColor;
|
||||||
|
|
||||||
final EdgeInsetsGeometry padding;
|
final EdgeInsetsGeometry padding;
|
||||||
final double borderRadius;
|
final double borderRadius;
|
||||||
final bool disabled;
|
final bool disabled;
|
||||||
|
|
||||||
final Widget Function(BuildContext context, bool isHovering, bool disabled)
|
final Widget Function(
|
||||||
builder;
|
BuildContext context,
|
||||||
|
bool isHovering,
|
||||||
|
bool disabled,
|
||||||
|
) builder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AFBaseButton> createState() => _AFBaseButtonState();
|
State<AFBaseButton> createState() => _AFBaseButtonState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AFBaseButtonState extends State<AFBaseButton> {
|
class _AFBaseButtonState extends State<AFBaseButton> {
|
||||||
|
final FocusNode focusNode = FocusNode();
|
||||||
|
|
||||||
bool isHovering = false;
|
bool isHovering = false;
|
||||||
|
bool isFocused = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
focusNode.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final Color borderColor = _buildBorderColor(context);
|
final Color borderColor = _buildBorderColor(context);
|
||||||
final Color backgroundColor = _buildBackgroundColor(context);
|
final Color backgroundColor = _buildBackgroundColor(context);
|
||||||
|
final Color ringColor = _buildRingColor(context);
|
||||||
|
|
||||||
return MouseRegion(
|
return Actions(
|
||||||
cursor:
|
actions: {
|
||||||
widget.disabled ? SystemMouseCursors.basic : SystemMouseCursors.click,
|
ActivateIntent: CallbackAction<ActivateIntent>(
|
||||||
|
onInvoke: (_) {
|
||||||
|
if (!widget.disabled) {
|
||||||
|
widget.onTap?.call();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
child: Focus(
|
||||||
|
focusNode: focusNode,
|
||||||
|
onFocusChange: (isFocused) {
|
||||||
|
setState(() => this.isFocused = isFocused);
|
||||||
|
},
|
||||||
|
child: MouseRegion(
|
||||||
|
cursor: widget.disabled
|
||||||
|
? SystemMouseCursors.basic
|
||||||
|
: SystemMouseCursors.click,
|
||||||
onEnter: (_) => setState(() => isHovering = true),
|
onEnter: (_) => setState(() => isHovering = true),
|
||||||
onExit: (_) => setState(() => isHovering = false),
|
onExit: (_) => setState(() => isHovering = false),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: widget.disabled ? null : widget.onTap,
|
onTap: widget.disabled ? null : widget.onTap,
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(widget.borderRadius),
|
||||||
|
border: isFocused
|
||||||
|
? Border.all(
|
||||||
|
color: ringColor,
|
||||||
|
width: 2,
|
||||||
|
strokeAlign: BorderSide.strokeAlignOutside,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
child: DecoratedBox(
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
@ -58,7 +108,14 @@ class _AFBaseButtonState extends State<AFBaseButton> {
|
|||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: widget.padding,
|
padding: widget.padding,
|
||||||
child: widget.builder(context, isHovering, widget.disabled),
|
child: widget.builder(
|
||||||
|
context,
|
||||||
|
isHovering,
|
||||||
|
widget.disabled,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -67,7 +124,8 @@ class _AFBaseButtonState extends State<AFBaseButton> {
|
|||||||
|
|
||||||
Color _buildBorderColor(BuildContext context) {
|
Color _buildBorderColor(BuildContext context) {
|
||||||
final theme = AppFlowyTheme.of(context);
|
final theme = AppFlowyTheme.of(context);
|
||||||
return widget.borderColor?.call(context, isHovering, widget.disabled) ??
|
return widget.borderColor
|
||||||
|
?.call(context, isHovering, widget.disabled, isFocused) ??
|
||||||
theme.borderColorScheme.greyTertiary;
|
theme.borderColorScheme.greyTertiary;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,4 +134,22 @@ class _AFBaseButtonState extends State<AFBaseButton> {
|
|||||||
return widget.backgroundColor?.call(context, isHovering, widget.disabled) ??
|
return widget.backgroundColor?.call(context, isHovering, widget.disabled) ??
|
||||||
theme.fillColorScheme.transparent;
|
theme.fillColorScheme.transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Color _buildRingColor(BuildContext context) {
|
||||||
|
final theme = AppFlowyTheme.of(context);
|
||||||
|
|
||||||
|
if (widget.ringColor != null) {
|
||||||
|
return widget.ringColor!
|
||||||
|
.call(context, isHovering, widget.disabled, isFocused);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFocused) {
|
||||||
|
return AppFlowyTheme.of(context)
|
||||||
|
.borderColorScheme
|
||||||
|
.themeThick
|
||||||
|
.withAlpha(128);
|
||||||
|
}
|
||||||
|
|
||||||
|
return theme.borderColorScheme.transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ class AFFilledButton extends StatelessWidget {
|
|||||||
return AFBaseButton(
|
return AFBaseButton(
|
||||||
disabled: disabled,
|
disabled: disabled,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
borderColor: (_, __, ___) => Colors.transparent,
|
borderColor: (_, __, ___, ____) => Colors.transparent,
|
||||||
padding: padding ?? size.buildPadding(context),
|
padding: padding ?? size.buildPadding(context),
|
||||||
borderRadius: borderRadius ?? size.buildBorderRadius(context),
|
borderRadius: borderRadius ?? size.buildBorderRadius(context),
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
|
@ -121,7 +121,7 @@ class AFFilledTextButton extends AFBaseTextButton {
|
|||||||
return AFBaseButton(
|
return AFBaseButton(
|
||||||
disabled: disabled,
|
disabled: disabled,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
borderColor: (_, __, ___) => Colors.transparent,
|
borderColor: (_, __, ___, ____) => Colors.transparent,
|
||||||
padding: padding ?? size.buildPadding(context),
|
padding: padding ?? size.buildPadding(context),
|
||||||
borderRadius: borderRadius ?? size.buildBorderRadius(context),
|
borderRadius: borderRadius ?? size.buildBorderRadius(context),
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
|
@ -86,7 +86,7 @@ class AFGhostButton extends StatelessWidget {
|
|||||||
return AFBaseButton(
|
return AFBaseButton(
|
||||||
disabled: disabled,
|
disabled: disabled,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
borderColor: (_, __, ___) => Colors.transparent,
|
borderColor: (_, __, ___, ____) => Colors.transparent,
|
||||||
padding: padding ?? size.buildPadding(context),
|
padding: padding ?? size.buildPadding(context),
|
||||||
borderRadius: borderRadius ?? size.buildBorderRadius(context),
|
borderRadius: borderRadius ?? size.buildBorderRadius(context),
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
|
@ -109,7 +109,7 @@ class AFGhostIconTextButton extends StatelessWidget {
|
|||||||
return AFBaseButton(
|
return AFBaseButton(
|
||||||
disabled: disabled,
|
disabled: disabled,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
borderColor: (context, isHovering, disabled) {
|
borderColor: (context, isHovering, disabled, isFocused) {
|
||||||
return Colors.transparent;
|
return Colors.transparent;
|
||||||
},
|
},
|
||||||
padding: padding ?? size.buildPadding(context),
|
padding: padding ?? size.buildPadding(context),
|
||||||
|
@ -88,7 +88,7 @@ class AFGhostTextButton extends AFBaseTextButton {
|
|||||||
return AFBaseButton(
|
return AFBaseButton(
|
||||||
disabled: disabled,
|
disabled: disabled,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
borderColor: (_, __, ___) => Colors.transparent,
|
borderColor: (_, __, ___, ____) => Colors.transparent,
|
||||||
padding: padding ?? size.buildPadding(context),
|
padding: padding ?? size.buildPadding(context),
|
||||||
borderRadius: borderRadius ?? size.buildBorderRadius(context),
|
borderRadius: borderRadius ?? size.buildBorderRadius(context),
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
|
@ -38,7 +38,7 @@ class AFOutlinedButton extends StatelessWidget {
|
|||||||
padding: padding,
|
padding: padding,
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
disabled: disabled,
|
disabled: disabled,
|
||||||
borderColor: (context, isHovering, disabled) {
|
borderColor: (context, isHovering, disabled, isFocused) {
|
||||||
final theme = AppFlowyTheme.of(context);
|
final theme = AppFlowyTheme.of(context);
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
return theme.borderColorScheme.greyTertiary;
|
return theme.borderColorScheme.greyTertiary;
|
||||||
@ -79,7 +79,7 @@ class AFOutlinedButton extends StatelessWidget {
|
|||||||
padding: padding,
|
padding: padding,
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
disabled: disabled,
|
disabled: disabled,
|
||||||
borderColor: (context, isHovering, disabled) {
|
borderColor: (context, isHovering, disabled, isFocused) {
|
||||||
final theme = AppFlowyTheme.of(context);
|
final theme = AppFlowyTheme.of(context);
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
return theme.fillColorScheme.errorThick;
|
return theme.fillColorScheme.errorThick;
|
||||||
@ -118,7 +118,7 @@ class AFOutlinedButton extends StatelessWidget {
|
|||||||
padding: padding,
|
padding: padding,
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
disabled: true,
|
disabled: true,
|
||||||
borderColor: (context, isHovering, disabled) {
|
borderColor: (context, isHovering, disabled, isFocused) {
|
||||||
final theme = AppFlowyTheme.of(context);
|
final theme = AppFlowyTheme.of(context);
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
return theme.borderColorScheme.greyTertiary;
|
return theme.borderColorScheme.greyTertiary;
|
||||||
@ -148,7 +148,7 @@ class AFOutlinedButton extends StatelessWidget {
|
|||||||
final EdgeInsetsGeometry? padding;
|
final EdgeInsetsGeometry? padding;
|
||||||
final double? borderRadius;
|
final double? borderRadius;
|
||||||
|
|
||||||
final AFBaseButtonColorBuilder? borderColor;
|
final AFBaseButtonBorderColorBuilder? borderColor;
|
||||||
final AFBaseButtonColorBuilder? backgroundColor;
|
final AFBaseButtonColorBuilder? backgroundColor;
|
||||||
|
|
||||||
final AFOutlinedButtonWidgetBuilder builder;
|
final AFOutlinedButtonWidgetBuilder builder;
|
||||||
|
@ -46,7 +46,7 @@ class AFOutlinedIconTextButton extends StatelessWidget {
|
|||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
disabled: disabled,
|
disabled: disabled,
|
||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
borderColor: (context, isHovering, disabled) {
|
borderColor: (context, isHovering, disabled, isFocused) {
|
||||||
final theme = AppFlowyTheme.of(context);
|
final theme = AppFlowyTheme.of(context);
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
return theme.borderColorScheme.greyTertiary;
|
return theme.borderColorScheme.greyTertiary;
|
||||||
@ -101,7 +101,7 @@ class AFOutlinedIconTextButton extends StatelessWidget {
|
|||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
disabled: disabled,
|
disabled: disabled,
|
||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
borderColor: (context, isHovering, disabled) {
|
borderColor: (context, isHovering, disabled, isFocused) {
|
||||||
final theme = AppFlowyTheme.of(context);
|
final theme = AppFlowyTheme.of(context);
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
return theme.fillColorScheme.errorThick;
|
return theme.fillColorScheme.errorThick;
|
||||||
@ -156,7 +156,7 @@ class AFOutlinedIconTextButton extends StatelessWidget {
|
|||||||
? theme.textColorScheme.tertiary
|
? theme.textColorScheme.tertiary
|
||||||
: theme.textColorScheme.primary;
|
: theme.textColorScheme.primary;
|
||||||
},
|
},
|
||||||
borderColor: (context, isHovering, disabled) {
|
borderColor: (context, isHovering, disabled, isFocused) {
|
||||||
final theme = AppFlowyTheme.of(context);
|
final theme = AppFlowyTheme.of(context);
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
return theme.borderColorScheme.greyTertiary;
|
return theme.borderColorScheme.greyTertiary;
|
||||||
@ -190,7 +190,7 @@ class AFOutlinedIconTextButton extends StatelessWidget {
|
|||||||
final AFOutlinedIconBuilder iconBuilder;
|
final AFOutlinedIconBuilder iconBuilder;
|
||||||
|
|
||||||
final AFBaseButtonColorBuilder? textColor;
|
final AFBaseButtonColorBuilder? textColor;
|
||||||
final AFBaseButtonColorBuilder? borderColor;
|
final AFBaseButtonBorderColorBuilder? borderColor;
|
||||||
final AFBaseButtonColorBuilder? backgroundColor;
|
final AFBaseButtonColorBuilder? backgroundColor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -40,7 +40,7 @@ class AFOutlinedTextButton extends AFBaseTextButton {
|
|||||||
disabled: disabled,
|
disabled: disabled,
|
||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
textStyle: textStyle,
|
textStyle: textStyle,
|
||||||
borderColor: (context, isHovering, disabled) {
|
borderColor: (context, isHovering, disabled, isFocused) {
|
||||||
final theme = AppFlowyTheme.of(context);
|
final theme = AppFlowyTheme.of(context);
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
return theme.borderColorScheme.greyTertiary;
|
return theme.borderColorScheme.greyTertiary;
|
||||||
@ -95,7 +95,7 @@ class AFOutlinedTextButton extends AFBaseTextButton {
|
|||||||
disabled: disabled,
|
disabled: disabled,
|
||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
textStyle: textStyle,
|
textStyle: textStyle,
|
||||||
borderColor: (context, isHovering, disabled) {
|
borderColor: (context, isHovering, disabled, isFocused) {
|
||||||
final theme = AppFlowyTheme.of(context);
|
final theme = AppFlowyTheme.of(context);
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
return theme.fillColorScheme.errorThick;
|
return theme.fillColorScheme.errorThick;
|
||||||
@ -150,7 +150,7 @@ class AFOutlinedTextButton extends AFBaseTextButton {
|
|||||||
? theme.textColorScheme.tertiary
|
? theme.textColorScheme.tertiary
|
||||||
: theme.textColorScheme.primary;
|
: theme.textColorScheme.primary;
|
||||||
},
|
},
|
||||||
borderColor: (context, isHovering, disabled) {
|
borderColor: (context, isHovering, disabled, isFocused) {
|
||||||
final theme = AppFlowyTheme.of(context);
|
final theme = AppFlowyTheme.of(context);
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
return theme.borderColorScheme.greyTertiary;
|
return theme.borderColorScheme.greyTertiary;
|
||||||
@ -173,7 +173,7 @@ class AFOutlinedTextButton extends AFBaseTextButton {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final AFBaseButtonColorBuilder? borderColor;
|
final AFBaseButtonBorderColorBuilder? borderColor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user