refactor(ui_kit): use tapped state and merged styles

This commit is contained in:
Hugo Pointcheval 2023-02-13 17:08:02 +01:00
parent 61e28ce5e7
commit b6f25dd5b5
Signed by: hugo
GPG Key ID: 3AAC487E131E00BC
11 changed files with 430 additions and 220 deletions

View File

@ -14,14 +14,17 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart' hide OutlinedButton;
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
import 'package:wyatt_ui_kit/wyatt_ui_kit.dart';
const _color1 = Color(0xFF3C97FB);
const _color2 = Color(0xFF446DF4);
const _color3 = Color.fromARGB(255, 26, 132, 247);
const _color4 = Color.fromARGB(255, 19, 68, 228);
const _colors = [_color1, _color2];
const _colorsDarken = [_color2, _color3];
const _disabled = Color(0xFF6B7280);
const _background = Color(0xFF16191D);
const _disabledBackground = Color(0xFF16191D + 0x66FFFFFF);
@ -38,19 +41,31 @@ class Buttons extends StatelessWidget {
style: Theme.of(context).textTheme.titleLarge,
),
const Gap(20),
const OutlinedButton(
label: TextWrapper('Voir notre savoir faire', gradient: _colors),
style: OutlinedButtonStyle(
const FlatButton(
label: TextWrapper('Voir notre savoir faire'),
normalStyle: FlatButtonStyle(
foregroundColors: MultiColor(_colors),
backgroundColors: MultiColor.single(Colors.transparent),
borderColors: MultiColor(_colors),
stroke: 3,
),
hoveredStyle: FlatButtonStyle(
foregroundColors: MultiColor.single(Colors.white),
backgroundColors: MultiColor(_colors),
borderColors: MultiColor(_colors),
stroke: 3,
),
tappedStyle: FlatButtonStyle(
foregroundColors: MultiColor.single(Colors.white),
backgroundColors: MultiColor(_colorsDarken),
borderColors: MultiColor(_colorsDarken),
stroke: 3,
),
prefix: Icon(
Icons.arrow_forward_rounded,
color: _color1,
),
suffix: Icon(
Icons.arrow_forward_rounded,
color: _color2,
),
),
const Gap(20),
@ -60,7 +75,7 @@ class Buttons extends StatelessWidget {
style:
context.textTheme.titleLarge?.copyWith(color: Colors.white),
),
style: const FlatButtonStyle(
normalStyle: const FlatButtonStyle(
backgroundColors: MultiColor(_colors),
),
prefix: const Icon(
@ -82,13 +97,13 @@ class Buttons extends StatelessWidget {
Column(
children: [
const Gap(20),
OutlinedButton(
FlatButton(
label: TextWrapper(
'Démarrer mon projet',
style: context.textTheme.titleLarge
?.copyWith(color: _disabled),
),
style: const OutlinedButtonStyle(
normalStyle: const FlatButtonStyle(
borderColors: MultiColor(_disabledColors),
backgroundColors:
MultiColor.single(_disabledBackground),
@ -102,7 +117,7 @@ class Buttons extends StatelessWidget {
style: context.textTheme.titleLarge
?.copyWith(color: Colors.white),
),
style: const FlatButtonStyle(
normalStyle: const FlatButtonStyle(
backgroundColors: MultiColor(_colors),
),
),
@ -113,13 +128,13 @@ class Buttons extends StatelessWidget {
Column(
children: [
const Gap(20),
OutlinedButton(
FlatButton(
label: TextWrapper(
'Aller voir nos réalisations',
style: context.textTheme.titleLarge
?.copyWith(color: Colors.white),
),
style: const OutlinedButtonStyle(
normalStyle: const FlatButtonStyle(
borderColors: MultiColor(_colors),
backgroundColors:
MultiColor.single(_disabledBackground),
@ -137,7 +152,7 @@ class Buttons extends StatelessWidget {
style: context.textTheme.titleLarge
?.copyWith(color: Colors.white),
),
style: const FlatButtonStyle(
normalStyle: const FlatButtonStyle(
backgroundColors: MultiColor(_colors),
),
suffix: const Icon(
@ -152,6 +167,30 @@ class Buttons extends StatelessWidget {
),
),
),
const Gap(20),
FlatButton(
onPressed: () => print('pressed'),
label: TextWrapper(
'Démarrer mon projet',
style:
context.textTheme.titleLarge?.copyWith(color: Colors.white),
),
disabledStyle: const FlatButtonStyle(
backgroundColors: MultiColor.single(Colors.blue),
),
normalStyle: const FlatButtonStyle(
backgroundColors: MultiColor(_colors),
),
hoveredStyle: const FlatButtonStyle(
backgroundColors: MultiColor.single(Colors.green),
),
focusedStyle: const FlatButtonStyle(
backgroundColors: MultiColor.single(Colors.yellow),
),
tappedStyle: const FlatButtonStyle(
backgroundColors: MultiColor.single(Colors.red),
),
),
],
);
}

View File

@ -15,4 +15,3 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export './flat_button/flat_button.dart';
export './outlined_button/outlined_button.dart';

View File

@ -0,0 +1,65 @@
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
class ButtonCubit extends Cubit<ControlState> {
ButtonCubit() : super(ControlState.normal);
FutureOr<void> onMouseEnter() async {
if (state == ControlState.disabled) {
return;
}
emit(ControlState.hovered);
}
FutureOr<void> onMouseLeave() async {
if (state == ControlState.disabled) {
return;
}
emit(ControlState.normal);
}
FutureOr<void> onFocus() async {
if (state == ControlState.disabled) {
return;
}
emit(ControlState.focused);
}
FutureOr<void> onUnfocus() async {
if (state == ControlState.disabled) {
return;
}
emit(ControlState.normal);
}
FutureOr<void> onClickDown() async {
if (state == ControlState.disabled) {
return;
}
emit(ControlState.tapped);
}
FutureOr<void> onClickUpIn() async {
if (state == ControlState.disabled) {
return;
}
emit(ControlState.hovered);
}
FutureOr<void> onClickUpOut() async {
if (state == ControlState.disabled) {
return;
}
emit(ControlState.normal);
}
FutureOr<void> disable() async {
emit(ControlState.disabled);
}
FutureOr<void> enable() async {
emit(ControlState.normal);
}
}

View File

@ -15,12 +15,9 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart' hide ButtonStyle;
import 'package:gap/gap.dart';
import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
import 'package:wyatt_ui_kit/src/components/gradients/gradient_text.dart';
import 'package:wyatt_ui_kit/src/core/extensions/theme_extensions.dart';
import 'package:wyatt_ui_kit/src/core/helpers/linear_gradient_helper.dart';
import 'package:wyatt_ui_kit/src/components/buttons/flat_button/flat_button_screen.dart';
part 'flat_button.g.dart';
@ -30,61 +27,42 @@ class FlatButton extends FlatButtonComponent with $FlatButtonCWMixin {
super.prefix,
super.suffix,
super.label,
super.state,
super.style,
super.disabledStyle,
super.normalStyle,
super.hoveredStyle,
super.focusedStyle,
super.tappedStyle,
super.onPressed,
super.key,
});
@override
FlatButtonStyle? get style => super.style as FlatButtonStyle?;
FlatButtonStyle? get disabledStyle => super.disabledStyle as FlatButtonStyle?;
@override
Widget build(BuildContext context) => Material(
color: const Color(0x00000000),
child: Ink(
decoration: BoxDecoration(
color: (style != null)
? style!.backgroundColors?.color
: Theme.of(context).buttonTheme.colorScheme?.onPrimary,
gradient: (style?.backgroundColors?.isGradient ?? false)
? LinearGradient(
colors: style!.backgroundColors!.colors,
)
: null,
boxShadow: [
if (style?.shadow != null) ...[style!.shadow!]
],
borderRadius: BorderRadius.all(
Radius.circular(style?.radius ?? 0),
),
),
child: ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 88,
minHeight: 36,
), // min sizes for Material buttons
child: Padding(
padding: EdgeInsets.all(style?.padding ?? 0),
child: Row(
mainAxisSize: mainAxisSize ?? MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
prefix ?? const SizedBox.shrink(),
Gap(style?.padding ?? 10),
if (label != null) ...[
Text(
label!.text,
style: label!.style ?? context.textTheme.titleLarge,
).toGradient(
LinearGradientHelper.fromNullableColors(label!.gradient),
)
],
Gap(style?.padding ?? 10),
suffix ?? const SizedBox.shrink(),
],
),
),
),
),
FlatButtonStyle? get normalStyle => super.normalStyle as FlatButtonStyle?;
@override
FlatButtonStyle? get hoveredStyle => super.hoveredStyle as FlatButtonStyle?;
@override
FlatButtonStyle? get focusedStyle => super.focusedStyle as FlatButtonStyle?;
@override
FlatButtonStyle? get tappedStyle => super.tappedStyle as FlatButtonStyle?;
@override
Widget build(BuildContext context) => FlatButtonScreen(
prefix: prefix,
suffix: suffix,
label: label,
disabledStyle: disabledStyle,
normalStyle: normalStyle,
hoveredStyle: hoveredStyle,
focusedStyle: focusedStyle,
tappedStyle: tappedStyle,
onPressed: onPressed,
mainAxisSize: mainAxisSize,
key: key,
);
}

View File

@ -16,9 +16,23 @@ class $FlatButtonCWProxyImpl implements $FlatButtonComponentCWProxy {
@override
FlatButton label(TextWrapper? label) => this(label: label);
@override
FlatButton state(ControlState? state) => this(state: state);
FlatButton disabledStyle(ButtonStyle? disabledStyle) =>
this(disabledStyle: disabledStyle);
@override
FlatButton style(ButtonStyle? style) => this(style: style);
FlatButton normalStyle(ButtonStyle? normalStyle) =>
this(normalStyle: normalStyle);
@override
FlatButton hoveredStyle(ButtonStyle? hoveredStyle) =>
this(hoveredStyle: hoveredStyle);
@override
FlatButton focusedStyle(ButtonStyle? focusedStyle) =>
this(focusedStyle: focusedStyle);
@override
FlatButton tappedStyle(ButtonStyle? tappedStyle) =>
this(tappedStyle: tappedStyle);
@override
FlatButton onPressed(void Function()? onPressed) =>
this(onPressed: onPressed);
@override
FlatButton mainAxisSize(MainAxisSize? mainAxisSize) =>
this(mainAxisSize: mainAxisSize);
@ -29,8 +43,12 @@ class $FlatButtonCWProxyImpl implements $FlatButtonComponentCWProxy {
Widget? prefix,
Widget? suffix,
TextWrapper? label,
ControlState? state,
ButtonStyle? style,
ButtonStyle? disabledStyle,
ButtonStyle? normalStyle,
ButtonStyle? hoveredStyle,
ButtonStyle? focusedStyle,
ButtonStyle? tappedStyle,
void Function()? onPressed,
MainAxisSize? mainAxisSize,
Key? key,
}) =>
@ -38,8 +56,12 @@ class $FlatButtonCWProxyImpl implements $FlatButtonComponentCWProxy {
prefix: prefix ?? _value.prefix,
suffix: suffix ?? _value.suffix,
label: label ?? _value.label,
state: state ?? _value.state,
style: style ?? _value.style,
disabledStyle: disabledStyle ?? _value.disabledStyle,
normalStyle: normalStyle ?? _value.normalStyle,
hoveredStyle: hoveredStyle ?? _value.hoveredStyle,
focusedStyle: focusedStyle ?? _value.focusedStyle,
tappedStyle: tappedStyle ?? _value.tappedStyle,
onPressed: onPressed ?? _value.onPressed,
key: key ?? _value.key,
);
}

View File

@ -0,0 +1,234 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart' hide ButtonStyle;
import 'package:flutter/services.dart';
import 'package:gap/gap.dart';
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart';
import 'package:wyatt_ui_kit/src/components/gradients/gradient_box_border.dart';
import 'package:wyatt_ui_kit/src/components/gradients/gradient_text.dart';
import 'package:wyatt_ui_kit/src/core/extensions/theme_extensions.dart';
import 'package:wyatt_ui_kit/src/core/helpers/linear_gradient_helper.dart';
class FlatButtonScreen extends CubitScreen<ButtonCubit, ControlState> {
const FlatButtonScreen({
this.prefix,
this.suffix,
this.label,
this.disabledStyle,
this.normalStyle,
this.hoveredStyle,
this.focusedStyle,
this.tappedStyle,
this.onPressed,
this.mainAxisSize,
super.key,
});
final Widget? prefix;
final Widget? suffix;
final TextWrapper? label;
final FlatButtonStyle? disabledStyle;
final FlatButtonStyle? normalStyle;
final FlatButtonStyle? hoveredStyle;
final FlatButtonStyle? focusedStyle;
final FlatButtonStyle? tappedStyle;
final VoidCallback? onPressed;
final MainAxisSize? mainAxisSize;
@override
ButtonCubit create(BuildContext context) => ButtonCubit();
@override
Widget onBuild(BuildContext context, ControlState state) {
// Set a default style
FlatButtonStyle? style = normalStyle ?? const FlatButtonStyle();
switch (state) {
case ControlState.disabled:
style = disabledStyle ?? style;
break;
case ControlState.hovered:
style = hoveredStyle ?? style;
break;
case ControlState.tapped:
style = tappedStyle ?? style;
break;
case ControlState.focused:
style = focusedStyle ?? style;
break;
case ControlState.normal:
case ControlState.selected:
case ControlState.invalid:
break;
}
return Focus(
onFocusChange: (hasFocus) =>
hasFocus ? bloc(context).onFocus() : bloc(context).onUnfocus(),
onKeyEvent: (focusNode, event) {
if (event.logicalKey == LogicalKeyboardKey.enter && state.isFocused()) {
onPressed?.call();
bloc(context).onClickUpOut();
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
},
child: MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (event) {
bloc(context).onMouseEnter();
},
onExit: (event) {
bloc(context).onMouseLeave();
},
child: GestureDetector(
onTapDown: (details) {
bloc(context).onClickDown();
},
onTapUp: (details) {
onPressed?.call();
bloc(context).onClickUpIn();
},
onTapCancel: () {
onPressed?.call();
bloc(context).onClickUpOut();
},
child: Material(
color: const Color(0x00000000),
child: Ink(
decoration: BoxDecoration(
color: style.backgroundColors?.color ??
context.colorScheme.primary,
// If no border color => no default value
border: (style.borderColors != null)
? (style.borderColors?.isGradient ?? false)
? GradientBoxBorder(
gradient: LinearGradient(
colors: style.borderColors!.colors,
),
width: style.stroke ?? 2,
)
: Border.all(
color: style.borderColors!.color,
width: style.stroke ?? 2,
)
: null,
// if no gradient colors => no default value
gradient: (style.backgroundColors?.isGradient ?? false)
? LinearGradient(
colors: style.backgroundColors!.colors,
)
: null,
boxShadow: [
if (style.shadow != null) ...[style.shadow!]
],
borderRadius: BorderRadius.all(
Radius.circular(style.radius ?? 0),
),
),
child: ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 88,
minHeight: 36,
), // min sizes for Material buttons
child: Padding(
padding: EdgeInsets.all(style.padding ?? 0),
child: Row(
mainAxisSize: mainAxisSize ?? MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Builder(
builder: (context) {
final color = style?.foregroundColors?.color;
if (color != null) {
return ColorFiltered(
colorFilter:
ColorFilter.mode(color, BlendMode.srcIn),
child: prefix ?? const SizedBox.shrink(),
);
} else {
return prefix ?? const SizedBox.shrink();
}
},
),
Gap(style.padding ?? 10),
// Choose color
// label.style.color ??
// buttonStyle.foregroundColor.color ??
// context.textTheme.titleLarge.color
//
// Choose gradient
// label.gradient ??
// buttonStyle.foregroundColor.colors ??
// null
if (label != null) ...[
Builder(
builder: (context) {
final color = label?.style?.color ??
style?.foregroundColors?.color ??
context.textTheme.titleLarge?.color;
final buttonStyleGradient =
(style?.foregroundColors?.isGradient ?? false)
? style?.foregroundColors?.colors
: null;
final gradient =
label?.gradient ?? buttonStyleGradient;
return Text(
label!.text,
style:
(label!.style ?? context.textTheme.titleLarge)
?.copyWith(color: color),
).toGradient(
LinearGradientHelper.fromNullableColors(
gradient,
),
);
},
),
],
Gap(style.padding ?? 10),
Builder(
builder: (context) {
final color = style?.foregroundColors?.color;
if (color != null) {
return ColorFiltered(
colorFilter:
ColorFilter.mode(color, BlendMode.srcIn),
child: suffix ?? const SizedBox.shrink(),
);
} else {
return suffix ?? const SizedBox.shrink();
}
},
),
],
),
),
),
),
),
),
),
);
}
}

View File

@ -1,95 +0,0 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart' hide ButtonStyle;
import 'package:gap/gap.dart';
import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
import 'package:wyatt_ui_kit/src/components/gradients/gradient_box_border.dart';
import 'package:wyatt_ui_kit/src/components/gradients/gradient_text.dart';
import 'package:wyatt_ui_kit/src/core/extensions/theme_extensions.dart';
import 'package:wyatt_ui_kit/src/core/helpers/linear_gradient_helper.dart';
part 'outlined_button.g.dart';
@ComponentCopyWithExtension()
class OutlinedButton extends OutlinedButtonComponent
with $OutlinedButtonCWMixin {
const OutlinedButton({
super.prefix,
super.suffix,
super.label,
super.state,
super.style,
super.key,
});
@override
OutlinedButtonStyle? get style => super.style as OutlinedButtonStyle?;
@override
Widget build(BuildContext context) => Material(
color: const Color(0x00000000),
child: Ink(
decoration: BoxDecoration(
color: (style != null)
? style!.backgroundColors?.color
: Theme.of(context).buttonTheme.colorScheme?.onPrimary,
border: (style?.borderColors?.isGradient ?? false)
? GradientBoxBorder(
gradient: LinearGradient(
colors: style!.borderColors!.colors,
),
width: style?.stroke ?? 2,
)
: null,
boxShadow: [
if (style?.shadow != null) ...[style!.shadow!]
],
borderRadius: BorderRadius.all(
Radius.circular(style?.radius ?? 0),
),
),
child: ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 88,
minHeight: 36,
), // min sizes for Material buttons
child: Padding(
padding: EdgeInsets.all(style?.padding ?? 0),
child: Row(
mainAxisSize: mainAxisSize ?? MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
prefix ?? const SizedBox.shrink(),
Gap(style?.padding ?? 10),
if (label != null) ...[
Text(
label!.text,
style: label!.style ?? context.textTheme.titleLarge,
).toGradient(
LinearGradientHelper.fromNullableColors(label!.gradient),
)
],
Gap(style?.padding ?? 10),
suffix ?? const SizedBox.shrink(),
],
),
),
),
),
);
}

View File

@ -1,50 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'outlined_button.dart';
// **************************************************************************
// ComponentCopyWithGenerator
// **************************************************************************
class $OutlinedButtonCWProxyImpl implements $OutlinedButtonComponentCWProxy {
const $OutlinedButtonCWProxyImpl(this._value);
final OutlinedButton _value;
@override
OutlinedButton prefix(Widget? prefix) => this(prefix: prefix);
@override
OutlinedButton suffix(Widget? suffix) => this(suffix: suffix);
@override
OutlinedButton label(TextWrapper? label) => this(label: label);
@override
OutlinedButton state(ControlState? state) => this(state: state);
@override
OutlinedButton style(ButtonStyle? style) => this(style: style);
@override
OutlinedButton mainAxisSize(MainAxisSize? mainAxisSize) =>
this(mainAxisSize: mainAxisSize);
@override
OutlinedButton key(Key? key) => this(key: key);
@override
OutlinedButton call({
Widget? prefix,
Widget? suffix,
TextWrapper? label,
ControlState? state,
ButtonStyle? style,
MainAxisSize? mainAxisSize,
Key? key,
}) =>
OutlinedButton(
prefix: prefix ?? _value.prefix,
suffix: suffix ?? _value.suffix,
label: label ?? _value.label,
state: state ?? _value.state,
style: style ?? _value.style,
key: key ?? _value.key,
);
}
mixin $OutlinedButtonCWMixin on Component {
$OutlinedButtonComponentCWProxy get copyWith =>
$OutlinedButtonCWProxyImpl(this as OutlinedButton);
}

View File

@ -18,4 +18,6 @@ import 'package:flutter/material.dart';
extension ThemeExtension on BuildContext {
TextTheme get textTheme => Theme.of(this).textTheme;
ColorScheme get colorScheme => Theme.of(this).colorScheme;
ButtonThemeData get buttonTheme => Theme.of(this).buttonTheme;
}

View File

@ -0,0 +1,13 @@
@startuml button_state_diagram
[*] --> normal
[*] --> disabled
normal --> hovered : on mouse enter
hovered --> normal : on mouse leave
hovered --> tapped : on mouse click down
tapped --> hovered : on mouse click up in (the cursor is in button)
tapped --> normal : on mouse click up out (the cursor is out of button)
normal --> focused : on focus
focused --> normal : on unfocus
focused --> tapped : on space bar press down
tapped --> focused : on space bar press up
@enduml

View File

@ -9,9 +9,12 @@ environment:
sdk: ">=2.19.0 <3.0.0"
dependencies:
equatable: ^2.0.5
flutter: { sdk: flutter }
gap: ^2.0.1
flutter_animate: ^3.0.0
flutter_bloc: ^8.1.2
gap: ^2.0.1
meta: ^1.8.0
wyatt_bloc_helper:
git:
url: ssh://git@git.wyatt-studio.fr:993/Wyatt-FOSS/wyatt-packages.git