Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug : Button group immutability fix #39

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 0 additions & 27 deletions CONTRIBUTING

This file was deleted.

53 changes: 53 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Getting Involved

Thank you for your interest in this project. We'd love to see your contributions. There are just few small guidelines you need to follow.
Please note we have a code of conduct, please follow it in all your interactions with the project.

## Opening an issue

If you've noticed a bug or you have a suggestion for a new feature, please go ahead and open an issue in this project. Please do include as much information as possible.

Please file issues before doing substantial work; this will ensure that others don't duplicate the work and that there's a chance to discuss any design issues.

## Making a code change

We're always open to pull requests, but these should be small and clearly described so that we can understand what you're trying to do.

When you're ready to start coding, fork the needed repository to your own GitHub account and make your changes in a new branch. Once you're happy, open a pull request and explain what the change is and why you think we should include it in our project.

If the change is a bug fix, try to create a test that aligns with the bug.

### Creating a new component

We want the designs to be the source of truth for this repository, so new components will only be accepted if they are with the design files.

New components should use all tokens matching the design, and should not use hardcoded values for color, spacing, or radius. This ensures that changes made to these fundamental tokens are reflected throughout the library.

All components should have inline [dartdoc](https://dart.dev/tools/dart-doc) documentation on public functions and variables. This is enforced by the lint rules.

To demonstrate a component, we need to create 2 examples: firstly in the zeta_flutter example app and secondly in widgetbook.

Example app should show basic examples to compare against the designs and is typically used by developers whilst building out components.

The widgetbook is used by the wider team to review components. We should attempt to show the full functionality of a component in a single Widgetbook instance. We have some helper functions for building knobs for icons and rounded bool in [utils.dart](./example/widgetbook/utils/utils.dart).
For more information on widgetbook, [read the docs](https://docs.widgetbook.io/).

We should also create a test for each widget created.

## Code reviews

All submissions, including submissions by project members, require review. We use GitHub pull requests (PRs) for this purpose. Consult [GitHub Help](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) for more information on using pull requests.

Before a PR can be reviewed, ensure you have done the following, and fixed any issues that may arise:

- Ensure branch is up to date `git rebase main`
- Check formatting: `flutter format .`
- Run static analyses: `flutter analyze`
- Run unit-tests: `flutter test`

All PRs should be titled according to [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), as the branch will be squashed, so the PR title will become the commit message.
Examples:

- `feat(X):` for new features
- `fix(x):` for bug fixes
- `chore(x):` for admin / chores.
14 changes: 10 additions & 4 deletions example/lib/pages/components/button_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,16 @@ class _ButtonExampleState extends State<ButtonExample> {
Column(
children: groupButtons(ZetaWidgetBorder.rounded),
),
Text('Floating Action Buttons', style: ZetaTextStyles.displayMedium),
Text('Tap buttons to change current FAB: ', style: ZetaTextStyles.bodyMedium),
Wrap(children: fabs.divide(SizedBox.square(dimension: 10)).toList()),
].divide(const SizedBox.square(dimension: ZetaSpacing.m)).toList(),
Text('Floating Action Buttons',
style: ZetaTextStyles.displayMedium),
Text('Tap buttons to change current FAB: ',
style: ZetaTextStyles.bodyMedium),
Wrap(
children:
fabs.divide(SizedBox.square(dimension: 10)).toList()),
]
.divide(const SizedBox.square(dimension: ZetaSpacing.m))
.toList(),
),
),
Expanded(child: const SizedBox()),
Expand Down
132 changes: 98 additions & 34 deletions lib/src/components/buttons/button_group.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,21 @@ class ZetaButtonGroup extends StatelessWidget {

/// Returns [ZetaGroupButton]s with there appropriate styling.
List<ZetaGroupButton> getButtons() {
for (final element in buttons) {
element
.._isInitial = element._isFinal = false
.._isLarge = isLarge
.._rounded = rounded
.._isInverse = isInverse;
final List<ZetaGroupButton> mappedButtons = [];

for (final (index, button) in buttons.indexed) {
mappedButtons.add(
button.copyWith(
large: isLarge,
inverse: isInverse,
round: rounded,
fin: index == buttons.length - 1,
initial: index == 0,
),
);
}

buttons.first._isInitial = true;
buttons.last._isFinal = true;

return buttons;
return mappedButtons;
}

@override
Expand All @@ -63,33 +66,58 @@ class ZetaButtonGroup extends StatelessWidget {
// TODO(UX-854): Create country variant.

/// Group Button item
// ignore: must_be_immutable
class ZetaGroupButton extends StatefulWidget {
/// Constructs [ZetaGroupButton]
ZetaGroupButton({
const ZetaGroupButton({
super.key,
this.label,
this.icon,
this.onPressed,
this.dropdown,
});
}) : isFinal = false,
isInitial = false,
isInverse = false,
isLarge = true,
rounded = true;

// const ZetaGroupButton({
// super.key,
// this.label,
// this.icon,
// this.onPressed,
// this.dropdown,
// }) : this.isFinal = false,
// this.isInitial = false,
// this.isInverse = false,
// this.isLarge = true,
// this.rounded = true;

/// Constructs dropdown group button
ZetaGroupButton.dropdown({
const ZetaGroupButton.dropdown({
super.key,
required this.onPressed,
required this.dropdown,
this.icon,
this.label,
this.isFinal = false,
this.isInitial = false,
this.isInverse = false,
this.isLarge = true,
this.rounded = true,
});

///Constructs group button with icon
ZetaGroupButton.icon({
const ZetaGroupButton.icon({
super.key,
required this.icon,
this.dropdown,
this.onPressed,
this.label,
this.isFinal = false,
this.isInitial = false,
this.isInverse = false,
this.isLarge = true,
this.rounded = true,
});

/// Label for [ZetaGroupButton].
Expand All @@ -105,29 +133,38 @@ class ZetaGroupButton extends StatefulWidget {
final Widget? dropdown;

///If [ZetaGroupButton] is large.
bool _isLarge = false;
final bool isLarge;

///If [ZetaGroupButton] is rounded.
bool _rounded = false;
final bool rounded;

/// If [ZetaGroupButton] is the first button in its list.
bool _isInitial = false;
final bool isInitial;

/// If [ZetaGroupButton] is the final button in its list.
bool _isFinal = false;
final bool isFinal;

bool _isInverse = false;
/// If [ZetaGroupButton] is inverse.

final bool isInverse;

@override
State<ZetaGroupButton> createState() => _ZetaGroupButtonState();

/// Returns copy of [ZetaGroupButton] with fields.
ZetaGroupButton copyWith({bool? isFinal, bool? isInitial}) {
ZetaGroupButton copyWith({
bool? fin,
bool? initial,
bool? large,
bool? round,
bool? inverse,
}) {
return ZetaGroupButton(
key: key,
label: label,
icon: icon,
onPressed: onPressed,
dropdown: dropdown,
);
}

Expand All @@ -137,7 +174,12 @@ class ZetaGroupButton extends StatefulWidget {
properties
..add(StringProperty('Label', label))
..add(DiagnosticsProperty<IconData?>('icon', icon))
..add(ObjectFlagProperty<VoidCallback?>.has('onPressed', onPressed));
..add(ObjectFlagProperty<VoidCallback?>.has('onPressed', onPressed))
..add(DiagnosticsProperty<bool>('isInitial', isInitial))
..add(DiagnosticsProperty<bool>('isLarge', isLarge))
..add(DiagnosticsProperty<bool>('rounded', rounded))
..add(DiagnosticsProperty<bool>('isFinal', isFinal))
..add(DiagnosticsProperty<bool>('isInverse', isInverse));
}
}

Expand All @@ -149,20 +191,32 @@ class _ZetaGroupButtonState extends State<ZetaGroupButton> {
super.initState();
controller = MaterialStatesController();
controller.addListener(() {
if (!controller.value.contains(MaterialState.disabled) && context.mounted && mounted) {
if (!controller.value.contains(MaterialState.disabled) &&
context.mounted &&
mounted) {
// TODO(UX-1005): setState causing exception when going from disabled to enabled.
setState(() {});
}
});
}

@override
void didUpdateWidget(ZetaGroupButton oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.onPressed != widget.onPressed) {
setState(() {});
}
}

@override
Widget build(BuildContext context) {
final colors = Zeta.of(context).colors;

final borderType = widget._rounded ? ZetaWidgetBorder.rounded : ZetaWidgetBorder.sharp;
final borderType =
widget.rounded ? ZetaWidgetBorder.rounded : ZetaWidgetBorder.sharp;

final BorderSide borderSide = _getBorderSide(controller.value, colors, false);
final BorderSide borderSide =
_getBorderSide(controller.value, colors, false);

return Container(
decoration: BoxDecoration(
Expand All @@ -172,7 +226,7 @@ class _ZetaGroupButtonState extends State<ZetaGroupButton> {
bottom: borderSide,
right: controller.value.contains(MaterialState.focused)
? BorderSide(color: colors.blue.shade50, width: 2)
: (widget._isFinal)
: (widget.isFinal)
? borderSide
: BorderSide.none,
),
Expand All @@ -190,15 +244,20 @@ class _ZetaGroupButtonState extends State<ZetaGroupButton> {
if (widget.icon != null) Icon(widget.icon),
Text(widget.label!),
if (widget.dropdown != null) // TODO(UX-1006): Dropdown
Icon(widget._rounded ? ZetaIcons.expand_more_round : ZetaIcons.expand_more_sharp, size: 20),
Icon(
widget.rounded
? ZetaIcons.expand_more_round
: ZetaIcons.expand_more_sharp,
size: 20,
),
],
).paddingAll(_padding),
),
),
);
}

double get _padding => widget._isLarge ? ZetaSpacing.x4 : ZetaSpacing.x3;
double get _padding => widget.isLarge ? ZetaSpacing.x4 : ZetaSpacing.x3;

BorderSide _getBorderSide(
Set<MaterialState> states,
Expand All @@ -208,7 +267,7 @@ class _ZetaGroupButtonState extends State<ZetaGroupButton> {
if (states.contains(MaterialState.focused)) {
return BorderSide(color: colors.blue.shade50, width: ZetaSpacing.x0_5);
}
if (widget._isInverse) return BorderSide(color: colors.black);
if (widget.isInverse) return BorderSide(color: colors.black);
if (states.contains(MaterialState.disabled)) {
return BorderSide(color: colors.cool.shade40);
}
Expand All @@ -218,13 +277,13 @@ class _ZetaGroupButtonState extends State<ZetaGroupButton> {
}

BorderRadius _getRadius(ZetaWidgetBorder borderType) {
if (widget._isInitial) {
if (widget.isInitial) {
return borderType.radius.copyWith(
topRight: Radius.zero,
bottomRight: Radius.zero,
);
}
if (widget._isFinal) {
if (widget.isFinal) {
return borderType.radius.copyWith(
topLeft: Radius.zero,
bottomLeft: Radius.zero,
Expand All @@ -241,7 +300,7 @@ class _ZetaGroupButtonState extends State<ZetaGroupButton> {
),
),
backgroundColor: MaterialStateProperty.resolveWith<Color?>((states) {
if (widget._isInverse) return colors.cool.shade100;
if (widget.isInverse) return colors.cool.shade100;

if (states.contains(MaterialState.disabled)) {
return colors.surfaceDisabled;
Expand All @@ -258,7 +317,7 @@ class _ZetaGroupButtonState extends State<ZetaGroupButton> {
if (states.contains(MaterialState.disabled)) {
return colors.textDisabled;
}
if (widget._isInverse) return colors.cool.shade100.onColor;
if (widget.isInverse) return colors.cool.shade100.onColor;
return colors.textDefault;
}),
elevation: const MaterialStatePropertyAll(0),
Expand All @@ -269,6 +328,11 @@ class _ZetaGroupButtonState extends State<ZetaGroupButton> {
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<MaterialStatesController>('controller', controller));
properties.add(
DiagnosticsProperty<MaterialStatesController>(
'controller',
controller,
),
);
}
}
Loading