Skip to content

Commit

Permalink
Animating checkbox
Browse files Browse the repository at this point in the history
  • Loading branch information
tilucasoli committed Apr 17, 2024
1 parent b6d2b29 commit 207fcd0
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 64 deletions.
2 changes: 1 addition & 1 deletion demo/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
1 change: 0 additions & 1 deletion demo/lib/components/alert.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:mix/mix.dart';
import 'package:remix_ui/remix_ui.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
Expand Down
2 changes: 1 addition & 1 deletion demo/lib/components/badge.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:mix/mix.dart';
import 'package:remix_ui/remix_ui.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
Expand All @@ -13,6 +12,7 @@ Widget buildCheckboxUseCase(BuildContext context) {
child: RemixBadge(
label: context.knobs.string(
label: "Label",
description: 'The text displayed in the badge',
initialValue: "Label",
),
),
Expand Down
1 change: 0 additions & 1 deletion demo/lib/components/card.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:mix/mix.dart';
import 'package:remix_ui/remix_ui.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;

Expand Down
41 changes: 27 additions & 14 deletions demo/lib/components/checkbox.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:mix/mix.dart';
import 'package:remix_ui/remix_ui.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
Expand All @@ -9,20 +10,32 @@ import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
)
Widget buildCheckboxUseCase(BuildContext context) {
return Center(
child: RemixCheckbox(
label: context.knobs.stringOrNull(
label: 'Title',
initialValue: 'Title',
),
onChanged: (value) {},
checked: context.knobs.boolean(
label: 'Checked',
initialValue: false,
),
disabled: context.knobs.boolean(
label: 'Disabled',
initialValue: false,
),
child: Row(
children: [
Box(
style: Style(
box.color.grey(),
box.height(20),
box.width(20),
box.borderRadius(4),
),
),
RemixCheckbox(
label: context.knobs.stringOrNull(
label: 'Title',
initialValue: 'Title',
),
onChanged: (value) {},
value: context.knobs.boolean(
label: 'Checked',
initialValue: false,
),
disabled: context.knobs.boolean(
label: 'Disabled',
initialValue: false,
),
),
],
),
);
}
75 changes: 42 additions & 33 deletions lib/components/checkbox/checkbox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,47 @@ import 'package:remix_ui/components/checkbox/checkbox.variants.dart';
import 'tokens/checkbox_spec.dart';
import 'tokens/checkbox_util.dart';

final $defaultCheckboxStyle = AnimatedStyle(
Style(
// Flex Container
$remix.checkbox.flexContainer.mainAxisAlignment.center(),
$remix.checkbox.flexContainer.crossAxisAlignment.center(),
$remix.checkbox.flexContainer.mainAxisSize.min(),
$remix.checkbox.flexContainer.gap(6),
final $defaultCheckboxStyle = Style(
// Flex Container
$remix.checkbox.flexContainer.mainAxisAlignment.center(),
$remix.checkbox.flexContainer.crossAxisAlignment.center(),
$remix.checkbox.flexContainer.mainAxisSize.min(),
$remix.checkbox.flexContainer.gap(6),
// Inner Container
$remix.checkbox.innerContainer.borderRadius.all(7),
$remix.checkbox.innerContainer.width(20),
$remix.checkbox.innerContainer.height(20),
$remix.checkbox.innerContainer.border(
color: const Color.fromARGB(115, 3, 3, 3),
width: 1.5,
),
// Label
$remix.checkbox.label.style.fontSize(16),
$remix.checkbox.label.style.color.black87(),

// Checked
CheckboxState.checked(
// Inner Container
$remix.checkbox.innerContainer.borderRadius.all(7),
$remix.checkbox.innerContainer.width(20),
$remix.checkbox.innerContainer.height(20),
$remix.checkbox.innerContainer.border(
color: const Color.fromARGB(115, 3, 3, 3),
width: 1.5,
),
$remix.checkbox.innerContainer.color.black87(),
// Icon
$remix.checkbox.icon.color.white(),
$remix.checkbox.icon.size(15),
// Label
$remix.checkbox.label.style.fontSize(16),
$remix.checkbox.label.style.bold(),
$remix.checkbox.label.style.color.black87(),
// Checked
CheckboxState.checked(
// Inner Container
$remix.checkbox.innerContainer.color.black87(),
// Icon
$remix.checkbox.icon.color.white(),
$remix.checkbox.icon.size(15),
),
),
duration: Durations.extralong2,
curve: Curves.bounceInOut,
).animate(
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 200),
);

class RemixCheckbox extends StatelessWidget {
RemixCheckbox({
super.key,
this.label,
this.disabled = false,
this.checked = false,
this.value = false,
this.onChanged,
this.iconChecked = Icons.check_rounded,
this.iconUnchecked,
Expand All @@ -53,7 +56,7 @@ class RemixCheckbox extends StatelessWidget {

final String? label;
final bool disabled;
final bool checked;
final bool value;
final IconData iconChecked;
final IconData? iconUnchecked;
final ValueChanged<bool>? onChanged;
Expand All @@ -66,29 +69,35 @@ class RemixCheckbox extends StatelessWidget {
Widget build(BuildContext context) {
return MixBuilder(
style: $defaultCheckboxStyle.applyVariant(
checked ? CheckboxState.checked : CheckboxState.unchecked,
value ? CheckboxState.checked : CheckboxState.unchecked,
),
builder: (mix) {
final spec = CheckboxSpec.of(mix);

final duration = mix.animation?.duration ?? Duration.zero;
final curve = mix.animation?.curve ?? Curves.linear;
return Pressable(
onPress:
onChanged == null || disabled ? null : () => onChanged!(!checked),
onChanged == null || disabled ? null : () => onChanged!(!value),
child: MixedFlex(
spec: spec.flexContainer,
direction: Axis.horizontal,
children: [
MixedBox(
AnimatedMixedBox(
spec: spec.innerContainer,
child: MixedIcon(
checked ? iconChecked : iconUnchecked,
duration: duration,
child: AnimatedMixedIcon(
icon: value ? iconChecked : iconUnchecked,
spec: spec.icon,
duration: duration,
curve: curve,
),
),
if (label != null)
MixedText(
AnimatedMixedText(
text: label!,
spec: spec.label,
duration: duration,
curve: curve,
),
],
),
Expand Down
7 changes: 6 additions & 1 deletion lib/components/checkbox/tokens/checkbox_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,10 @@ class CheckboxSpec extends Spec<CheckboxSpec> {
}

@override
List<Object?> get props => [flexContainer, innerContainer, icon, label];
List<Object?> get props => [
flexContainer,
innerContainer,
icon,
label,
];
}
10 changes: 5 additions & 5 deletions lib/components/checkbox/tokens/checkbox_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@ class CheckboxSpecUtility<T extends SpecAttribute>
CheckboxSpecUtility(super.builder);

BoxSpecUtility get innerContainer => BoxSpecUtility(
(innerContainer) => call(innerContainer: innerContainer),
(innerContainer) => only(innerContainer: innerContainer),
);

FlexSpecUtility get flexContainer => FlexSpecUtility(
(flexContainer) => call(flexContainer: flexContainer),
(flexContainer) => only(flexContainer: flexContainer),
);

IconSpecUtility get icon => IconSpecUtility(
(icon) => call(icon: icon),
(icon) => only(icon: icon),
);

TextSpecUtility get label => TextSpecUtility(
(label) => call(label: label),
(label) => only(label: label),
);

@override
T call({
T only({
FlexSpecAttribute flexContainer = const FlexSpecAttribute(),
BoxSpecAttribute innerContainer = const BoxSpecAttribute(),
IconSpecAttribute icon = const IconSpecAttribute(),
Expand Down
12 changes: 6 additions & 6 deletions test/components/checkbox_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ void main() {
await tester.pumpWidget(
MaterialApp(
home: RemixCheckbox(
checked: isChecked,
value: isChecked,
label: 'Checkbox',
onChanged: (value) {
expect(value, !isChecked);
Expand All @@ -32,7 +32,7 @@ void main() {
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: RemixCheckbox(
checked: false,
value: false,
disabled: true,
onChanged: (value) {
didCallOnChanged = true;
Expand All @@ -52,15 +52,15 @@ void main() {
for (var isChecked in [true, false]) {
await tester.pumpWidget(MaterialApp(
home: RemixCheckbox(
checked: isChecked,
value: isChecked,
label: 'Checkbox',
onChanged: (value) {},
),
));

expect(
find.byWidgetPredicate(
(widget) => widget is RemixCheckbox && widget.checked == isChecked,
(widget) => widget is RemixCheckbox && widget.value == isChecked,
),
findsOneWidget,
);
Expand All @@ -72,7 +72,7 @@ void main() {

await tester.pumpWidget(MaterialApp(
home: RemixCheckbox(
checked: false,
value: false,
label: label,
onChanged: (value) {},
),
Expand All @@ -94,7 +94,7 @@ void main() {
for (var isChecked in [true, false]) {
await tester.pumpWidget(MaterialApp(
home: RemixCheckbox(
checked: isChecked,
value: isChecked,
iconChecked: iconChecked,
iconUnchecked: iconUnchecked,
label: 'Checkbox',
Expand Down

0 comments on commit 207fcd0

Please sign in to comment.