From af2e234518ab45904fbaf74f4b2be8a32b2c7819 Mon Sep 17 00:00:00 2001 From: Leo Farias Date: Mon, 4 Dec 2023 08:19:44 -0500 Subject: [PATCH] Improvements and clean up (#143) --- .vscode/launch.json | 5 +- .vscode/settings.json | 22 +- analysis_options.yaml | 9 +- coverage/lcov.info | 5780 ++++++++++------- demo/ios/Flutter/AppFrameworkInfo.plist | 2 +- demo/ios/Podfile | 2 +- demo/ios/Podfile.lock | 29 + demo/ios/Runner.xcodeproj/project.pbxproj | 98 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../contents.xcworkspacedata | 3 + demo/ios/Runner/Info.plist | 8 +- demo/lib/components/examples/box/box.mix.dart | 16 +- demo/lib/components/m2_typography.dart | 49 - demo/lib/components/m3_typography.dart | 49 - demo/lib/docs/variants/catalog/pressable.dart | 17 +- demo/lib/docs/variants/default.dart | 8 +- demo/lib/styles.dart | 12 +- demo/lib/views/basic_example.dart | 25 +- demo/lib/views/button_example.dart | 48 +- demo/lib/views/example.dart | 15 +- demo/lib/views/layout_example.dart | 8 +- demo/lib/views/typography_example.dart | 28 +- demo/lib/views/variants.dart | 18 +- demo/macos/Podfile.lock | 2 +- lib/exports.dart | 105 +- lib/src/attributes/alignment_attribute.dart | 123 - lib/src/attributes/attribute.dart | 109 - .../attributes/border/border_attribute.dart | 185 +- lib/src/attributes/border/border_dto.dart | 189 + .../border/border_radius_attribute.dart | 376 +- .../attributes/border/border_radius_dto.dart | 113 + .../attributes/border/border_radius_util.dart | 413 ++ lib/src/attributes/border/border_util.dart | 472 ++ lib/src/attributes/color/color_attribute.dart | 21 + lib/src/attributes/color/color_dto.dart | 29 + lib/src/attributes/color/color_util.dart | 17 + lib/src/attributes/color_attribute.dart | 38 - .../constraints/constraints_attribute.dart | 56 + .../constraints_dto.dart} | 66 +- .../constraints/constraints_util.dart | 102 + .../decoration/decoration_attribute.dart | 85 + .../attributes/decoration/decoration_dto.dart | 150 + .../decoration/decoration_util.dart | 168 + lib/src/attributes/decoration_attribute.dart | 125 - lib/src/attributes/edge_insets_attribute.dart | 100 - .../gradient/gradient_attribute.dart | 52 + lib/src/attributes/gradient/gradient_dto.dart | 404 ++ .../attributes/gradient/gradient_util.dart | 676 ++ lib/src/attributes/scalar_attribute.dart | 240 - lib/src/attributes/scalars/scalar_util.dart | 761 +++ .../attributes/scalars/scalars_attribute.dart | 86 + lib/src/attributes/shadow/shadow_dto.dart | 114 + lib/src/attributes/shadow/shadow_util.dart | 244 + lib/src/attributes/shadow_attribute.dart | 75 - lib/src/attributes/space_attribute.dart | 231 - .../attributes/spacing/edge_insets_dto.dart | 34 + .../attributes/spacing/spacing_attribute.dart | 48 + lib/src/attributes/spacing/spacing_dto.dart | 77 + lib/src/attributes/spacing/spacing_util.dart | 658 ++ .../strut_style/strut_style_attribute.dart | 15 + .../strut_style_dto.dart} | 29 +- .../strut_style/strut_style_util.dart | 65 + lib/src/attributes/style_mix_attribute.dart | 29 +- lib/src/attributes/text_directives_util.dart | 30 + .../text_style/text_style_attribute.dart | 17 + .../attributes/text_style/text_style_dto.dart | 302 + .../text_style/text_style_util.dart | 504 ++ lib/src/attributes/text_style_attribute.dart | 160 - lib/src/attributes/variant_attribute.dart | 35 +- lib/src/core/attribute.dart | 135 + lib/src/core/attributes_map.dart | 125 +- lib/src/core/constants.dart | 2 - lib/src/core/directive.dart | 56 + lib/src/core/extensions/iterable_ext.dart | 52 + lib/src/core/extensions/values_ext.dart | 163 + lib/src/decorators/clip_decorator.dart | 203 +- lib/src/decorators/decorator.dart | 26 +- lib/src/decorators/default_decorators.dart | 113 +- lib/src/deprecations.dart | 322 +- .../{directives => }/controllers.dart | 50 +- .../directives/{directives => }/counter.dart | 0 lib/src/directives/directive_attribute.dart | 6 - .../directives/{directives => }/glitch.dart | 0 lib/src/directives/text_directive.dart | 81 - lib/src/factory/mix_provider.dart | 70 +- lib/src/factory/mix_provider_data.dart | 118 +- lib/src/factory/style_mix.dart | 98 +- .../extensions => factory}/style_mix_ext.dart | 12 +- .../{extensions => }/build_context_ext.dart | 8 +- .../equality => helpers}/compare_mixin.dart | 43 +- .../deep_collection_equality.dart | 66 +- lib/src/helpers/extensions/iterable_ext.dart | 20 - lib/src/helpers/extensions/values_ext.dart | 406 -- lib/src/helpers/lerp_helpers.dart | 35 + .../helpers/{extensions => }/string_ext.dart | 36 + .../container/container_attribute.dart | 105 + .../container}/container_spec.dart | 64 +- lib/src/recipes/container/container_util.dart | 327 + .../recipes/container/container_widget.dart | 55 + lib/src/recipes/flex/flex_attribute.dart | 78 + .../{specs => recipes/flex}/flex_spec.dart | 71 +- lib/src/recipes/flex/flex_util.dart | 90 + lib/src/recipes/flex/flex_widget.dart | 249 + lib/src/recipes/icon/icon_attribute.dart | 33 + lib/src/recipes/icon/icon_spec.dart | 47 + lib/src/recipes/icon/icon_util.dart | 27 + lib/src/recipes/icon/icon_widget.dart | 84 + lib/src/recipes/image/image_attribute.dart | 50 + .../{specs => recipes/image}/image_spec.dart | 35 +- lib/src/recipes/image/image_util.dart | 45 + lib/src/recipes/stack/stack_attribute.dart | 51 + .../{specs => recipes/stack}/stack_spec.dart | 26 +- lib/src/recipes/stack/stack_util.dart | 62 + lib/src/recipes/stack/stack_widget.dart | 79 + lib/src/recipes/text/text_attribute.dart | 98 + .../{specs => recipes/text}/text_spec.dart | 92 +- lib/src/recipes/text/text_util.dart | 130 + lib/src/recipes/text/text_widget.dart | 51 + lib/src/specs/icon_spec.dart | 52 - lib/src/theme/mix_theme.dart | 192 +- lib/src/theme/tokens/breakpoints.dart | 38 +- lib/src/theme/tokens/color_token.dart | 31 +- lib/src/theme/tokens/material_tokens.dart | 302 +- lib/src/theme/tokens/mix_token.dart | 64 +- lib/src/theme/tokens/radius_token.dart | 50 +- lib/src/theme/tokens/space_token.dart | 57 +- lib/src/theme/tokens/text_style_token.dart | 116 +- lib/src/theme/tokens/token_util.dart | 37 +- lib/src/utils/alignment_util.dart | 72 - lib/src/utils/border_radius_util.dart | 101 - lib/src/utils/border_util.dart | 199 - lib/src/utils/box_constraints_util.dart | 21 - lib/src/utils/context_variant_util.dart | 104 - .../on_breakpoint_util.dart | 64 + .../on_brightness_util.dart | 27 + .../on_directionality_util.dart | 24 + .../context_variant_util/on_helper_util.dart | 17 + .../on_orientation_util.dart | 29 + lib/src/utils/decoration_util.dart | 10 - lib/src/utils/decorators_util.dart | 61 +- lib/src/utils/gradient_util.dart | 51 - lib/src/utils/pressable_util.dart | 14 +- lib/src/utils/scalar_util.dart | 55 - lib/src/utils/space_util.dart | 156 - lib/src/utils/text_directives_util.dart | 8 - lib/src/utils/text_util.dart | 81 - lib/src/variants/context_variant.dart | 36 +- lib/src/variants/multi_variant.dart | 154 +- lib/src/variants/variant.dart | 70 +- lib/src/widgets/container_widget.dart | 68 - lib/src/widgets/flex_widget.dart | 111 - lib/src/widgets/gap_widget.dart | 73 + lib/src/widgets/icon_widget.dart | 65 - lib/src/widgets/stack_widget.dart | 54 - lib/src/widgets/styled_widget.dart | 9 +- lib/src/widgets/text_widget.dart | 45 - test/helpers/attribute_generator.dart | 395 +- test/helpers/equatable_mixin_test.dart | 2 +- test/helpers/string_ext_test.dart | 2 +- test/helpers/testing_utils.dart | 95 +- .../attributes/alignment_attribute_test.dart | 154 - .../border/border_attribute_test.dart | 246 +- .../attributes/border/border_dto_test.dart | 158 + .../border/border_radius_attribute_test.dart | 424 -- .../border/border_radius_dto_test.dart | 227 + .../border/border_radius_util_test.dart | 209 + .../attributes/border/border_util_test.dart | 233 + test/src/attributes/color/color_dto_test.dart | 31 + test/src/attributes/color_attribute_test.dart | 36 - .../constraints_attribute_test.dart | 82 + .../constraints/constraints_dto_test.dart | 80 + .../constraints/constraints_util_test.dart | 56 + .../constraints_attribute_test.dart | 59 - .../decoration/decoration_attribute_test.dart | 79 + .../decoration/decoration_dto_test.dart | 100 + .../decoration/decoration_util_test.dart | 98 + .../attributes/decoration_attribute_test.dart | 84 - .../edge_insets_attribute_test.dart | 95 - .../gradient/gradient_dto_test.dart | 1 + .../gradient/gradient_util_test.dart | 568 ++ .../src/attributes/scalar_attribute_test.dart | 269 - .../attributes/scalars/scalar_util_test.dart | 221 + .../scalars/scalars_attribute_test.dart | 95 + .../shadow_dto_test.dart} | 48 +- test/src/attributes/space_attribute_test.dart | 274 - .../spacing/spacing_attribute_test.dart | 235 + .../attributes/spacing/spacing_dto_test.dart | 53 + .../attributes/spacing/spacing_util_test.dart | 611 ++ .../strut_style_attribute_test.dart | 71 + .../strut_style_dto_test.dart} | 62 +- .../strut_style/strut_style_util_test.dart | 85 + .../attributes/style_mix_attribute_test.dart | 21 +- .../attributes/text_directives_util_test.dart | 84 + .../text_style_dto_test.dart} | 43 +- .../text_style/text_style_util_test.dart | 230 + .../extensions/string_ext_test.dart | 2 +- test/src/core/extensions/values_ext_test.dart | 248 + test/src/decorators/clip_decorator_test.dart | 284 +- .../decorators/default_decorators_test.dart | 20 +- test/src/directives/text_directive_test.dart | 91 - test/src/factory/mix_provider_data_test.dart | 30 +- .../style_mix_ext_test.dart | 26 +- test/src/factory/style_mix_test.dart | 17 +- .../build_context_ext_test.dart | 1 + .../deep_collection_equality_test.dart | 3 +- .../helpers/extensions/values_ext_test.dart | 338 - .../{extensions => }/iterable_ext_test.dart | 2 +- test/src/helpers/lerp_helpers_test.dart | 20 + test/src/helpers/string_ext_test.dart | 150 + .../container}/container_spec_test.dart | 51 +- .../container/container_widget_test.dart | 48 + .../flex}/flex_spec_test.dart | 30 +- test/src/recipes/icon/icon_spec_test.dart | 55 + .../{specs => recipes}/icon_spec_test.dart | 12 +- .../stack}/stack_spec_test.dart | 22 +- test/src/recipes/stack/stack_widget_test.dart | 73 + .../text}/text_spec_test.dart | 73 +- test/src/theme/mix_theme_test.dart | 35 + test/src/theme/tokens/breakpoints_test.dart | 61 +- test/src/theme/tokens/color_token_test.dart | 70 +- .../theme/tokens/material_tokens_test.dart | 323 +- test/src/theme/tokens/radius_token_test.dart | 85 +- test/src/theme/tokens/space_token_test.dart | 66 +- .../theme/tokens/text_style_token_test.dart | 54 +- test/src/utils/alignment_util_test.dart | 120 - test/src/utils/border_radius_util_test.dart | 197 - test/src/utils/border_util_test.dart | 126 - test/src/utils/box_constraints_util_test.dart | 74 - .../on_breakpoint_util_test.dart | 60 + .../on_brightness_util_test.dart | 29 + .../on_directionality_util_test.dart | 29 + .../on_helper_util_test.dart | 23 + .../on_orientation_util_test.dart | 32 + test/src/utils/context_variant_util_test.dart | 123 - test/src/utils/decorators_util_test.dart | 36 +- test/src/utils/gradient_util_test.dart | 57 - test/src/utils/space_util_test.dart | 548 -- test/src/utils/text_util_test.dart | 89 - test/src/variants/multi_variant_test.dart | 20 +- test/src/widgets/container_widget_test.dart | 53 - test/src/widgets/gap_widget_test.dart | 122 + test/src/widgets/stack_widget_test.dart | 125 - tools/update_exports.dart | 19 +- .../pages/docs/concepts/mixable-widgets.mdx | 8 +- website/pages/docs/concepts/mixing.mdx | 19 +- website/pages/docs/concepts/variants.mdx | 62 +- .../docs/introduction/getting-started.mdx | 4 +- 247 files changed, 19421 insertions(+), 11856 deletions(-) create mode 100644 demo/ios/Podfile.lock delete mode 100644 demo/lib/components/m2_typography.dart delete mode 100644 demo/lib/components/m3_typography.dart delete mode 100644 lib/src/attributes/alignment_attribute.dart delete mode 100644 lib/src/attributes/attribute.dart create mode 100644 lib/src/attributes/border/border_dto.dart create mode 100644 lib/src/attributes/border/border_radius_dto.dart create mode 100644 lib/src/attributes/border/border_radius_util.dart create mode 100644 lib/src/attributes/border/border_util.dart create mode 100644 lib/src/attributes/color/color_attribute.dart create mode 100644 lib/src/attributes/color/color_dto.dart create mode 100644 lib/src/attributes/color/color_util.dart delete mode 100644 lib/src/attributes/color_attribute.dart create mode 100644 lib/src/attributes/constraints/constraints_attribute.dart rename lib/src/attributes/{constraints_attribute.dart => constraints/constraints_dto.dart} (51%) create mode 100644 lib/src/attributes/constraints/constraints_util.dart create mode 100644 lib/src/attributes/decoration/decoration_attribute.dart create mode 100644 lib/src/attributes/decoration/decoration_dto.dart create mode 100644 lib/src/attributes/decoration/decoration_util.dart delete mode 100644 lib/src/attributes/decoration_attribute.dart delete mode 100644 lib/src/attributes/edge_insets_attribute.dart create mode 100644 lib/src/attributes/gradient/gradient_attribute.dart create mode 100644 lib/src/attributes/gradient/gradient_dto.dart create mode 100644 lib/src/attributes/gradient/gradient_util.dart delete mode 100644 lib/src/attributes/scalar_attribute.dart create mode 100644 lib/src/attributes/scalars/scalar_util.dart create mode 100644 lib/src/attributes/scalars/scalars_attribute.dart create mode 100644 lib/src/attributes/shadow/shadow_dto.dart create mode 100644 lib/src/attributes/shadow/shadow_util.dart delete mode 100644 lib/src/attributes/shadow_attribute.dart delete mode 100644 lib/src/attributes/space_attribute.dart create mode 100644 lib/src/attributes/spacing/edge_insets_dto.dart create mode 100644 lib/src/attributes/spacing/spacing_attribute.dart create mode 100644 lib/src/attributes/spacing/spacing_dto.dart create mode 100644 lib/src/attributes/spacing/spacing_util.dart create mode 100644 lib/src/attributes/strut_style/strut_style_attribute.dart rename lib/src/attributes/{strut_style_attribute.dart => strut_style/strut_style_dto.dart} (68%) create mode 100644 lib/src/attributes/strut_style/strut_style_util.dart create mode 100644 lib/src/attributes/text_directives_util.dart create mode 100644 lib/src/attributes/text_style/text_style_attribute.dart create mode 100644 lib/src/attributes/text_style/text_style_dto.dart create mode 100644 lib/src/attributes/text_style/text_style_util.dart delete mode 100644 lib/src/attributes/text_style_attribute.dart create mode 100644 lib/src/core/attribute.dart delete mode 100644 lib/src/core/constants.dart create mode 100644 lib/src/core/directive.dart create mode 100644 lib/src/core/extensions/iterable_ext.dart create mode 100644 lib/src/core/extensions/values_ext.dart rename lib/src/directives/{directives => }/controllers.dart (100%) rename lib/src/directives/{directives => }/counter.dart (100%) delete mode 100644 lib/src/directives/directive_attribute.dart rename lib/src/directives/{directives => }/glitch.dart (100%) delete mode 100644 lib/src/directives/text_directive.dart rename lib/src/{helpers/extensions => factory}/style_mix_ext.dart (91%) rename lib/src/helpers/{extensions => }/build_context_ext.dart (86%) rename lib/src/{core/equality => helpers}/compare_mixin.dart (61%) rename lib/src/{core/equality => helpers}/deep_collection_equality.dart (100%) delete mode 100644 lib/src/helpers/extensions/iterable_ext.dart delete mode 100644 lib/src/helpers/extensions/values_ext.dart create mode 100644 lib/src/helpers/lerp_helpers.dart rename lib/src/helpers/{extensions => }/string_ext.dart (58%) create mode 100644 lib/src/recipes/container/container_attribute.dart rename lib/src/{specs => recipes/container}/container_spec.dart (60%) create mode 100644 lib/src/recipes/container/container_util.dart create mode 100644 lib/src/recipes/container/container_widget.dart create mode 100644 lib/src/recipes/flex/flex_attribute.dart rename lib/src/{specs => recipes/flex}/flex_spec.dart (52%) create mode 100644 lib/src/recipes/flex/flex_util.dart create mode 100644 lib/src/recipes/flex/flex_widget.dart create mode 100644 lib/src/recipes/icon/icon_attribute.dart create mode 100644 lib/src/recipes/icon/icon_spec.dart create mode 100644 lib/src/recipes/icon/icon_util.dart create mode 100644 lib/src/recipes/icon/icon_widget.dart create mode 100644 lib/src/recipes/image/image_attribute.dart rename lib/src/{specs => recipes/image}/image_spec.dart (65%) create mode 100644 lib/src/recipes/image/image_util.dart create mode 100644 lib/src/recipes/stack/stack_attribute.dart rename lib/src/{specs => recipes/stack}/stack_spec.dart (66%) create mode 100644 lib/src/recipes/stack/stack_util.dart create mode 100644 lib/src/recipes/stack/stack_widget.dart create mode 100644 lib/src/recipes/text/text_attribute.dart rename lib/src/{specs => recipes/text}/text_spec.dart (55%) create mode 100644 lib/src/recipes/text/text_util.dart create mode 100644 lib/src/recipes/text/text_widget.dart delete mode 100644 lib/src/specs/icon_spec.dart delete mode 100644 lib/src/utils/alignment_util.dart delete mode 100644 lib/src/utils/border_radius_util.dart delete mode 100644 lib/src/utils/border_util.dart delete mode 100644 lib/src/utils/box_constraints_util.dart delete mode 100644 lib/src/utils/context_variant_util.dart create mode 100644 lib/src/utils/context_variant_util/on_breakpoint_util.dart create mode 100644 lib/src/utils/context_variant_util/on_brightness_util.dart create mode 100644 lib/src/utils/context_variant_util/on_directionality_util.dart create mode 100644 lib/src/utils/context_variant_util/on_helper_util.dart create mode 100644 lib/src/utils/context_variant_util/on_orientation_util.dart delete mode 100644 lib/src/utils/decoration_util.dart delete mode 100644 lib/src/utils/gradient_util.dart delete mode 100644 lib/src/utils/scalar_util.dart delete mode 100644 lib/src/utils/space_util.dart delete mode 100644 lib/src/utils/text_directives_util.dart delete mode 100644 lib/src/utils/text_util.dart delete mode 100644 lib/src/widgets/container_widget.dart delete mode 100644 lib/src/widgets/flex_widget.dart create mode 100644 lib/src/widgets/gap_widget.dart delete mode 100644 lib/src/widgets/icon_widget.dart delete mode 100644 lib/src/widgets/stack_widget.dart delete mode 100644 lib/src/widgets/text_widget.dart delete mode 100644 test/src/attributes/alignment_attribute_test.dart create mode 100644 test/src/attributes/border/border_dto_test.dart delete mode 100644 test/src/attributes/border/border_radius_attribute_test.dart create mode 100644 test/src/attributes/border/border_radius_dto_test.dart create mode 100644 test/src/attributes/border/border_radius_util_test.dart create mode 100644 test/src/attributes/border/border_util_test.dart create mode 100644 test/src/attributes/color/color_dto_test.dart delete mode 100644 test/src/attributes/color_attribute_test.dart create mode 100644 test/src/attributes/constraints/constraints_attribute_test.dart create mode 100644 test/src/attributes/constraints/constraints_dto_test.dart create mode 100644 test/src/attributes/constraints/constraints_util_test.dart delete mode 100644 test/src/attributes/constraints_attribute_test.dart create mode 100644 test/src/attributes/decoration/decoration_attribute_test.dart create mode 100644 test/src/attributes/decoration/decoration_dto_test.dart create mode 100644 test/src/attributes/decoration/decoration_util_test.dart delete mode 100644 test/src/attributes/decoration_attribute_test.dart delete mode 100644 test/src/attributes/edge_insets_attribute_test.dart create mode 100644 test/src/attributes/gradient/gradient_dto_test.dart create mode 100644 test/src/attributes/gradient/gradient_util_test.dart delete mode 100644 test/src/attributes/scalar_attribute_test.dart create mode 100644 test/src/attributes/scalars/scalar_util_test.dart create mode 100644 test/src/attributes/scalars/scalars_attribute_test.dart rename test/src/attributes/{shadow_attribute_test.dart => shadow/shadow_dto_test.dart} (51%) delete mode 100644 test/src/attributes/space_attribute_test.dart create mode 100644 test/src/attributes/spacing/spacing_attribute_test.dart create mode 100644 test/src/attributes/spacing/spacing_dto_test.dart create mode 100644 test/src/attributes/spacing/spacing_util_test.dart create mode 100644 test/src/attributes/strut_style/strut_style_attribute_test.dart rename test/src/attributes/{strut_style_attribute_test.dart => strut_style/strut_style_dto_test.dart} (52%) create mode 100644 test/src/attributes/strut_style/strut_style_util_test.dart create mode 100644 test/src/attributes/text_directives_util_test.dart rename test/src/attributes/{text_style_attribute_test.dart => text_style/text_style_dto_test.dart} (72%) create mode 100644 test/src/attributes/text_style/text_style_util_test.dart rename test/src/{helpers => core}/extensions/string_ext_test.dart (98%) create mode 100644 test/src/core/extensions/values_ext_test.dart delete mode 100644 test/src/directives/text_directive_test.dart rename test/src/{helpers/extensions => factory}/style_mix_ext_test.dart (92%) rename test/src/helpers/{extensions => }/build_context_ext_test.dart (97%) delete mode 100644 test/src/helpers/extensions/values_ext_test.dart rename test/src/helpers/{extensions => }/iterable_ext_test.dart (92%) create mode 100644 test/src/helpers/lerp_helpers_test.dart create mode 100644 test/src/helpers/string_ext_test.dart rename test/src/{specs => recipes/container}/container_spec_test.dart (68%) create mode 100644 test/src/recipes/container/container_widget_test.dart rename test/src/{specs => recipes/flex}/flex_spec_test.dart (83%) create mode 100644 test/src/recipes/icon/icon_spec_test.dart rename test/src/{specs => recipes}/icon_spec_test.dart (70%) rename test/src/{specs => recipes/stack}/stack_spec_test.dart (76%) create mode 100644 test/src/recipes/stack/stack_widget_test.dart rename test/src/{specs => recipes/text}/text_spec_test.dart (67%) create mode 100644 test/src/theme/mix_theme_test.dart delete mode 100644 test/src/utils/alignment_util_test.dart delete mode 100644 test/src/utils/border_radius_util_test.dart delete mode 100644 test/src/utils/border_util_test.dart delete mode 100644 test/src/utils/box_constraints_util_test.dart create mode 100644 test/src/utils/context_variant_util/on_breakpoint_util_test.dart create mode 100644 test/src/utils/context_variant_util/on_brightness_util_test.dart create mode 100644 test/src/utils/context_variant_util/on_directionality_util_test.dart create mode 100644 test/src/utils/context_variant_util/on_helper_util_test.dart create mode 100644 test/src/utils/context_variant_util/on_orientation_util_test.dart delete mode 100644 test/src/utils/gradient_util_test.dart delete mode 100644 test/src/utils/space_util_test.dart delete mode 100644 test/src/utils/text_util_test.dart delete mode 100644 test/src/widgets/container_widget_test.dart create mode 100644 test/src/widgets/gap_widget_test.dart delete mode 100644 test/src/widgets/stack_widget_test.dart diff --git a/.vscode/launch.json b/.vscode/launch.json index a724feb06..bd90200a9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -32,7 +32,10 @@ "name": "Run all Tests", "request": "launch", "type": "dart", - "program": "./test/" + "program": "./test/", + "args": [ + "--coverage" + ] } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 200a0b0da..5665e2fd1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,25 +13,5 @@ }, "files.exclude": { "**/.fvm/versions": true - }, - "workbench.colorCustomizations": { - "activityBar.activeBackground": "#0a1a34", - "activityBar.background": "#0a1a34", - "activityBar.foreground": "#e7e7e7", - "activityBar.inactiveForeground": "#e7e7e799", - "activityBarBadge.background": "#a01f50", - "activityBarBadge.foreground": "#e7e7e7", - "commandCenter.border": "#e7e7e799", - "sash.hoverBorder": "#0a1a34", - "statusBar.background": "#02050a", - "statusBar.foreground": "#e7e7e7", - "statusBarItem.hoverBackground": "#0a1a34", - "statusBarItem.remoteBackground": "#02050a", - "statusBarItem.remoteForeground": "#e7e7e7", - "titleBar.activeBackground": "#02050a", - "titleBar.activeForeground": "#e7e7e7", - "titleBar.inactiveBackground": "#02050a99", - "titleBar.inactiveForeground": "#e7e7e799" - }, - "peacock.color": "#02050a" + } } \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml index eff801de3..c91f61ca6 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -12,8 +12,6 @@ analyzer: exclude: - "**/*.g.dart" - "**/*.freezed.dart" - - dart_code_metrics: extends: @@ -29,6 +27,7 @@ dart_code_metrics: arguments-ordering: child-last: true avoid-dynamic: false + avoid-shadowing: false prefer-getter-over-method: false enum-constants-ordering: false prefer-widget-private-members: false @@ -55,12 +54,12 @@ dart_code_metrics: - private-fields - constructors - static-methods - - public-getters + - private-methods - private-getters - - public-setters - private-setters + - public-getters + - public-setters - public-methods - - private-methods - overridden-public-methods - overridden-public-getters - build-method diff --git a/coverage/lcov.info b/coverage/lcov.info index 89c06eda7..d124677af 100644 --- a/coverage/lcov.info +++ b/coverage/lcov.info @@ -1,1632 +1,2547 @@ -SF:lib/src/core/equality/compare_mixin.dart -DA:5,2 -DA:6,8 -DA:11,23 -DA:14,23 -DA:15,46 -DA:17,37 -DA:18,23 -DA:19,23 -DA:21,28 -DA:22,5 -DA:23,45 -DA:24,2 -DA:25,66 -DA:27,22 -DA:35,23 -DA:36,23 -DA:41,2 -DA:42,2 -DA:43,1 -DA:44,5 -DA:45,2 -DA:46,4 -DA:51,2 -DA:52,0 -DA:54,2 -DA:55,2 -DA:56,2 -DA:59,2 -DA:62,6 -DA:63,8 +SF:lib/src/helpers/compare_mixin.dart +DA:8,4 +DA:9,16 +DA:16,29 +DA:22,29 +DA:23,58 +DA:26,49 +DA:27,29 +DA:28,29 +DA:31,45 +DA:32,16 +DA:35,56 +DA:36,12 +DA:39,75 +DA:43,25 +DA:53,29 +DA:54,29 +DA:60,4 +DA:62,4 +DA:63,2 +DA:64,10 DA:65,4 -DA:68,2 -DA:69,8 -DA:70,4 -DA:72,8 -DA:77,2 -DA:78,6 -DA:80,3 -DA:82,5 -DA:83,1 -DA:85,1 -DA:89,2 -DA:91,2 -DA:100,3 -DA:102,32 -DA:105,25 -DA:106,75 -DA:107,75 -DA:110,3 -DA:111,15 -DA:113,0 -DA:114,0 -DA:117,0 -DA:119,0 -DA:120,0 -DA:121,0 -DA:123,0 -DA:124,0 -DA:125,0 -DA:127,0 -DA:128,0 -DA:129,0 -DA:131,0 -DA:132,0 -DA:133,0 -DA:134,0 -DA:138,0 -DA:144,3 -DA:146,12 +DA:66,8 +DA:72,4 +DA:73,0 +DA:76,4 +DA:77,4 +DA:78,4 +DA:81,4 +DA:85,12 +DA:86,16 +DA:88,8 +DA:92,4 +DA:93,16 +DA:94,8 +DA:96,16 +DA:102,3 +DA:103,9 +DA:105,5 +DA:107,10 +DA:108,1 +DA:110,2 +DA:114,3 +DA:116,3 +DA:127,4 +DA:130,53 +DA:133,49 +DA:134,147 +DA:135,144 +DA:139,9 +DA:140,45 +DA:143,0 +DA:144,0 +DA:147,0 +DA:149,0 +DA:150,0 +DA:151,0 +DA:153,0 +DA:154,0 +DA:155,0 +DA:157,0 +DA:158,0 +DA:159,0 +DA:161,0 +DA:162,0 +DA:163,0 +DA:164,0 +DA:168,0 +DA:175,4 +DA:177,16 LF:69 LH:51 end_of_record -SF:lib/src/core/equality/deep_collection_equality.dart -DA:2,51 -DA:3,3 -DA:5,9 -DA:7,3 -DA:8,2 -DA:9,2 -DA:10,1 -DA:11,2 -DA:12,2 -DA:15,1 -DA:18,1 -DA:19,1 -DA:20,3 -DA:21,2 -DA:22,1 -DA:23,1 -DA:24,1 -DA:26,2 -DA:27,2 -DA:33,1 -DA:36,2 -DA:37,6 -DA:38,2 -DA:39,4 -DA:47,2 -DA:49,6 -DA:51,4 -DA:52,5 -DA:53,6 -DA:64,1 -DA:65,3 -DA:66,2 -DA:67,1 -LF:33 -LH:33 -end_of_record -SF:lib/src/helpers/extensions/iterable_ext.dart -DA:2,30 -DA:4,1 -DA:5,2 -DA:6,1 -DA:14,2 -DA:15,2 -DA:16,2 -LF:7 -LH:7 -end_of_record -SF:lib/src/helpers/extensions/string_ext.dart -DA:5,18 -DA:7,12 -DA:8,6 -DA:9,6 -DA:10,6 -DA:14,6 -DA:15,6 -DA:16,6 -DA:17,12 -DA:19,18 -DA:20,6 -DA:21,30 -DA:23,12 -DA:27,6 +SF:lib/src/helpers/string_ext.dart +DA:25,36 +DA:27,24 +DA:28,12 +DA:29,12 DA:30,12 -DA:31,12 -DA:34,12 -DA:35,6 -DA:42,3 -DA:44,3 -DA:46,2 -DA:47,10 -DA:48,2 -DA:49,6 -DA:52,2 -DA:55,12 -DA:57,3 -DA:58,3 -DA:59,6 -DA:60,6 -DA:62,9 -DA:63,15 -DA:66,4 -DA:68,4 -DA:70,20 -DA:72,2 -DA:73,10 -DA:75,1 -DA:76,2 -DA:77,1 -DA:79,3 -DA:81,1 -DA:86,25 -DA:87,5 +DA:36,12 +DA:37,12 +DA:38,12 +DA:39,24 +DA:41,36 +DA:42,12 +DA:43,60 +DA:45,24 +DA:49,12 +DA:52,24 +DA:53,24 +DA:56,24 +DA:57,12 +DA:65,6 +DA:68,6 +DA:71,3 +DA:72,15 +DA:73,3 +DA:74,9 +DA:77,3 +DA:81,18 +DA:84,4 +DA:85,4 +DA:86,8 +DA:87,8 +DA:89,12 +DA:90,20 +DA:94,8 +DA:97,8 +DA:100,44 +DA:103,3 +DA:104,15 +DA:107,1 +DA:108,2 +DA:109,1 +DA:111,3 +DA:113,1 +DA:120,55 +DA:123,10 LF:44 LH:44 end_of_record -SF:lib/src/helpers/extensions/values_ext.dart -DA:17,1 -DA:18,1 -DA:19,1 -DA:20,1 -DA:21,1 -DA:22,1 -DA:23,1 +SF:lib/src/attributes/border/border_attribute.dart +DA:10,3 +DA:12,3 +DA:14,3 +DA:16,3 +DA:18,3 +DA:20,0 +DA:22,0 DA:24,1 -DA:25,1 -DA:26,1 -DA:30,1 -DA:31,1 -DA:32,2 -DA:33,1 -DA:34,2 -DA:35,2 -DA:36,2 -DA:37,2 -DA:38,1 -DA:39,2 -DA:40,1 +DA:26,4 +DA:29,2 +DA:30,4 +DA:32,1 +DA:33,2 +LF:13 +LH:11 +end_of_record +SF:lib/src/attributes/border/border_dto.dart +DA:21,9 +DA:30,6 +DA:31,6 +DA:32,6 +DA:33,12 +DA:34,12 +DA:35,12 +DA:36,12 +DA:40,2 DA:41,2 -DA:48,2 -DA:52,4 -DA:56,0 -DA:57,0 -DA:58,0 -DA:59,0 -DA:62,0 -DA:65,0 -DA:66,0 -DA:67,0 -DA:68,0 -DA:71,0 -DA:74,0 -DA:75,0 -DA:76,0 -DA:77,0 -DA:80,0 -DA:85,0 -DA:86,0 -DA:87,0 -DA:88,0 -DA:89,0 +DA:42,4 +DA:43,4 +DA:44,4 +DA:45,4 +DA:49,0 +DA:56,3 +DA:57,3 +DA:60,21 +DA:62,11 +DA:64,15 +DA:66,3 +DA:67,6 +DA:70,0 +DA:77,3 +DA:80,3 +DA:81,3 +DA:82,3 +DA:83,3 +DA:84,3 +DA:85,3 +DA:86,3 +DA:87,3 +DA:91,0 DA:92,0 DA:93,0 DA:94,0 DA:95,0 DA:96,0 -DA:99,0 -DA:100,0 -DA:101,0 -DA:102,0 -DA:103,0 -DA:108,0 -DA:109,0 -DA:110,0 -DA:111,0 -DA:112,0 -DA:113,0 -DA:116,0 -DA:117,0 -DA:118,0 -DA:119,0 -DA:120,0 -DA:123,0 -DA:124,0 -DA:125,0 -DA:126,0 -DA:127,0 -DA:132,0 -DA:133,0 -DA:135,0 -DA:136,0 -DA:138,0 -DA:141,0 -DA:142,0 -DA:144,0 -DA:146,0 -DA:148,0 -DA:152,0 -DA:153,0 -DA:154,0 +DA:103,6 +DA:105,6 +DA:106,3 +DA:107,6 +DA:108,6 +DA:109,6 +DA:110,6 +DA:114,6 +DA:115,12 +DA:116,12 +DA:117,12 +DA:118,12 +DA:122,3 +DA:123,21 +DA:133,10 +DA:140,7 +DA:141,7 +DA:142,14 +DA:143,7 +DA:144,7 +DA:145,7 +DA:149,1 +DA:155,1 DA:156,0 -DA:160,0 -DA:161,0 -DA:162,0 -DA:163,0 -DA:164,0 -DA:166,18 -DA:171,0 -DA:172,0 -DA:173,0 -DA:174,0 -DA:176,0 -DA:181,2 -DA:182,1 -DA:183,1 -DA:184,2 -DA:185,5 -DA:190,16 -DA:194,2 -DA:195,6 -DA:199,4 -DA:200,2 -DA:201,2 -DA:202,2 -DA:203,2 -DA:209,2 -DA:214,1 -DA:215,1 -DA:219,2 -DA:223,2 -DA:227,2 -DA:231,2 -DA:235,2 -DA:239,1 -DA:240,1 -DA:245,2 -DA:249,2 -DA:254,2 -DA:259,2 -DA:264,2 -DA:268,2 -DA:269,2 -DA:270,2 -DA:271,2 -DA:272,5 -DA:273,2 -DA:274,2 -DA:279,2 -DA:283,1 -DA:284,2 -DA:285,0 -DA:286,0 -DA:289,0 -DA:294,1 -DA:295,1 -DA:296,1 -DA:297,1 -DA:298,1 -DA:299,1 -DA:305,2 -DA:306,1 -DA:307,1 -DA:308,1 -DA:309,1 -DA:314,2 -DA:318,2 -DA:321,0 -DA:322,0 -DA:324,0 -DA:329,2 -DA:330,2 -DA:331,1 -DA:332,1 -DA:333,1 -DA:338,1 -DA:339,2 -DA:340,0 -DA:341,0 -DA:344,0 -DA:349,0 -DA:350,0 -DA:351,0 -DA:352,0 -DA:357,2 -DA:358,2 -DA:359,1 -DA:360,1 -DA:361,1 -DA:366,2 -DA:367,1 -DA:368,2 -DA:369,1 -DA:370,1 -DA:371,1 -DA:372,1 -DA:373,1 -DA:374,1 -DA:375,1 -DA:376,1 -DA:377,1 -DA:378,1 -DA:379,1 -DA:380,1 -DA:381,1 -DA:382,1 -DA:383,1 -DA:384,1 -DA:385,1 -DA:386,1 -DA:391,2 -DA:392,2 -DA:393,2 -DA:394,2 -DA:395,2 -DA:400,2 -DA:401,2 -DA:402,2 -DA:403,2 -DA:404,2 -LF:202 -LH:119 +DA:157,1 +DA:158,1 +DA:159,0 +DA:163,1 +DA:167,1 +DA:168,3 +DA:169,2 +DA:170,1 +DA:171,1 +DA:175,7 +DA:179,7 +DA:180,15 +DA:181,7 +DA:182,9 +DA:183,10 +DA:187,3 +DA:188,15 +LF:79 +LH:69 end_of_record -SF:lib/src/attributes/attribute.dart -DA:10,106 -DA:20,3 -DA:24,3 +SF:lib/src/attributes/border/border_radius_attribute.dart +DA:12,3 +DA:14,0 +DA:17,0 +DA:18,0 +DA:22,0 DA:25,0 -DA:28,0 -DA:30,0 -DA:31,0 -DA:32,0 -DA:34,0 -DA:35,0 -DA:36,0 -DA:37,0 -DA:39,0 -DA:40,0 -DA:41,0 -DA:44,0 -DA:54,28 -DA:60,9 -DA:62,27 -DA:65,6 -DA:67,6 -DA:69,10 -DA:70,0 -DA:71,10 -DA:74,8 -DA:75,16 -DA:80,102 -DA:86,15 -DA:88,0 -DA:89,0 -DA:91,0 -DA:94,0 -DA:95,0 -DA:98,1 -DA:103,4 -DA:106,4 -DA:107,4 -LF:37 -LH:18 -end_of_record -SF:lib/src/decorators/decorator.dart -DA:7,7 -DA:9,3 -DA:10,6 -DA:20,5 -LF:4 -LH:4 -end_of_record -SF:lib/src/variants/multi_variant.dart -DA:16,3 -DA:18,3 -DA:22,3 -DA:23,15 -DA:24,12 -DA:26,3 +DA:28,2 DA:29,4 -DA:33,2 +DA:31,2 +DA:32,4 DA:34,2 +DA:35,4 DA:37,2 -DA:39,8 +DA:38,4 +DA:40,2 DA:41,4 -DA:42,2 -DA:43,0 +DA:43,2 +DA:44,4 DA:46,2 DA:47,4 -DA:48,6 -DA:49,0 +DA:49,2 +DA:50,4 DA:52,1 -DA:53,2 -DA:55,3 -DA:57,2 -DA:58,0 -DA:62,4 -DA:63,1 +DA:56,4 +DA:59,2 +DA:60,4 +DA:62,2 +DA:63,4 DA:67,1 -DA:90,1 -DA:93,1 -DA:96,2 -DA:99,0 -DA:100,0 -LF:31 -LH:26 -end_of_record -SF:lib/src/attributes/alignment_attribute.dart -DA:13,57 -DA:23,1 -DA:24,4 -DA:56,57 -DA:58,51 -DA:60,1 -DA:62,5 -DA:65,7 -DA:66,21 -DA:108,53 -DA:110,50 -DA:111,1 -DA:112,1 +DA:69,1 +DA:70,1 +DA:71,1 +DA:80,1 +DA:86,1 +DA:87,1 +DA:96,1 +DA:97,1 +DA:105,1 +DA:106,1 DA:114,1 DA:115,2 -DA:116,2 -DA:120,2 -DA:122,6 -LF:18 -LH:18 -end_of_record -SF:lib/src/attributes/border/border_attribute.dart -DA:14,6 -DA:22,0 -DA:23,0 -DA:30,6 -DA:32,1 -DA:33,0 -DA:35,1 -DA:38,0 -DA:45,2 -DA:50,2 -DA:51,6 -DA:52,6 -DA:53,5 -DA:54,5 -DA:58,4 -DA:60,4 -DA:61,8 -DA:62,8 -DA:63,8 -DA:64,8 -DA:68,1 -DA:69,5 -DA:76,3 -DA:83,1 -DA:84,0 -DA:86,0 -DA:89,0 -DA:96,0 -DA:97,0 -DA:98,0 -DA:99,0 -DA:100,0 -DA:101,0 -DA:105,1 -DA:113,1 -DA:114,3 -DA:115,3 -DA:116,3 -DA:117,3 -DA:121,1 +DA:120,1 +DA:122,1 DA:123,1 -DA:124,2 -DA:125,2 -DA:126,2 -DA:127,2 -DA:131,1 -DA:132,5 -DA:142,7 -DA:149,0 -DA:155,0 -DA:156,0 -DA:157,0 -DA:158,0 -DA:159,0 -DA:163,2 -DA:167,1 -DA:168,2 -DA:169,2 -DA:170,2 -DA:171,1 -DA:175,5 -DA:179,5 -DA:180,10 -DA:181,6 -DA:182,7 -DA:183,9 -DA:187,2 -DA:188,10 -LF:68 +DA:124,1 +DA:133,1 +DA:139,1 +DA:140,1 +DA:149,1 +DA:153,1 +DA:161,1 +DA:165,1 +DA:173,1 +DA:174,2 +LF:54 LH:49 end_of_record -SF:lib/src/attributes/border/border_radius_attribute.dart -DA:20,5 -DA:39,1 -DA:40,1 -DA:41,1 -DA:42,1 -DA:43,1 -DA:44,1 -DA:45,1 -DA:46,1 +SF:lib/src/attributes/border/border_radius_dto.dart +DA:20,6 +DA:31,4 +DA:32,4 +DA:33,4 +DA:34,4 +DA:35,4 +DA:36,4 +DA:37,4 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:50,0 +DA:57,3 +DA:58,3 +DA:61,4 +DA:62,4 +DA:63,4 +DA:64,4 +DA:65,4 +DA:67,2 +DA:71,2 +DA:72,3 +DA:73,3 +DA:74,3 +DA:75,3 +DA:76,4 +DA:77,4 +DA:78,4 +DA:79,4 +DA:83,4 +DA:87,4 +DA:88,1 +DA:89,1 +DA:90,1 +DA:91,1 +DA:92,1 +DA:94,4 +DA:95,4 +DA:96,4 +DA:97,4 +DA:98,4 +DA:102,4 +DA:103,4 +DA:104,4 +DA:105,4 +DA:106,4 +DA:107,4 +DA:108,4 +DA:109,4 +DA:110,4 +DA:111,4 +LF:54 +LH:47 +end_of_record +SF:lib/src/attributes/border/border_radius_util.dart +DA:32,66 +DA:33,1 DA:47,1 -DA:48,1 -DA:55,5 -DA:62,0 -DA:64,2 -DA:65,1 -DA:72,1 -DA:73,2 -DA:75,2 -DA:76,1 -DA:83,2 -DA:113,2 -DA:121,2 +DA:48,2 +DA:62,1 +DA:63,3 +DA:77,1 +DA:78,3 +DA:92,1 +DA:93,3 +DA:107,1 +DA:108,3 DA:122,1 -DA:129,1 -DA:133,1 -DA:134,1 -DA:135,2 -DA:136,2 -DA:137,2 -DA:141,4 -DA:143,4 -DA:144,4 -DA:145,4 -DA:146,4 -DA:147,4 -DA:155,4 -DA:162,1 -DA:192,1 -DA:200,1 -DA:201,2 -DA:203,0 -DA:205,2 -DA:206,1 -DA:213,1 -DA:216,0 -DA:223,1 -DA:226,0 -DA:233,1 -DA:239,1 -DA:240,1 +DA:123,3 +DA:142,1 +DA:143,1 +DA:144,2 +DA:159,1 +DA:160,1 +DA:161,2 +DA:176,1 +DA:177,1 +DA:178,2 +DA:193,1 +DA:194,1 +DA:195,2 +DA:200,3 +DA:203,2 +DA:209,4 +DA:210,2 DA:241,2 -DA:242,2 -DA:243,2 -DA:247,3 -DA:249,3 -DA:250,3 -DA:251,3 -DA:252,3 -DA:253,3 +DA:266,2 +DA:267,2 +DA:268,2 +DA:269,2 +DA:270,2 +DA:277,1 +DA:278,1 +DA:291,1 +DA:292,3 +DA:306,1 +DA:307,3 +DA:321,1 +DA:322,3 +DA:336,1 +DA:337,3 +DA:351,1 +DA:352,3 +DA:360,0 +DA:385,0 +DA:386,0 +DA:387,0 +DA:388,0 +DA:389,0 +DA:390,0 +DA:396,1 +DA:402,2 +DA:403,1 +DA:412,3 LF:59 -LH:55 +LH:52 end_of_record -SF:lib/src/attributes/color_attribute.dart -DA:9,16 -DA:11,4 -DA:12,4 -DA:14,12 -DA:16,12 -DA:18,12 -DA:19,0 -DA:26,3 -DA:28,1 -DA:29,1 +SF:lib/src/attributes/border/border_util.dart +DA:37,67 +DA:39,1 +DA:43,1 +DA:51,2 +DA:52,2 +DA:76,2 +DA:77,10 +DA:102,1 +DA:103,3 +DA:128,1 +DA:129,3 +DA:154,1 +DA:155,3 +DA:180,1 +DA:181,3 +DA:204,0 +DA:205,0 +DA:227,0 +DA:250,1 +DA:251,5 +DA:275,1 +DA:276,1 +DA:277,4 +DA:281,0 +DA:282,0 +DA:285,1 +DA:293,2 +DA:294,1 +DA:321,0 +DA:327,0 +DA:359,2 +DA:361,1 +DA:367,3 +DA:392,4 +DA:409,1 +DA:410,3 +DA:424,2 +DA:438,2 +DA:457,2 +DA:463,2 +DA:464,2 +DA:470,4 +LF:42 +LH:35 +end_of_record +SF:lib/src/attributes/color/color_dto.dart +DA:10,24 +DA:12,5 +DA:13,5 +DA:15,20 +DA:17,20 +DA:19,20 +DA:22,3 +DA:24,4 +DA:27,8 +DA:28,16 +LF:10 +LH:10 +end_of_record +SF:lib/src/attributes/constraints/constraints_attribute.dart +DA:11,5 +DA:18,5 +DA:20,0 +DA:21,0 +DA:23,0 +DA:24,0 +DA:27,1 +DA:31,4 DA:34,1 -DA:36,1 -DA:37,1 -LF:13 -LH:12 +DA:36,2 +DA:42,0 +DA:44,0 +DA:45,0 +DA:51,0 +DA:53,0 +DA:54,0 +LF:16 +LH:6 end_of_record -SF:lib/src/attributes/constraints_attribute.dart -DA:10,5 -DA:28,5 -DA:37,1 -DA:41,1 -DA:42,2 +SF:lib/src/attributes/constraints/constraints_util.dart +DA:29,65 +DA:30,0 DA:43,2 -DA:44,1 -DA:45,2 -DA:46,1 -DA:47,2 -DA:51,2 -DA:55,2 -DA:56,2 -DA:57,1 -DA:58,1 -DA:59,2 -DA:60,2 -DA:61,2 -DA:62,2 -DA:63,2 -DA:67,4 -DA:68,0 -DA:69,0 -DA:75,1 -DA:76,5 -LF:25 -LH:23 +DA:57,2 +DA:71,2 +DA:85,2 +DA:87,1 +DA:93,2 +DA:94,1 +LF:9 +LH:8 end_of_record -SF:lib/src/attributes/decoration_attribute.dart -DA:16,7 -DA:33,7 -DA:42,2 -DA:46,2 -DA:47,4 -DA:49,4 -DA:50,4 -DA:51,6 -DA:52,6 -DA:53,4 -DA:57,5 -DA:59,5 -DA:60,9 -DA:61,7 -DA:62,6 -DA:63,7 -DA:64,6 -DA:68,1 -DA:69,7 -DA:85,3 -DA:92,1 -DA:96,1 -DA:97,3 -DA:98,2 -DA:99,2 -DA:100,3 -DA:104,1 -DA:106,1 -DA:107,2 -DA:108,2 -DA:109,1 -DA:110,1 -DA:114,1 -DA:115,5 -DA:120,0 -DA:122,0 -DA:124,0 -LF:37 -LH:34 +SF:lib/src/attributes/decoration/decoration_attribute.dart +DA:12,4 +DA:14,1 +DA:18,5 +DA:20,4 +DA:23,1 +DA:25,2 +LF:6 +LH:6 end_of_record -SF:lib/src/attributes/edge_insets_attribute.dart -DA:20,2 -DA:37,1 -DA:38,7 -DA:43,2 -DA:50,1 -DA:52,1 -DA:53,1 -DA:54,1 -DA:55,1 -DA:56,1 -DA:60,1 -DA:62,1 -DA:63,1 -DA:64,1 +SF:lib/src/attributes/decoration/decoration_util.dart +DA:20,64 +DA:21,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:29,0 +DA:30,0 +DA:31,0 +DA:38,3 +DA:39,2 +DA:41,3 +DA:49,6 +DA:50,3 +DA:61,0 +DA:62,0 DA:65,1 -DA:66,1 -DA:74,2 +DA:66,3 +DA:69,1 +DA:70,1 +DA:71,2 +DA:75,1 +DA:76,3 +DA:79,1 +DA:80,2 DA:81,1 -DA:83,1 -DA:84,1 DA:85,1 -DA:86,1 +DA:86,2 DA:87,1 DA:91,1 +DA:92,2 DA:93,1 -DA:94,1 -DA:95,1 -DA:96,1 -DA:97,1 -LF:29 +DA:97,3 +DA:105,3 +DA:106,2 +DA:107,3 +DA:108,2 +DA:109,1 +DA:110,1 +DA:118,0 +DA:119,0 +DA:121,0 +DA:127,0 +DA:128,0 +DA:137,0 +DA:138,0 +DA:141,0 +DA:142,0 +DA:145,0 +DA:146,0 +DA:147,0 +DA:151,0 +DA:152,0 +DA:155,0 +DA:161,0 +DA:162,0 +DA:163,0 +DA:164,0 +LF:57 LH:29 end_of_record -SF:lib/src/attributes/scalar_attribute.dart -DA:7,3 -DA:9,1 -DA:10,1 -DA:14,5 -DA:16,1 +SF:lib/src/attributes/gradient/gradient_attribute.dart DA:17,1 -DA:22,3 -DA:24,1 -DA:25,1 -DA:30,1 -DA:32,1 -DA:34,1 -DA:39,3 +DA:22,0 +DA:23,0 +DA:32,0 +DA:36,0 +DA:37,0 DA:41,1 -DA:42,1 -DA:47,3 -DA:49,1 +DA:46,2 +DA:48,1 +DA:51,2 +LF:10 +LH:5 +end_of_record +SF:lib/src/attributes/gradient/gradient_util.dart +DA:48,65 DA:51,1 -DA:56,3 -DA:58,1 -DA:59,1 -DA:64,3 -DA:66,1 -DA:67,1 -DA:71,7 +DA:52,2 +DA:56,2 +DA:57,4 +DA:61,1 +DA:62,2 +DA:68,2 +DA:69,2 +DA:70,2 +DA:71,2 +DA:72,4 DA:73,1 -DA:74,1 -DA:79,1 -DA:81,1 -DA:82,1 -DA:86,1 -DA:88,1 -DA:89,1 -DA:93,1 -DA:95,1 -DA:96,1 -DA:101,2 -DA:103,1 -DA:104,1 -DA:108,1 -DA:110,1 -DA:111,1 -DA:116,1 -DA:118,1 -DA:119,1 +DA:74,2 +DA:76,0 +DA:77,0 +DA:107,65 +DA:123,1 DA:124,3 -DA:126,1 -DA:127,1 -DA:132,7 -DA:134,1 -DA:135,1 -DA:139,2 DA:141,1 -DA:142,1 -DA:147,3 -DA:149,1 +DA:142,3 +DA:159,1 +DA:160,3 +DA:177,0 +DA:178,0 +DA:192,0 +DA:205,0 +DA:218,0 +DA:231,0 +DA:245,1 +DA:246,3 +DA:260,1 +DA:270,1 +DA:277,4 +DA:281,2 +DA:309,66 +DA:327,1 +DA:328,2 +DA:329,3 +DA:349,1 +DA:350,2 +DA:351,3 +DA:371,1 +DA:372,1 +DA:373,4 +DA:392,0 +DA:393,0 +DA:394,0 +DA:413,0 +DA:430,0 +DA:449,2 +DA:450,6 +DA:468,1 +DA:476,1 +DA:481,2 +DA:485,2 +DA:514,65 +DA:530,1 +DA:531,2 +DA:532,3 +DA:550,1 +DA:551,2 +DA:552,3 +DA:570,0 +DA:571,0 +DA:572,0 +DA:587,0 +DA:600,0 +DA:613,0 +DA:626,0 +DA:640,1 +DA:641,3 +DA:655,1 +DA:664,1 +DA:670,2 +DA:674,2 +LF:76 +LH:56 +end_of_record +SF:lib/src/attributes/scalars/scalar_util.dart +DA:11,465 +DA:13,4 +DA:15,14 +DA:16,14 +DA:24,76 +DA:26,10 +DA:31,6 +DA:36,10 +DA:41,135 +DA:43,0 +DA:45,6 +DA:50,66 +DA:52,0 +DA:53,3 +DA:54,0 +DA:55,3 +DA:56,6 +DA:57,3 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:63,0 +DA:64,0 +DA:79,1 +DA:93,1 +DA:109,0 +DA:111,0 +DA:127,65 +DA:129,0 +DA:130,0 DA:150,1 -DA:155,2 +DA:151,3 +DA:152,3 DA:157,1 -DA:158,1 -DA:162,2 -DA:164,1 -DA:165,1 -DA:170,3 -DA:172,1 -DA:173,1 -DA:178,3 -DA:180,1 -DA:182,1 -DA:186,2 -DA:188,1 -DA:189,1 -DA:193,2 +DA:158,0 +DA:159,0 +DA:175,66 +DA:176,3 +DA:177,3 +DA:178,6 +DA:179,6 DA:195,1 -DA:196,1 -DA:200,3 -DA:202,1 -DA:203,1 -DA:207,1 -DA:209,1 -DA:210,1 -DA:214,3 -DA:216,1 -DA:217,1 -DA:222,2 -DA:224,1 -DA:225,1 -DA:229,2 -DA:231,1 -DA:232,1 -DA:236,1 -DA:238,1 -DA:239,1 -LF:93 -LH:93 -end_of_record -SF:lib/src/attributes/shadow_attribute.dart -DA:13,4 -DA:15,2 -DA:19,2 -DA:20,4 -DA:21,2 -DA:22,2 -DA:26,1 +DA:196,3 +DA:197,3 +DA:202,0 +DA:220,0 +DA:222,0 +DA:223,0 +DA:226,0 +DA:227,0 +DA:228,0 +DA:243,1 +DA:244,3 +DA:245,3 +DA:261,2 +DA:262,3 +DA:263,6 +DA:264,3 +DA:280,1 +DA:281,0 +DA:282,3 +DA:298,1 +DA:299,3 +DA:300,0 +DA:301,0 +DA:302,0 +DA:318,0 +DA:320,0 +DA:339,0 +DA:341,0 +DA:342,0 +DA:343,0 +DA:344,0 +DA:345,0 +DA:346,0 +DA:362,1 +DA:363,3 +DA:364,3 +DA:365,3 +DA:367,3 +DA:368,3 +DA:369,3 +DA:385,1 +DA:386,3 +DA:387,3 +DA:388,3 +DA:389,3 +DA:390,3 +DA:406,1 +DA:407,3 +DA:408,3 +DA:420,1 +DA:438,1 +DA:439,3 +DA:440,3 +DA:441,3 +DA:442,3 +DA:455,0 +DA:457,0 +DA:461,1 +DA:480,1 +DA:481,3 +DA:482,3 +DA:483,3 +DA:484,3 +DA:485,3 +DA:486,3 +DA:487,3 +DA:502,1 +DA:504,3 +DA:505,3 +DA:506,3 +DA:507,3 +DA:508,3 +DA:509,3 +DA:510,3 +DA:511,3 +DA:512,3 +DA:513,3 +DA:514,3 +DA:515,3 +DA:516,3 +DA:517,0 +DA:518,0 +DA:519,0 +DA:520,0 +DA:521,0 +DA:522,0 +DA:523,0 +DA:524,0 +DA:525,0 +DA:526,0 +DA:527,0 +DA:528,0 +DA:529,0 +DA:530,0 +DA:531,0 +DA:532,0 +DA:550,2 +DA:551,6 +DA:552,3 +DA:570,2 +DA:571,3 +DA:572,0 +DA:592,1 +DA:594,3 +DA:595,0 +DA:596,0 +DA:597,0 +DA:615,2 +DA:617,3 +DA:618,0 +DA:636,1 +DA:638,3 +DA:640,0 +DA:642,4 +DA:663,1 +DA:665,0 +DA:666,0 +DA:667,0 +DA:668,3 +DA:669,0 +DA:687,2 +DA:689,3 +DA:690,6 +DA:709,1 +DA:710,3 +DA:711,3 +DA:712,3 +DA:730,1 +DA:731,3 +DA:732,3 +DA:754,1 +DA:755,3 +DA:756,3 +DA:757,3 +DA:758,3 +DA:759,3 +DA:760,3 +LF:180 +LH:117 +end_of_record +SF:lib/src/attributes/scalars/scalars_attribute.dart +DA:12,16 +DA:14,7 +DA:17,2 +DA:18,4 +DA:23,2 +DA:25,1 +DA:26,0 +DA:32,3 +DA:34,1 +DA:35,0 +DA:37,1 +DA:39,2 +DA:47,5 +DA:49,1 +DA:50,0 +DA:52,1 +DA:54,2 +DA:61,3 +DA:63,1 +DA:64,0 +DA:70,2 +DA:72,1 +DA:73,0 +DA:75,0 +DA:79,0 +DA:82,1 +DA:84,2 +LF:27 +LH:20 +end_of_record +SF:lib/src/attributes/shadow/shadow_dto.dart +DA:14,7 +DA:25,3 +DA:27,1 DA:28,1 DA:29,1 -DA:30,3 +DA:30,2 DA:31,1 DA:35,0 DA:36,0 -DA:42,3 -DA:49,2 -DA:53,2 -DA:55,2 -DA:56,2 -DA:57,2 -DA:58,2 -DA:59,2 -DA:63,1 -DA:65,1 -DA:66,3 -DA:67,1 -DA:68,1 -DA:69,1 -DA:73,0 -DA:74,0 -LF:29 -LH:25 -end_of_record -SF:lib/src/attributes/space_attribute.dart -DA:20,6 -DA:38,1 -DA:42,1 +DA:39,2 DA:43,2 -DA:44,2 +DA:44,4 DA:45,2 DA:46,2 -DA:47,2 -DA:48,2 -DA:52,3 -DA:54,3 -DA:55,3 -DA:57,3 -DA:58,3 -DA:59,3 -DA:61,3 -DA:62,6 -DA:63,6 -DA:64,6 -DA:65,6 -DA:67,1 -DA:68,1 -DA:69,1 -DA:71,1 -DA:72,2 -DA:73,2 -DA:74,2 -DA:75,2 -DA:78,0 -DA:83,2 -DA:84,14 -DA:89,0 -DA:95,0 -DA:106,6 -DA:119,6 -DA:121,1 -DA:130,1 -DA:131,0 -DA:132,0 -DA:133,1 -DA:134,1 -DA:143,4 -DA:150,1 -DA:159,1 -DA:160,0 -DA:161,0 -DA:162,1 -DA:163,1 -DA:171,6 -DA:184,6 -DA:186,1 -DA:195,1 -DA:196,0 -DA:197,0 -DA:198,1 -DA:199,1 -DA:208,4 -DA:215,1 -DA:224,1 -DA:225,0 -DA:226,0 -DA:227,1 -DA:228,1 -LF:63 -LH:52 +DA:50,1 +DA:54,1 +DA:55,1 +DA:56,1 +DA:57,1 +DA:61,0 +DA:62,0 +DA:68,6 +DA:75,4 +DA:76,4 +DA:77,8 +DA:78,4 +DA:79,4 +DA:80,4 +DA:84,0 +DA:85,0 +DA:88,4 +DA:92,4 +DA:93,8 +DA:94,4 +DA:95,4 +DA:96,4 +DA:100,2 +DA:104,2 +DA:105,2 +DA:106,2 +DA:107,2 +DA:108,2 +DA:112,4 +DA:113,20 +LF:44 +LH:38 end_of_record -SF:lib/src/attributes/strut_style_attribute.dart -DA:17,4 -DA:28,1 +SF:lib/src/attributes/shadow/shadow_util.dart DA:32,1 -DA:33,2 -DA:34,2 -DA:35,2 +DA:35,1 DA:36,1 -DA:37,2 -DA:38,1 -DA:39,1 -DA:40,2 -DA:44,2 -DA:48,2 -DA:49,3 -DA:50,4 -DA:51,2 -DA:52,3 -DA:53,3 -DA:54,3 -DA:55,3 -DA:56,4 -DA:60,1 -DA:61,1 -DA:62,1 -DA:63,1 -DA:64,1 -DA:65,1 -DA:66,1 -DA:67,1 -DA:68,1 -DA:69,1 -LF:31 -LH:31 -end_of_record -SF:lib/src/attributes/style_mix_attribute.dart -DA:8,1 -DA:10,1 -DA:14,4 -DA:17,1 -DA:18,2 -LF:5 +DA:42,2 +DA:59,0 +DA:60,0 +DA:77,0 +DA:78,0 +DA:94,0 +DA:97,1 +DA:98,1 +DA:99,1 +DA:111,1 +DA:125,1 +DA:126,4 +DA:136,0 +DA:148,0 +DA:149,0 +DA:162,0 +DA:163,0 +DA:176,0 +DA:188,0 +DA:191,0 +DA:197,0 +DA:204,0 +DA:214,1 +DA:226,1 +DA:227,2 +DA:229,4 +DA:233,0 +DA:234,0 +DA:235,0 +DA:236,0 +DA:237,0 +DA:238,0 +DA:239,0 +DA:240,0 +DA:241,0 +DA:242,0 +DA:243,0 +LF:40 +LH:14 +end_of_record +SF:lib/src/attributes/spacing/edge_insets_dto.dart +DA:18,8 +DA:27,12 +DA:32,3 +DA:33,21 +LF:4 +LH:4 +end_of_record +SF:lib/src/attributes/spacing/spacing_attribute.dart +DA:13,6 +DA:15,0 +DA:17,3 +DA:18,3 +DA:19,3 +DA:20,3 +DA:21,3 +DA:22,3 +DA:24,0 +DA:26,0 +DA:32,6 +DA:34,1 +DA:36,4 +DA:42,6 +DA:44,1 +DA:46,4 +LF:16 +LH:13 +end_of_record +SF:lib/src/attributes/spacing/spacing_util.dart +DA:33,64 +DA:35,1 +DA:36,2 +DA:55,1 +DA:56,1 +DA:57,2 +DA:78,1 +DA:79,1 +DA:80,2 +DA:101,1 +DA:102,1 +DA:103,1 +DA:104,1 +DA:126,1 +DA:127,3 +DA:148,1 +DA:149,3 +DA:164,1 +DA:165,3 +DA:180,1 +DA:181,3 +DA:202,0 +DA:203,0 +DA:224,0 +DA:225,0 +DA:246,2 +DA:254,4 +DA:255,2 +DA:283,2 +DA:297,2 +DA:335,1 +DA:336,1 +DA:352,0 +DA:353,0 +DA:354,0 +DA:373,1 +DA:374,3 +DA:392,1 +DA:393,3 +DA:411,0 +DA:412,0 +DA:430,0 +DA:431,0 +DA:449,0 +DA:450,0 +DA:463,0 +DA:464,0 +DA:465,0 +DA:489,1 +DA:504,1 +DA:525,1 +DA:526,2 +DA:527,1 +DA:570,2 +DA:582,4 +DA:594,4 +DA:606,4 +DA:618,4 +DA:630,4 +DA:642,4 +DA:657,3 +LF:61 +LH:45 +end_of_record +SF:lib/src/attributes/strut_style/strut_style_attribute.dart +DA:9,4 +DA:11,1 +DA:13,4 +LF:3 +LH:3 +end_of_record +SF:lib/src/attributes/style_mix_attribute.dart +DA:12,1 +DA:16,1 +DA:20,2 +DA:23,1 +DA:24,2 +DA:29,1 +DA:31,1 +DA:33,3 +LF:8 +LH:8 +end_of_record +SF:lib/src/attributes/text_directives_util.dart +DA:7,3 +DA:8,3 +DA:9,3 +DA:10,3 +DA:11,3 +DA:13,1 +DA:14,4 +DA:18,0 +DA:23,0 +DA:25,2 +DA:26,4 +DA:27,2 +DA:28,2 +DA:29,2 +LF:14 +LH:12 +end_of_record +SF:lib/src/attributes/text_style/text_style_attribute.dart +DA:11,4 +DA:13,0 +DA:15,0 +LF:3 +LH:1 +end_of_record +SF:lib/src/attributes/text_style/text_style_util.dart +DA:43,4 +DA:45,1 +DA:67,1 +DA:68,1 +DA:92,2 +DA:101,0 +DA:102,0 +DA:116,1 +DA:117,3 +DA:131,1 +DA:132,3 +DA:143,1 +DA:144,1 +DA:145,2 +DA:159,0 +DA:160,0 +DA:174,0 +DA:175,0 +DA:189,0 +DA:190,0 +DA:212,1 +DA:213,4 +DA:228,1 +DA:229,1 +DA:230,2 +DA:246,1 +DA:247,3 +DA:261,0 +DA:262,0 +DA:273,0 +DA:283,0 +DA:284,0 +DA:298,0 +DA:299,0 +DA:312,0 +DA:313,0 +DA:327,0 +DA:340,0 +DA:353,2 +DA:363,2 +DA:376,1 +DA:377,1 +DA:390,0 +DA:400,0 +DA:410,0 +DA:411,0 +DA:421,0 +DA:422,0 +DA:435,0 +DA:455,2 +DA:478,2 +DA:479,2 +DA:480,1 +DA:488,4 +DA:491,1 +DA:502,4 +LF:56 +LH:30 +end_of_record +SF:lib/src/attributes/variant_attribute.dart +DA:15,4 +DA:17,6 +DA:19,6 +DA:21,0 +DA:23,0 +DA:25,0 +DA:28,1 +DA:29,3 +DA:39,3 +DA:41,3 +DA:42,9 +DA:44,0 +DA:46,0 +DA:48,0 +DA:52,0 +DA:53,0 +DA:54,0 +DA:63,1 +DA:66,1 +DA:67,2 +DA:68,1 +DA:69,0 +DA:70,1 +DA:71,0 +DA:74,2 +DA:77,1 +DA:78,2 +DA:80,0 +DA:81,0 +DA:83,0 +DA:85,0 +DA:87,0 +LF:32 +LH:16 +end_of_record +SF:lib/src/core/attribute.dart +DA:8,757 +DA:12,752 +DA:19,44 +DA:75,719 +DA:80,8 +DA:87,20 +DA:89,5 +DA:90,10 +DA:92,3 +DA:95,6 +DA:96,12 +DA:112,15 +DA:114,0 +DA:115,0 +DA:117,0 +DA:120,0 +DA:121,0 +DA:124,1 +DA:129,4 +DA:132,2 +DA:133,2 +LF:21 +LH:16 +end_of_record +SF:lib/src/core/attributes_map.dart +DA:14,40 +DA:16,64 +DA:18,40 +DA:19,80 +DA:22,40 +DA:25,40 +DA:26,54 +DA:27,14 +DA:30,14 +DA:31,7 +DA:36,9 +DA:38,9 +DA:41,2 +DA:48,6 +DA:50,3 +DA:52,0 +DA:54,129 +DA:56,1 +DA:57,3 +DA:59,0 +DA:61,10 +DA:62,30 +DA:64,3 +DA:65,12 +DA:68,4 +DA:69,9 +DA:75,40 +DA:77,64 +DA:79,40 +DA:80,80 +DA:83,40 +DA:86,40 +DA:87,42 +DA:88,2 +DA:91,2 +DA:93,2 +DA:96,0 +DA:103,3 +DA:105,3 +DA:107,0 +DA:109,13 +DA:111,36 +DA:112,84 +DA:114,0 +DA:115,0 +DA:117,3 +DA:118,12 +DA:121,3 +DA:122,6 +LF:49 +LH:43 +end_of_record +SF:lib/src/core/directive.dart +DA:11,2 +DA:16,6 +DA:18,1 +DA:19,2 +DA:33,2 +DA:37,0 +LF:6 LH:5 end_of_record -SF:lib/src/attributes/text_style_attribute.dart -DA:37,5 +SF:lib/src/core/extensions/iterable_ext.dart +DA:6,3 +DA:8,1 +DA:9,2 +DA:10,1 +DA:18,3 +DA:19,3 +DA:20,3 +DA:27,1 +DA:30,1 +DA:32,1 +DA:33,1 +DA:34,1 +DA:36,2 +DA:37,2 +DA:38,1 +DA:39,1 +DA:40,1 +DA:41,1 +DA:45,0 +DA:46,0 +DA:49,0 +LF:21 +LH:18 +end_of_record +SF:lib/src/core/extensions/values_ext.dart +DA:23,2 +DA:25,3 +DA:29,0 +DA:32,1 +DA:33,2 +DA:34,0 +DA:35,0 +DA:37,0 +DA:42,2 +DA:46,0 +DA:50,0 +DA:54,10 +DA:56,1 +DA:57,2 DA:62,0 +DA:66,4 +DA:70,24 +DA:75,1 +DA:76,1 +DA:81,2 +DA:83,3 +DA:88,0 +DA:92,1 +DA:93,2 +DA:94,2 +DA:96,0 +DA:99,3 +DA:103,2 +DA:107,2 +DA:111,1 +DA:112,2 +DA:115,3 +DA:116,3 +DA:121,2 +DA:124,0 +DA:125,0 +DA:127,0 +DA:132,0 +DA:136,2 +DA:140,2 +DA:144,0 +DA:145,0 +DA:150,6 +DA:154,1 +DA:155,4 +DA:160,2 +DA:162,3 +LF:47 +LH:32 +end_of_record +SF:lib/src/decorators/clip_decorator.dart +DA:10,3 +DA:12,1 +DA:14,1 +DA:15,2 +DA:16,1 +DA:20,0 +DA:21,0 +DA:23,2 +DA:25,2 +DA:26,2 +DA:27,2 +DA:28,2 +DA:38,3 +DA:40,1 +DA:42,1 +DA:43,1 +DA:44,2 +DA:48,0 +DA:49,0 +DA:51,2 +DA:53,2 +DA:54,2 +DA:55,2 +DA:56,2 +DA:66,2 +DA:68,1 +DA:70,1 +DA:71,2 +DA:72,1 +DA:76,0 +DA:77,0 +DA:79,1 +DA:81,1 +DA:82,1 +DA:83,1 +DA:84,1 +DA:95,3 +DA:102,1 +DA:104,1 +DA:105,2 +DA:106,2 +DA:107,1 +DA:111,0 +DA:112,0 +DA:114,1 +DA:116,1 +DA:117,1 +DA:118,1 +DA:119,1 +DA:120,1 +DA:127,0 +DA:133,0 +DA:135,0 +DA:140,0 +DA:148,0 +DA:150,0 +DA:151,0 +DA:152,0 +DA:153,0 +DA:155,0 +DA:161,64 +DA:162,0 +DA:164,0 +DA:165,0 +DA:166,0 +DA:167,0 +DA:168,0 +DA:173,0 +LF:68 +LH:43 +end_of_record +SF:lib/src/decorators/decorator.dart +DA:8,6 +DA:13,5 +DA:15,0 +LF:3 +LH:2 +end_of_record +SF:lib/src/decorators/default_decorators.dart +DA:9,2 +DA:11,1 +DA:14,0 +DA:15,0 +DA:17,1 +DA:19,3 +DA:24,0 +DA:26,0 +DA:29,0 +DA:30,0 +DA:32,0 +DA:34,0 +DA:40,3 +DA:42,1 +DA:44,1 +DA:45,1 +DA:46,2 +DA:50,0 +DA:51,0 +DA:53,2 +DA:55,2 +DA:56,2 +DA:57,2 +DA:58,2 +DA:66,2 +DA:68,1 +DA:71,0 +DA:72,0 +DA:74,1 +DA:75,3 +DA:80,2 +DA:82,1 +DA:85,0 +DA:86,0 +DA:88,1 +DA:90,3 +DA:95,2 +DA:97,1 +DA:100,0 +DA:101,0 +DA:103,1 +DA:105,3 +LF:42 +LH:26 +end_of_record +SF:lib/src/decorators/widget_decorator_wrapper.dart +DA:6,0 +DA:12,0 +DA:14,0 +LF:3 +LH:0 +end_of_record +SF:lib/src/deprecations.dart +DA:13,0 +DA:15,0 +DA:18,0 +DA:20,0 +DA:23,0 +DA:27,0 +DA:30,0 +DA:32,0 +DA:35,0 +DA:39,0 +DA:41,0 +DA:44,0 +DA:46,0 +DA:49,0 +DA:53,0 +DA:56,0 +DA:60,0 +DA:63,0 +DA:65,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:84,0 +DA:85,0 +DA:86,0 +DA:89,0 +DA:91,0 +DA:95,0 +DA:98,0 +DA:101,0 +DA:104,0 +DA:107,0 +DA:110,0 +DA:113,0 +DA:116,0 +DA:119,0 +DA:122,0 +DA:125,0 +DA:128,0 +DA:131,0 +DA:137,0 +DA:139,0 +DA:143,0 +DA:204,0 +DA:205,0 +DA:208,0 +DA:209,0 +DA:212,0 +DA:213,0 +DA:216,0 +DA:217,0 +DA:220,0 +DA:221,0 +DA:224,0 +DA:225,0 +DA:228,0 +DA:229,0 +DA:232,0 +DA:233,0 +DA:236,0 +DA:237,0 +DA:240,0 +DA:241,0 +DA:244,0 +DA:245,0 +DA:248,0 +DA:249,0 +DA:252,0 +DA:253,0 +DA:256,0 +DA:257,0 +DA:260,0 +DA:261,0 +DA:264,0 +DA:265,0 +DA:268,0 +DA:269,0 +DA:272,0 +DA:273,0 +DA:276,0 +DA:277,0 +DA:280,0 +DA:281,0 +DA:288,0 +DA:289,0 +DA:296,0 +DA:299,0 +DA:302,0 +DA:305,0 +DA:308,0 +DA:311,0 +DA:314,0 +DA:317,0 +DA:320,0 +DA:325,0 +DA:327,0 +DA:329,0 +DA:331,0 +DA:333,0 +DA:335,0 +DA:337,0 +DA:339,0 +DA:341,0 +DA:344,0 +DA:347,0 +DA:355,0 +DA:356,0 +DA:358,0 +DA:359,0 +DA:361,0 +DA:362,0 +DA:365,0 +DA:367,0 +DA:369,0 +DA:372,0 +DA:374,0 +DA:377,0 +DA:379,0 +DA:382,0 +DA:384,0 +DA:387,0 +DA:389,0 +DA:392,0 +DA:394,0 +DA:397,0 +DA:399,0 +DA:409,0 +DA:412,0 +DA:415,0 +DA:418,0 +DA:421,0 +DA:424,0 +DA:427,0 +DA:430,0 +DA:433,0 +DA:436,0 +DA:441,0 +DA:443,0 +DA:446,0 +DA:448,0 +DA:451,0 +DA:453,0 +DA:457,0 +DA:459,0 +DA:463,0 +DA:465,0 +DA:468,0 +DA:470,0 +DA:473,0 +DA:475,0 +DA:478,0 +DA:480,0 +DA:483,0 +DA:485,0 +DA:489,0 +DA:492,0 +DA:495,0 +DA:498,0 +DA:501,0 +DA:504,0 +DA:507,0 +DA:510,0 +DA:513,0 +DA:516,0 +DA:519,0 +DA:522,0 +DA:525,0 +DA:528,0 +DA:531,0 +DA:534,0 +DA:537,0 +DA:540,0 +DA:543,0 +DA:546,0 +DA:549,0 +DA:552,0 +DA:555,0 +DA:558,0 +DA:561,0 +DA:564,0 +DA:567,0 +DA:570,0 +DA:573,0 +DA:576,0 +DA:579,0 +DA:582,0 +DA:585,0 +DA:591,3 +DA:594,0 +DA:600,0 +DA:604,0 +DA:607,0 +DA:611,0 +DA:614,0 +DA:616,0 +DA:619,0 +DA:621,0 +DA:622,0 +LF:204 +LH:1 +end_of_record +SF:lib/src/factory/mix_provider.dart +DA:11,3 +DA:14,4 +DA:15,4 +DA:19,0 +DA:20,0 +DA:22,0 +DA:31,0 +DA:32,0 +DA:46,3 +DA:51,3 +DA:56,6 +LF:11 +LH:6 +end_of_record +SF:lib/src/factory/mix_provider_data.dart +DA:24,36 +DA:30,36 +DA:31,36 +DA:33,72 +DA:35,36 +DA:37,36 +DA:44,16 +DA:49,1 +DA:50,1 +DA:55,1 +DA:56,2 +DA:59,10 +DA:60,20 +DA:61,10 +DA:63,10 +DA:69,0 +DA:70,0 +DA:73,0 +DA:76,0 +DA:77,0 +DA:79,0 +DA:80,0 +DA:81,0 +DA:90,1 +DA:91,1 +DA:92,1 +DA:93,3 +DA:98,0 +DA:99,0 +DA:102,36 +DA:107,108 +DA:109,72 +DA:110,72 +DA:113,36 +DA:114,108 +DA:117,2 +DA:118,1 +DA:121,1 +DA:122,0 +DA:125,1 +DA:128,1 +DA:133,3 +DA:136,10 +DA:137,10 +DA:139,10 +DA:140,0 +LF:46 +LH:34 +end_of_record +SF:lib/src/factory/style_group.dart +DA:4,0 +LF:1 +LH:0 +end_of_record +SF:lib/src/factory/style_mix.dart +DA:47,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:52,0 +DA:54,104 +DA:70,13 +DA:92,13 +DA:95,13 +DA:97,13 +DA:109,40 +DA:110,40 +DA:111,40 +DA:113,54 +DA:114,14 +DA:115,14 +DA:116,3 +DA:117,2 +DA:118,2 +DA:119,4 +DA:120,4 +DA:122,2 +DA:126,40 +DA:127,40 +DA:128,40 +DA:140,1 +DA:157,1 +DA:158,1 +DA:160,3 +DA:182,1 +DA:188,12 +DA:191,5 +DA:194,2 +DA:199,3 +DA:226,1 +DA:227,2 +DA:233,3 +DA:237,3 +DA:238,0 +DA:239,0 +DA:249,4 +DA:252,9 +DA:253,9 +DA:255,3 +DA:284,1 +DA:286,1 +DA:291,1 +DA:292,1 +DA:296,1 +DA:301,3 +DA:302,1 +DA:303,1 +DA:305,1 +DA:310,2 +DA:313,2 +DA:314,1 +DA:316,1 +DA:321,1 +DA:322,1 +DA:323,1 +DA:327,1 +DA:333,5 +DA:336,1 +DA:339,1 +DA:365,1 +DA:369,1 +DA:373,3 +DA:374,2 +DA:375,1 +DA:378,2 +DA:383,4 +DA:385,1 +DA:411,1 +DA:412,1 +DA:414,2 +DA:415,1 +DA:416,2 +DA:420,1 +DA:423,4 +DA:424,12 +DA:437,1 +DA:440,0 +DA:442,0 +DA:443,0 +DA:445,0 +LF:86 +LH:74 +end_of_record +SF:lib/src/factory/style_mix_ext.dart +DA:12,1 +DA:18,1 +DA:19,1 +DA:26,1 +DA:33,1 +DA:41,1 +DA:48,1 +DA:49,1 +DA:56,1 +DA:63,1 DA:64,1 -DA:68,1 DA:71,1 -DA:75,1 -DA:76,2 -DA:77,2 -DA:78,1 -DA:79,2 -DA:80,1 -DA:81,1 +DA:79,1 DA:82,1 -DA:83,2 -DA:84,2 -DA:85,1 -DA:86,1 -DA:87,1 -DA:89,2 -DA:90,1 -DA:91,1 -DA:92,1 -DA:93,2 -DA:94,1 +DA:88,1 DA:95,1 DA:96,1 -DA:97,3 -DA:98,2 -DA:99,1 -DA:100,1 -DA:104,3 -DA:107,3 -DA:110,3 -DA:111,6 -DA:112,3 -DA:113,3 -DA:114,3 -DA:115,3 -DA:116,3 -DA:117,3 -DA:118,3 -DA:119,3 -DA:120,3 -DA:121,3 -DA:122,3 -DA:123,3 -DA:124,3 -DA:125,3 -DA:126,4 -DA:127,3 -DA:128,3 -DA:129,3 -DA:130,3 -DA:131,3 -DA:135,1 -DA:136,1 -DA:137,1 -DA:138,1 -DA:139,1 -DA:140,1 -DA:141,1 -DA:142,1 -DA:143,1 -DA:144,1 -DA:145,1 -DA:146,1 -DA:147,1 -DA:148,1 -DA:149,1 -DA:150,1 -DA:151,1 -DA:152,1 -DA:153,1 -DA:154,1 -DA:155,1 -DA:156,1 -DA:157,1 -DA:158,1 -LF:78 -LH:77 +DA:103,1 +DA:110,1 +DA:111,1 +DA:118,1 +DA:125,1 +DA:127,1 +LF:23 +LH:23 end_of_record -SF:lib/src/attributes/variant_attribute.dart -DA:14,4 -DA:16,6 -DA:18,0 -DA:20,0 -DA:21,0 -DA:24,0 -DA:27,1 -DA:28,3 -DA:33,3 -DA:35,12 -DA:37,0 -DA:39,0 -DA:40,0 +SF:lib/src/recipes/container/container_attribute.dart +DA:28,68 +DA:41,4 +DA:42,4 +DA:44,4 +DA:45,6 +DA:46,6 +DA:47,6 +DA:48,5 +DA:49,5 +DA:50,5 +DA:51,6 +DA:52,5 +DA:53,4 +DA:54,4 +DA:55,4 +DA:58,4 +DA:60,4 +DA:61,4 +DA:62,6 +DA:63,6 +DA:64,5 +DA:65,7 +DA:66,4 +DA:67,4 +DA:68,5 +DA:69,4 +DA:70,4 +DA:74,4 +DA:78,2 +DA:79,4 +DA:80,6 +DA:81,6 +DA:82,4 +DA:83,4 +DA:84,4 +DA:85,4 +DA:86,4 +DA:87,4 +DA:88,4 +DA:92,0 +DA:93,0 +DA:94,0 +DA:95,0 +DA:96,0 +DA:97,0 +DA:98,0 +DA:99,0 +DA:100,0 +DA:101,0 +DA:102,0 +DA:103,0 +LF:51 +LH:39 +end_of_record +SF:lib/src/recipes/container/container_spec.dart +DA:18,4 +DA:31,0 DA:43,0 DA:47,0 DA:48,0 DA:49,0 -DA:57,1 -DA:59,0 -DA:62,1 -DA:63,2 -DA:64,1 -DA:65,0 -DA:66,1 +DA:50,0 +DA:51,0 +DA:52,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:56,0 +DA:57,0 +DA:61,1 +DA:74,1 +DA:75,1 +DA:76,1 +DA:77,1 +DA:78,1 +DA:79,1 +DA:80,1 +DA:81,1 +DA:82,1 +DA:83,0 +DA:84,0 +DA:88,1 +DA:90,1 +DA:91,3 +DA:92,3 +DA:93,3 +DA:94,3 +DA:95,3 +DA:96,4 +DA:97,2 +DA:98,3 +DA:99,3 +DA:100,3 +DA:104,0 +DA:105,0 +DA:106,0 +DA:107,0 +DA:108,0 +DA:109,0 +DA:110,0 +DA:111,0 +DA:112,0 +DA:113,0 +DA:114,0 +DA:115,0 +LF:50 +LH:23 +end_of_record +SF:lib/src/recipes/container/container_util.dart +DA:159,0 +DA:160,0 +DA:173,3 +DA:186,3 +DA:199,3 +DA:212,3 +DA:232,64 +DA:234,2 +DA:246,4 +DA:247,2 +DA:262,2 +DA:263,6 +DA:266,0 +DA:267,0 +DA:270,0 +DA:271,0 +DA:274,0 +DA:275,0 +DA:278,0 +DA:279,0 +DA:282,0 +DA:283,0 +DA:284,0 +DA:288,0 +DA:289,0 +DA:292,0 +DA:293,0 +DA:296,0 +DA:298,0 +DA:300,0 +DA:312,0 +DA:314,0 +DA:315,0 +DA:316,0 +DA:317,0 +DA:320,0 +DA:325,0 +LF:37 +LH:10 +end_of_record +SF:lib/src/recipes/container/container_widget.dart +DA:11,2 +DA:15,2 +DA:17,2 +DA:18,2 +DA:19,0 +DA:22,4 +DA:23,2 +DA:24,2 +DA:26,2 +DA:28,4 +DA:34,3 +DA:39,3 +DA:41,3 +DA:42,6 +DA:43,6 +DA:44,6 +DA:45,6 +DA:46,6 +DA:47,6 +DA:48,6 +DA:49,6 +DA:50,6 +DA:51,6 +DA:52,3 +LF:24 +LH:23 +end_of_record +SF:lib/src/recipes/flex/flex_attribute.dart +DA:18,194 +DA:30,1 +DA:31,1 +DA:34,2 +DA:36,2 +DA:37,2 +DA:38,2 +DA:39,2 +DA:40,2 +DA:41,2 +DA:42,2 +DA:43,2 +DA:44,2 +DA:45,2 +DA:49,1 +DA:53,1 +DA:54,2 +DA:55,2 +DA:56,2 +DA:57,2 +DA:58,2 +DA:59,2 +DA:60,2 +DA:61,2 +DA:62,2 +DA:66,0 +DA:67,0 +DA:68,0 +DA:69,0 +DA:70,0 +DA:71,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:75,0 +DA:76,0 +LF:36 +LH:25 +end_of_record +SF:lib/src/recipes/flex/flex_spec.dart +DA:20,3 +DA:32,0 +DA:43,1 +DA:44,2 +DA:46,0 +DA:49,1 +DA:51,1 +DA:53,3 +DA:55,3 +DA:56,3 +DA:58,3 +DA:59,3 +DA:60,3 +DA:61,3 +DA:62,3 +DA:63,3 DA:67,0 -DA:70,2 -DA:73,1 -DA:74,2 +DA:69,0 +DA:70,0 +DA:71,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:75,0 DA:76,0 +DA:77,0 DA:78,0 -DA:79,0 -DA:82,0 -LF:32 -LH:14 -end_of_record -SF:lib/src/decorators/default_decorators.dart -DA:8,3 -DA:10,1 -DA:12,2 -DA:15,1 -DA:16,1 -DA:18,0 -DA:19,0 -DA:21,1 -DA:23,1 -DA:29,2 -DA:31,1 -DA:32,1 -DA:34,1 -DA:36,1 -DA:37,1 -DA:38,2 -DA:42,1 -DA:44,3 -DA:47,0 -DA:48,0 -DA:50,1 -DA:52,1 -DA:53,1 -DA:54,1 -DA:63,1 -DA:68,3 -DA:70,1 -DA:72,2 -DA:75,1 -DA:76,1 -DA:78,0 -DA:79,0 -DA:81,1 DA:82,1 -DA:87,3 -DA:89,1 -DA:91,2 DA:94,1 -DA:95,1 +DA:95,0 +DA:96,0 DA:97,0 DA:98,0 -DA:100,1 -DA:101,1 -DA:106,3 +DA:99,0 +DA:100,0 +DA:101,0 +DA:102,0 +DA:103,0 +DA:107,1 DA:108,1 -DA:110,2 +DA:109,1 +DA:110,1 +DA:111,1 +DA:112,1 DA:113,1 DA:114,1 -DA:116,0 -DA:117,0 -DA:119,1 -DA:120,1 -LF:52 -LH:42 +DA:115,1 +DA:116,1 +DA:117,1 +LF:49 +LH:27 end_of_record -SF:lib/src/decorators/widget_decorator_wrapper.dart +SF:lib/src/recipes/flex/flex_util.dart DA:6,0 +DA:10,0 DA:12,0 DA:14,0 -LF:3 -LH:0 -end_of_record -SF:lib/src/deprecations.dart -DA:33,0 -DA:35,0 +DA:15,0 +DA:18,0 +DA:19,0 +DA:20,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:30,0 +DA:31,0 +DA:32,0 +DA:36,0 +DA:37,0 DA:38,0 -DA:40,0 +DA:42,0 DA:43,0 -DA:47,0 +DA:44,0 +DA:48,0 +DA:49,0 DA:50,0 -DA:52,0 +DA:54,0 DA:55,0 +DA:58,0 DA:59,0 -DA:61,0 -DA:64,0 -DA:66,0 -DA:69,0 -DA:73,0 +DA:62,0 +DA:63,0 +DA:65,0 DA:76,0 -DA:80,0 -DA:83,0 -DA:85,0 -DA:94,0 -DA:95,0 -DA:96,0 -DA:97,0 -DA:98,0 -DA:99,0 -DA:100,0 -DA:101,0 -DA:102,0 -DA:103,0 -DA:104,0 -DA:105,0 -DA:112,0 -DA:115,0 -DA:118,0 -DA:121,0 -DA:124,0 -DA:127,0 -DA:130,0 -DA:133,0 -DA:136,0 -DA:139,0 -DA:142,0 -DA:145,0 -DA:148,0 -DA:156,0 -DA:158,0 -DA:222,0 -DA:223,0 -DA:226,0 -DA:227,0 -DA:230,0 -DA:231,0 -DA:234,0 -DA:235,0 -DA:238,0 -DA:239,0 -DA:242,0 -DA:243,0 -DA:246,0 -DA:247,0 -DA:250,0 -DA:251,0 -DA:254,0 -DA:255,0 -DA:258,0 -DA:259,0 -DA:262,0 -DA:263,0 -DA:266,0 -DA:267,0 -DA:270,0 -DA:271,0 -DA:274,0 -DA:275,0 -DA:278,0 -DA:279,0 -DA:282,0 -DA:283,0 -DA:286,0 -DA:287,0 -DA:290,0 -DA:291,0 -DA:294,0 -DA:295,0 -DA:298,0 -DA:299,0 -DA:306,0 -DA:307,0 -DA:311,0 -DA:314,0 -DA:317,0 -DA:320,0 -DA:323,0 -DA:326,0 -DA:329,0 -DA:332,0 -DA:335,0 -DA:341,0 -DA:343,0 -DA:345,0 -DA:347,0 -DA:349,0 -DA:351,0 -DA:353,0 -DA:355,0 -DA:357,0 -DA:362,0 -DA:365,0 -DA:376,0 -DA:377,0 -DA:382,0 -DA:384,0 -DA:387,0 -DA:389,0 -DA:392,0 -DA:394,0 -DA:397,0 -DA:399,0 -DA:402,0 -DA:404,0 -DA:407,0 -DA:409,0 -DA:412,0 -DA:414,0 -DA:459,0 -DA:461,0 -DA:464,0 -DA:466,0 -DA:469,0 -DA:471,0 -DA:475,0 -DA:477,0 -DA:481,0 -DA:483,0 -DA:486,0 -DA:488,0 -DA:491,0 -DA:493,0 -DA:496,0 -DA:498,0 -DA:501,0 -DA:503,0 -LF:142 +DA:88,0 +LF:32 LH:0 end_of_record -SF:lib/src/directives/directive_attribute.dart -DA:4,3 -LF:1 -LH:1 +SF:lib/src/recipes/flex/flex_widget.dart +DA:30,1 +DA:41,1 +DA:43,1 +DA:44,1 +DA:45,0 +DA:48,2 +DA:49,1 +DA:50,1 +DA:52,1 +DA:54,1 +DA:55,1 +DA:58,1 +DA:59,0 +DA:60,0 +DA:61,0 +DA:65,1 +DA:67,1 +DA:68,1 +DA:75,1 +DA:86,1 +DA:88,1 +DA:89,1 +DA:91,3 +DA:92,3 +DA:94,3 +DA:96,3 +DA:97,1 +DA:119,1 +DA:124,1 +DA:144,1 +DA:149,1 +DA:171,1 +DA:182,1 +DA:184,2 +DA:185,2 +DA:186,2 +DA:188,1 +DA:190,1 +DA:192,1 +DA:193,1 +DA:216,1 +DA:221,1 +DA:241,1 +DA:246,1 +LF:44 +LH:40 +end_of_record +SF:lib/src/recipes/icon/icon_attribute.dart +DA:10,195 +DA:12,1 +DA:13,1 +DA:16,3 +DA:18,12 +DA:21,1 +DA:25,1 +DA:26,2 +DA:27,2 +DA:31,0 +DA:32,0 +LF:11 +LH:9 +end_of_record +SF:lib/src/recipes/icon/icon_spec.dart +DA:11,5 +DA:13,0 +DA:17,2 +DA:18,4 +DA:20,0 +DA:23,2 +DA:25,2 +DA:26,6 +DA:27,6 +DA:31,0 +DA:33,0 +DA:36,2 +DA:42,2 +DA:45,0 +DA:46,0 +LF:15 +LH:9 end_of_record -SF:lib/src/directives/directives/controllers.dart -DA:19,0 -DA:41,0 +SF:lib/src/recipes/icon/icon_util.dart +DA:14,64 +DA:16,0 +DA:17,0 +DA:20,0 +DA:21,0 +DA:24,1 +DA:25,2 +LF:7 +LH:3 +end_of_record +SF:lib/src/recipes/icon/icon_widget.dart +DA:8,1 +DA:21,1 +DA:23,1 +DA:24,1 +DA:25,0 +DA:28,2 +DA:29,1 +DA:30,1 +DA:32,1 +DA:34,1 +DA:35,1 +DA:36,1 +DA:37,1 +DA:38,1 +DA:39,1 DA:46,0 -DA:47,0 +DA:61,0 +DA:63,0 +DA:65,0 +DA:68,0 +DA:69,0 +DA:70,0 +DA:72,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:80,0 +LF:30 +LH:14 +end_of_record +SF:lib/src/recipes/image/image_attribute.dart +DA:16,64 +DA:24,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:31,0 +DA:35,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 DA:48,0 DA:49,0 +LF:17 +LH:1 +end_of_record +SF:lib/src/recipes/image/image_spec.dart +DA:17,0 +DA:25,0 +DA:32,0 +DA:33,0 +DA:35,0 +DA:38,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:49,0 +DA:51,0 DA:52,0 DA:53,0 DA:54,0 DA:55,0 DA:56,0 -DA:57,0 -DA:58,0 DA:60,0 -DA:64,0 -DA:65,0 DA:69,0 DA:70,0 DA:71,0 DA:72,0 -DA:76,0 -DA:80,0 -DA:81,0 -DA:82,0 -DA:83,0 -DA:84,0 -DA:85,0 -DA:86,0 -DA:88,0 -DA:92,0 -DA:93,0 -DA:96,0 -DA:97,0 -DA:99,0 -DA:104,0 -DA:105,0 -LF:36 +DA:73,0 +DA:74,0 +DA:78,0 +DA:79,0 +LF:28 LH:0 end_of_record -SF:lib/src/directives/text_directive.dart -DA:7,3 -DA:9,2 -DA:10,2 -DA:14,1 -DA:16,1 -DA:17,1 -DA:21,2 -DA:23,1 -DA:24,1 -DA:28,1 -DA:30,1 -DA:31,1 -DA:35,1 -DA:36,1 -DA:37,1 -DA:41,3 -DA:46,0 -DA:47,0 -DA:52,4 -DA:54,1 -DA:56,1 -DA:63,4 -DA:68,2 -DA:69,2 -DA:71,1 -DA:74,3 -DA:76,1 -DA:79,1 -DA:80,2 -LF:29 -LH:27 -end_of_record -SF:lib/src/factory/mix_provider.dart -DA:21,3 -DA:27,3 -DA:28,5 -DA:36,0 +SF:lib/src/recipes/image/image_util.dart +DA:14,64 +DA:16,0 +DA:17,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:26,0 +DA:27,0 +DA:30,0 DA:37,0 DA:40,0 -DA:49,0 -DA:53,0 -DA:56,3 -DA:62,3 -DA:64,2 -DA:66,2 -DA:70,6 -LF:13 -LH:8 -end_of_record -SF:lib/src/factory/mix_provider_data.dart -DA:25,31 -DA:31,31 -DA:32,31 -DA:34,31 -DA:35,31 -DA:36,31 -DA:43,14 -DA:48,1 -DA:49,1 -DA:54,1 -DA:55,3 -DA:61,9 -DA:62,18 -DA:65,8 -DA:66,16 -DA:74,3 -DA:75,3 -DA:76,3 -DA:77,9 -DA:84,0 -DA:85,0 -DA:88,31 -DA:93,62 -DA:95,62 -DA:96,62 -DA:99,31 -DA:100,31 -DA:103,2 -DA:104,3 -DA:107,1 -DA:108,0 -DA:111,1 -LF:32 -LH:29 -end_of_record -SF:lib/src/factory/style_group.dart -DA:4,0 -LF:1 -LH:0 +LF:11 +LH:1 end_of_record -SF:lib/src/factory/style_mix.dart -DA:31,84 -DA:47,12 -DA:69,12 -DA:72,12 -DA:74,12 -DA:86,35 -DA:87,35 -DA:88,35 -DA:90,48 -DA:91,13 -DA:92,13 -DA:93,3 -DA:94,2 -DA:95,2 -DA:96,3 -DA:97,3 -DA:99,2 -DA:103,35 -DA:114,1 -DA:131,1 -DA:132,1 -DA:134,3 -DA:156,1 -DA:162,8 -DA:165,5 -DA:168,2 -DA:173,3 -DA:200,1 -DA:201,2 -DA:207,3 -DA:211,3 -DA:212,0 -DA:213,0 -DA:223,4 -DA:226,9 -DA:227,9 -DA:229,3 -DA:258,1 -DA:260,1 -DA:265,1 -DA:266,1 -DA:270,1 -DA:275,2 -DA:276,1 -DA:277,1 -DA:279,1 -DA:284,2 -DA:287,2 -DA:288,1 -DA:290,1 -DA:295,1 -DA:296,1 -DA:301,1 -DA:307,5 -DA:310,1 -DA:313,1 -DA:339,1 -DA:343,1 -DA:347,2 -DA:348,2 -DA:349,1 -DA:352,2 -DA:357,4 -DA:359,1 -DA:385,1 -DA:386,1 -DA:388,2 -DA:389,1 -DA:390,2 -DA:394,1 -DA:397,4 -DA:401,4 -DA:402,12 -DA:403,9 -DA:406,1 -DA:407,5 -DA:414,1 -LF:77 -LH:75 +SF:lib/src/recipes/stack/stack_attribute.dart +DA:13,130 +DA:23,2 +DA:24,2 +DA:27,2 +DA:29,2 +DA:30,2 +DA:31,2 +DA:32,2 +DA:33,2 +DA:37,1 +DA:41,1 +DA:42,2 +DA:43,2 +DA:44,2 +DA:45,2 +DA:49,0 +DA:50,0 +LF:17 +LH:15 end_of_record -SF:lib/src/helpers/extensions/build_context_ext.dart -DA:8,2 -DA:13,2 -DA:16,2 -DA:19,0 -DA:22,3 -DA:25,2 -DA:28,3 -DA:31,3 -DA:34,2 -DA:37,3 -DA:40,3 -DA:43,3 -LF:12 +SF:lib/src/recipes/stack/stack_spec.dart +DA:11,3 +DA:18,1 +DA:20,1 +DA:21,3 +DA:22,2 +DA:23,2 +DA:24,2 +DA:28,0 +DA:30,0 +DA:31,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:38,1 +DA:45,1 +DA:46,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:53,1 +DA:54,5 +LF:21 LH:11 end_of_record -SF:lib/src/helpers/extensions/style_mix_ext.dart -DA:12,1 -DA:18,1 -DA:19,1 -DA:26,1 -DA:33,1 -DA:41,1 -DA:48,1 -DA:49,1 -DA:56,1 -DA:63,1 -DA:64,1 -DA:71,1 -DA:79,1 -DA:82,1 -DA:88,1 -DA:95,1 -DA:96,1 -DA:103,1 -DA:110,1 -DA:111,1 -DA:118,1 -DA:125,1 -DA:127,1 -LF:23 -LH:23 +SF:lib/src/recipes/stack/stack_util.dart +DA:13,64 +DA:15,1 +DA:21,1 +DA:28,2 +DA:31,1 +DA:32,3 +DA:35,1 +DA:36,3 +DA:39,1 +DA:40,1 +DA:41,2 +DA:45,1 +DA:46,3 +DA:49,0 +DA:55,0 +LF:15 +LH:13 end_of_record -SF:lib/src/specs/container_spec.dart -DA:21,4 -DA:31,4 -DA:32,4 -DA:33,4 -DA:34,4 -DA:35,4 -DA:36,4 -DA:37,4 -DA:38,4 -DA:39,4 +SF:lib/src/recipes/stack/stack_widget.dart +DA:11,1 +DA:19,1 +DA:21,1 +DA:22,1 +DA:23,0 +DA:26,2 +DA:27,1 +DA:28,1 +DA:30,1 +DA:32,2 +DA:38,1 DA:43,1 -DA:55,1 +DA:45,1 +DA:46,2 +DA:47,2 +DA:48,2 +DA:49,2 +DA:50,1 DA:56,1 -DA:57,1 -DA:58,1 -DA:59,1 -DA:60,1 -DA:61,1 -DA:62,1 -DA:66,1 -DA:68,1 -DA:69,3 -DA:70,3 -DA:71,3 -DA:72,3 -DA:73,3 -DA:74,4 -DA:75,2 -DA:79,0 -DA:80,0 -DA:81,0 -DA:82,0 -DA:83,0 -DA:84,0 -DA:85,0 -DA:86,0 -DA:87,0 -LF:37 -LH:28 +DA:65,1 +DA:67,2 +DA:68,2 +DA:69,2 +DA:71,1 +DA:73,2 +LF:25 +LH:24 end_of_record -SF:lib/src/specs/flex_spec.dart -DA:17,3 -DA:28,2 -DA:29,2 -DA:31,2 -DA:33,2 -DA:34,2 -DA:36,2 -DA:37,2 -DA:38,2 +SF:lib/src/recipes/text/text_attribute.dart +DA:25,70 DA:39,2 DA:40,2 -DA:44,1 -DA:46,1 -DA:47,3 +DA:42,2 +DA:43,3 +DA:44,3 +DA:45,2 DA:48,3 -DA:49,3 DA:50,3 DA:51,3 -DA:52,3 +DA:52,4 DA:53,3 DA:54,3 -DA:58,1 -DA:69,1 +DA:55,3 +DA:56,6 +DA:57,3 +DA:58,3 +DA:59,3 +DA:60,3 +DA:61,4 +DA:65,3 +DA:69,3 +DA:70,5 +DA:71,7 +DA:72,5 +DA:73,5 +DA:74,5 +DA:75,7 +DA:76,5 +DA:77,5 +DA:78,5 +DA:79,5 +DA:80,15 +DA:84,1 +DA:85,1 +DA:86,1 +DA:87,1 +DA:88,1 +DA:89,1 +DA:90,1 +DA:91,1 +DA:92,1 +DA:93,1 +DA:94,1 +DA:95,1 +DA:96,1 +LF:46 +LH:46 +end_of_record +SF:lib/src/recipes/text/text_spec.dart +DA:19,4 +DA:34,0 +DA:57,2 +DA:60,6 +DA:63,0 +DA:67,0 +DA:68,0 +DA:69,0 DA:70,0 DA:71,0 DA:72,0 @@ -1635,521 +2550,509 @@ DA:74,0 DA:75,0 DA:76,0 DA:77,0 -DA:81,1 +DA:78,0 DA:82,1 -DA:83,1 -DA:84,1 -DA:85,1 DA:86,1 -DA:87,1 -DA:88,1 -DA:89,1 -DA:90,1 -LF:41 -LH:33 -end_of_record -SF:lib/src/specs/icon_spec.dart -DA:14,3 -DA:20,2 -DA:21,2 -DA:22,2 -DA:23,2 -DA:24,2 -DA:28,1 -DA:30,1 -DA:31,3 -DA:32,3 -DA:33,3 -DA:37,1 -DA:43,1 -DA:44,0 -DA:45,0 -DA:46,1 -DA:50,0 -DA:51,0 -LF:18 -LH:14 +DA:87,3 +DA:88,3 +DA:89,3 +DA:91,3 +DA:92,3 +DA:93,3 +DA:94,3 +DA:96,3 +DA:97,3 +DA:98,3 +DA:99,3 +DA:103,1 +DA:117,1 +DA:118,0 +DA:119,0 +DA:120,0 +DA:121,0 +DA:122,0 +DA:123,0 +DA:124,0 +DA:125,0 +DA:126,0 +DA:127,0 +DA:128,0 +DA:132,1 +DA:133,1 +DA:134,1 +DA:135,1 +DA:136,1 +DA:137,1 +DA:138,1 +DA:139,1 +DA:140,1 +DA:141,1 +DA:142,1 +DA:143,1 +DA:144,1 +LF:56 +LH:31 end_of_record -SF:lib/src/specs/image_spec.dart -DA:18,0 +SF:lib/src/recipes/text/text_util.dart +DA:19,64 +DA:21,0 +DA:22,0 +DA:25,0 DA:26,0 -DA:27,0 -DA:28,0 DA:29,0 DA:30,0 -DA:31,0 -DA:32,0 -DA:36,0 -DA:38,0 -DA:39,0 -DA:40,0 +DA:33,0 +DA:34,0 +DA:37,2 +DA:38,6 DA:41,0 DA:42,0 DA:43,0 DA:47,0 -DA:56,0 -DA:57,0 -DA:58,0 +DA:48,0 +DA:49,0 +DA:53,0 +DA:54,0 +DA:55,0 DA:59,0 DA:60,0 -DA:61,0 -DA:65,0 -DA:66,0 -LF:24 -LH:0 +DA:63,0 +DA:64,0 +DA:67,0 +DA:68,0 +DA:71,2 +DA:84,2 +DA:98,4 +DA:101,0 +DA:114,0 +DA:116,0 +DA:120,0 +DA:128,0 +LF:34 +LH:6 end_of_record -SF:lib/src/specs/stack_spec.dart -DA:14,3 -DA:21,2 -DA:22,2 -DA:23,2 -DA:24,2 -DA:25,2 -DA:26,2 +SF:lib/src/recipes/text/text_widget.dart +DA:8,1 +DA:21,1 +DA:23,1 +DA:25,0 +DA:28,2 +DA:29,1 DA:30,1 DA:32,1 -DA:33,3 -DA:34,2 +DA:34,1 DA:35,2 -DA:36,2 +DA:36,1 +DA:37,1 +DA:38,1 +DA:39,1 DA:40,1 +DA:41,1 +DA:42,1 +DA:43,1 +DA:44,1 +DA:45,1 +DA:46,1 DA:47,1 -DA:48,0 -DA:49,0 -DA:50,0 -DA:51,0 -DA:55,1 -DA:56,5 -LF:21 -LH:17 +LF:22 +LH:21 end_of_record -SF:lib/src/specs/text_spec.dart -DA:23,3 -DA:37,2 -DA:38,2 -DA:39,2 -DA:40,2 -DA:41,2 -DA:42,2 -DA:43,2 -DA:44,2 -DA:45,2 -DA:47,2 -DA:48,2 -DA:49,2 -DA:50,3 -DA:54,2 -DA:58,3 -DA:59,1 -DA:65,1 -DA:69,1 +SF:lib/src/theme/mix_theme.dart +DA:12,11 +DA:14,39 +DA:15,46 +DA:16,37 +DA:19,1 +DA:20,2 +DA:25,0 +DA:26,0 +DA:37,94 +DA:45,51 +DA:46,0 +DA:54,43 +DA:61,43 DA:70,3 -DA:71,3 -DA:72,3 -DA:74,3 -DA:75,3 -DA:76,3 DA:77,3 DA:78,3 DA:79,3 DA:80,3 DA:81,3 -DA:85,1 -DA:99,1 -DA:100,0 -DA:101,0 +DA:82,3 +DA:86,0 +DA:93,0 +DA:94,0 +DA:95,0 +DA:96,0 +DA:97,0 +DA:98,0 DA:102,0 DA:103,0 -DA:104,0 -DA:105,0 DA:106,0 -DA:107,0 DA:108,0 DA:109,0 -DA:110,0 -DA:114,1 -DA:115,1 -DA:116,1 -DA:117,1 -DA:118,1 -DA:119,1 -DA:120,1 -DA:121,1 -DA:122,1 -DA:123,1 -DA:124,1 -DA:125,1 -DA:126,1 -LF:56 -LH:45 +DA:112,0 +DA:114,0 +DA:115,0 +DA:118,0 +DA:119,0 +DA:121,0 +DA:122,0 +DA:123,0 +DA:125,0 +DA:128,0 +DA:129,0 +DA:131,0 +DA:134,1 +DA:135,6 +DA:142,36 +DA:144,1 +DA:145,4 +DA:148,0 +DA:150,1 +DA:151,4 +DA:154,0 +DA:156,1 +DA:157,4 +DA:160,0 +DA:162,4 +DA:163,4 +DA:164,0 +DA:166,0 +DA:169,0 +DA:170,0 +LF:62 +LH:28 end_of_record -SF:lib/src/theme/mix_theme.dart -DA:13,6 -DA:14,6 -DA:16,5 -DA:17,10 -DA:18,3 +SF:lib/src/theme/tokens/breakpoints.dart +DA:15,65 +DA:20,2 +DA:21,12 +DA:43,64 +DA:45,0 +DA:46,0 +LF:6 +LH:4 +end_of_record +SF:lib/src/theme/tokens/color_token.dart +DA:6,4 +DA:8,0 +DA:10,2 +DA:11,4 +DA:22,4 +DA:24,1 +DA:28,4 +DA:31,1 +DA:32,2 +LF:9 +LH:8 +end_of_record +SF:lib/src/theme/tokens/material_tokens.dart +DA:12,3 +DA:17,3 +DA:22,3 +DA:27,3 +DA:32,3 +DA:37,3 +DA:42,3 +DA:47,3 +DA:52,3 +DA:57,3 +DA:62,3 +DA:67,3 +DA:70,1 +DA:79,3 +DA:83,3 +DA:87,3 +DA:91,3 +DA:95,3 +DA:99,3 +DA:103,3 +DA:107,3 +DA:111,3 +DA:115,3 +DA:119,3 +DA:123,3 +DA:127,3 +DA:131,3 +DA:135,3 +DA:140,3 +DA:144,3 +DA:148,3 +DA:152,3 +DA:156,3 +DA:160,3 +DA:164,3 +DA:168,3 +DA:172,3 +DA:176,3 +DA:180,3 +DA:184,3 +DA:188,3 +DA:191,1 +DA:194,3 +DA:195,3 +DA:201,1 +DA:205,3 +DA:206,3 +DA:210,0 +DA:211,0 +DA:212,0 +DA:213,0 +DA:214,0 +DA:215,0 +DA:216,0 +DA:217,0 +DA:218,0 +DA:219,0 +DA:220,0 +DA:221,0 +DA:225,0 +DA:226,0 +DA:227,0 +DA:228,0 +DA:229,0 +DA:230,0 +DA:231,0 +DA:232,0 +DA:233,0 +DA:234,0 +DA:235,0 +DA:236,0 +DA:237,0 +DA:238,0 +DA:239,0 +DA:240,0 +DA:241,0 +DA:242,0 +DA:243,0 +DA:244,0 +DA:245,0 +DA:246,0 +DA:247,0 +DA:248,0 +DA:249,0 +DA:250,0 +DA:251,0 +DA:252,0 +LF:87 +LH:47 +end_of_record +SF:lib/src/theme/tokens/mix_token.dart +DA:15,201 +DA:17,0 +DA:18,0 DA:21,0 DA:22,0 -DA:27,0 -DA:28,0 -DA:41,44 -DA:49,39 -DA:50,0 -DA:58,5 -DA:65,5 -DA:66,11 -DA:67,6 -DA:68,10 -DA:69,6 -DA:70,6 +DA:25,0 +DA:26,0 +DA:29,4 +DA:31,4 +DA:35,9 +DA:37,21 +DA:40,5 +DA:41,20 +DA:56,68 +DA:59,64 +DA:61,5 +DA:62,18 +DA:64,11 +DA:69,0 +DA:70,0 +DA:73,0 DA:74,0 +LF:22 +LH:12 +end_of_record +SF:lib/src/theme/tokens/radius_token.dart +DA:15,66 +DA:17,1 +DA:19,1 +DA:20,2 +DA:32,2 +DA:34,1 +DA:38,4 +DA:41,1 +DA:42,2 +DA:51,1 +DA:53,0 +DA:57,0 +DA:60,3 +DA:62,3 +DA:64,3 +DA:66,3 +LF:16 +LH:14 +end_of_record +SF:lib/src/theme/tokens/space_token.dart +DA:32,65 +DA:34,1 +DA:35,3 +LF:3 +LH:3 +end_of_record +SF:lib/src/theme/tokens/text_style_token.dart +DA:9,3 +DA:11,1 +DA:13,2 +DA:17,4 +DA:29,2 +DA:31,1 +DA:35,4 +DA:38,0 +DA:39,0 +DA:41,0 +DA:42,0 +DA:44,0 +DA:45,0 +DA:47,0 +DA:48,0 +DA:50,0 +DA:51,0 +DA:53,0 +DA:54,0 +DA:56,0 +DA:57,0 +DA:59,0 +DA:60,0 +DA:62,0 +DA:63,0 +DA:65,0 +DA:66,0 +DA:68,0 +DA:70,0 +DA:72,0 +DA:73,0 +DA:75,0 +DA:76,0 +DA:78,0 +DA:79,0 DA:81,0 DA:82,0 -DA:83,0 DA:84,0 DA:85,0 -DA:86,0 -DA:90,0 +DA:87,0 +DA:89,0 DA:91,0 -DA:94,15 +DA:92,0 +DA:94,0 DA:95,0 -DA:96,0 DA:97,0 -DA:98,0 DA:99,0 -DA:100,0 -DA:104,15 -DA:105,2 -DA:107,2 -DA:109,2 -DA:111,2 -DA:118,31 -DA:120,9 -DA:122,1 -DA:123,5 -DA:127,1 -DA:128,2 -DA:131,0 -DA:136,1 -DA:137,5 -DA:141,1 -DA:142,2 -DA:146,0 -DA:147,0 -DA:153,1 -DA:154,5 -DA:157,1 -DA:158,2 -DA:161,0 -DA:166,0 -DA:167,0 -DA:171,0 -DA:176,3 -DA:177,6 -DA:178,0 -DA:179,0 -DA:182,0 -DA:184,0 -LF:67 -LH:36 +DA:101,0 +DA:102,0 +DA:104,0 +DA:105,0 +DA:107,1 +DA:108,2 +DA:111,0 +DA:112,0 +DA:119,0 +DA:121,0 +DA:123,0 +LF:58 +LH:9 end_of_record -SF:lib/src/theme/tokens/breakpoints.dart -DA:16,50 -DA:22,2 -DA:23,12 -DA:24,4 +SF:lib/src/theme/tokens/token_util.dart +DA:7,0 +DA:18,1 +DA:23,65 +DA:24,2 DA:25,2 -DA:26,3 +DA:26,2 DA:27,2 -DA:28,3 -DA:40,50 -LF:9 -LH:9 -end_of_record -SF:lib/src/theme/tokens/material_tokens.dart -DA:12,4 -DA:17,4 -DA:22,4 -DA:27,4 -DA:32,4 -DA:37,4 -DA:42,4 -DA:47,4 -DA:52,4 -DA:57,4 -DA:62,4 -DA:67,4 -DA:70,1 -DA:79,4 -DA:83,4 -DA:87,4 -DA:91,4 -DA:95,4 -DA:99,4 -DA:103,4 -DA:107,4 -DA:111,4 -DA:115,4 -DA:119,4 -DA:123,4 -DA:127,4 -DA:131,4 -DA:135,4 -DA:140,4 -DA:144,4 -DA:148,4 -DA:152,4 -DA:156,4 -DA:160,4 -DA:164,4 -DA:168,4 -DA:172,4 -DA:176,4 -DA:180,4 -DA:184,4 -DA:188,4 -DA:191,1 -DA:198,1 -LF:43 -LH:43 -end_of_record -SF:lib/src/theme/tokens/mix_token.dart -DA:8,100 -DA:10,3 -DA:14,0 -DA:16,0 -DA:19,6 -DA:20,18 -DA:25,15 -LF:7 -LH:5 -end_of_record -SF:lib/src/theme/tokens/space_token.dart -DA:13,1 -DA:36,50 -DA:38,1 -DA:39,3 -DA:48,2 -DA:50,3 -DA:52,3 -DA:54,3 -DA:56,3 -DA:58,3 -DA:60,3 -DA:62,6 -LF:12 -LH:12 -end_of_record -SF:lib/src/theme/tokens/text_style_token.dart -DA:10,3 -DA:12,1 -DA:16,4 -DA:19,1 -DA:20,2 DA:28,2 -LF:6 -LH:6 +DA:29,2 +DA:33,64 +DA:37,64 +LF:11 +LH:10 end_of_record -SF:lib/src/utils/alignment_util.dart -DA:21,2 -DA:24,4 -DA:27,2 -DA:30,2 -DA:33,4 -DA:36,1 -DA:37,1 -DA:40,2 -DA:43,1 -DA:44,1 -DA:47,1 -DA:48,1 +SF:lib/src/utils/context_variant_util/on_breakpoint_util.dart +DA:12,3 +DA:15,3 +DA:18,3 +DA:21,3 +DA:28,0 +DA:29,0 +DA:34,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:41,0 DA:51,1 DA:52,1 -DA:55,1 -DA:56,1 -DA:59,1 -DA:60,1 -DA:63,1 -DA:64,1 -DA:67,1 -DA:68,1 -DA:71,1 -DA:72,1 -LF:24 -LH:24 -end_of_record -SF:lib/src/utils/border_radius_util.dart -DA:24,1 -DA:25,1 -DA:26,1 -DA:27,1 -DA:28,1 -DA:30,1 -DA:33,1 -DA:39,1 -DA:40,1 -DA:41,1 -DA:42,1 -DA:44,1 -DA:49,1 -DA:50,2 +DA:53,3 DA:54,1 DA:55,2 +DA:57,1 DA:59,1 -DA:60,2 -DA:64,1 -DA:65,2 -DA:70,1 -DA:71,2 -DA:75,1 -DA:76,2 -DA:79,1 -DA:80,1 -DA:81,1 -DA:82,1 -DA:86,1 -DA:87,1 -DA:88,1 -DA:89,1 -DA:94,1 -DA:95,2 -DA:99,1 -DA:100,2 -LF:36 -LH:36 +DA:61,1 +LF:20 +LH:12 end_of_record -SF:lib/src/utils/border_util.dart -DA:6,2 -DA:12,2 -DA:19,2 -DA:24,0 -DA:30,0 -DA:37,0 -DA:47,1 -DA:53,1 -DA:54,1 -DA:63,1 -DA:69,1 -DA:70,1 -DA:79,1 -DA:85,1 -DA:86,1 -DA:95,1 -DA:101,1 -DA:102,1 -DA:111,0 -DA:117,0 -DA:118,0 -DA:127,0 -DA:133,0 -DA:134,0 -DA:143,1 -DA:149,1 -DA:150,1 -DA:159,1 -DA:165,1 -DA:166,1 -DA:175,1 -DA:179,1 -DA:187,3 -DA:193,3 -DA:194,3 -LF:35 -LH:26 +SF:lib/src/utils/context_variant_util/on_brightness_util.dart +DA:10,9 +DA:13,9 +DA:20,3 +DA:21,3 +DA:22,9 +DA:23,3 +DA:24,9 +LF:7 +LH:7 end_of_record -SF:lib/src/utils/box_constraints_util.dart -DA:5,1 -DA:6,1 -DA:8,1 -DA:9,1 -DA:11,1 -DA:12,1 -DA:14,1 -DA:15,1 -DA:17,1 -DA:18,1 +SF:lib/src/utils/context_variant_util/on_directionality_util.dart +DA:7,3 +DA:10,3 +DA:19,1 DA:20,1 -DA:21,1 -LF:12 -LH:12 +DA:21,3 +DA:22,3 +LF:6 +LH:6 end_of_record -SF:lib/src/utils/context_variant_util.dart -DA:9,3 -DA:11,3 -DA:13,3 -DA:15,3 -DA:17,0 -DA:22,0 -DA:28,0 -DA:30,0 -DA:31,0 -DA:32,0 -DA:33,0 -DA:35,0 -DA:41,3 -DA:43,3 -DA:46,3 -DA:47,3 -DA:51,3 -DA:53,3 -DA:55,1 -DA:56,1 -DA:57,3 -DA:58,3 -DA:62,1 -DA:63,1 -DA:64,3 -DA:65,1 -DA:66,3 -DA:71,1 -DA:72,1 -DA:73,3 -DA:74,1 -DA:75,2 -DA:77,1 -DA:79,1 -DA:82,1 -DA:83,0 -DA:86,2 -DA:91,3 -DA:92,3 -DA:93,6 -DA:94,9 -DA:98,1 -DA:99,1 -DA:100,3 -DA:101,1 -DA:102,2 -LF:46 -LH:37 +SF:lib/src/utils/context_variant_util/on_helper_util.dart +DA:12,4 +DA:13,4 +DA:14,8 +DA:15,12 +LF:4 +LH:4 end_of_record -SF:lib/src/utils/decoration_util.dart -DA:6,2 -DA:7,4 -LF:2 -LH:2 +SF:lib/src/utils/context_variant_util/on_orientation_util.dart +DA:9,3 +DA:14,3 +DA:23,1 +DA:24,1 +DA:25,3 +DA:26,1 +DA:27,2 +LF:7 +LH:7 end_of_record -SF:lib/src/utils/gradient_util.dart -DA:5,1 -DA:13,1 -DA:15,1 -DA:16,1 -DA:17,1 -DA:18,1 -DA:21,1 -DA:27,1 -DA:37,1 -DA:39,1 -DA:40,1 +SF:lib/src/utils/decorators_util.dart +DA:21,0 +DA:22,0 +DA:24,1 +DA:25,1 +DA:32,64 +DA:37,2 +DA:38,0 +DA:39,2 DA:41,1 DA:42,1 -DA:47,1 -LF:14 -LH:14 +DA:47,64 +DA:48,3 +DA:49,3 +DA:50,3 +DA:52,3 +DA:56,64 +LF:16 +LH:13 end_of_record SF:lib/src/utils/helper_util.dart -DA:8,99 +DA:8,129 DA:10,1 DA:32,3 DA:35,1 @@ -2175,210 +3078,107 @@ DA:31,4 LF:15 LH:15 end_of_record -SF:lib/src/utils/space_util.dart -DA:10,6 -DA:11,3 -DA:12,3 -DA:13,3 -DA:14,4 -DA:15,4 -DA:16,4 -DA:17,4 -DA:18,4 -DA:19,4 -DA:20,4 -DA:21,4 -DA:22,4 -DA:24,1 -DA:25,1 -DA:26,1 -DA:27,1 -DA:28,1 -DA:30,0 -DA:31,0 -DA:39,6 -DA:40,3 -DA:41,3 -DA:42,3 -DA:43,4 -DA:44,4 -DA:45,4 -DA:46,4 -DA:47,4 -DA:48,4 -DA:49,4 -DA:50,4 -DA:51,4 -DA:53,1 -DA:54,1 -DA:55,1 -DA:56,1 -DA:57,1 -DA:59,0 -DA:60,0 -DA:75,49 -DA:77,2 -DA:79,3 -DA:81,3 -DA:82,3 -DA:83,3 -DA:84,3 -DA:86,3 -DA:87,3 -DA:89,1 -DA:90,2 -DA:91,1 -DA:92,1 -DA:93,1 -DA:94,1 -DA:98,2 -DA:112,4 -DA:123,49 -DA:125,2 -DA:127,3 -DA:128,3 -DA:130,1 -DA:131,2 -DA:132,1 -DA:133,1 -DA:134,1 -DA:135,1 -DA:139,1 -DA:154,2 -LF:69 -LH:65 -end_of_record -SF:lib/src/utils/text_util.dart -DA:13,0 -DA:14,0 -DA:17,1 -DA:18,2 -DA:20,2 -DA:41,2 -DA:43,1 -DA:44,2 -DA:47,1 -DA:58,2 -DA:64,2 -DA:66,2 -DA:68,1 -DA:69,1 -DA:70,1 -DA:71,2 -DA:72,1 -DA:76,2 -DA:78,1 -DA:80,2 -LF:20 -LH:18 -end_of_record SF:lib/src/variants/context_variant.dart -DA:16,5 -DA:18,3 -DA:41,3 -DA:44,3 -DA:47,6 -DA:50,1 -DA:51,3 +DA:50,10 +DA:52,3 +DA:75,3 +DA:78,3 +DA:81,6 +DA:84,1 +DA:85,3 LF:7 LH:7 end_of_record -SF:lib/src/variants/variant.dart -DA:16,48 -DA:19,3 -DA:22,3 -DA:27,1 -DA:49,1 -DA:52,1 -DA:56,2 +SF:lib/src/variants/multi_variant.dart +DA:57,3 DA:59,3 -DA:60,6 -LF:9 -LH:9 -end_of_record -SF:lib/src/widgets/container_widget.dart -DA:11,3 -DA:15,3 -DA:17,6 -DA:18,3 -DA:20,3 -DA:21,3 -DA:22,3 -DA:23,3 -DA:24,3 -DA:25,3 -DA:26,3 -DA:27,3 -DA:28,3 -LF:13 -LH:13 -end_of_record -SF:lib/src/widgets/empty_widget.dart -DA:4,1 -DA:6,0 -LF:2 -LH:1 -end_of_record -SF:lib/src/widgets/flex_widget.dart -DA:8,1 -DA:19,1 -DA:21,2 -DA:22,1 -DA:23,1 -DA:24,1 -DA:25,4 -DA:26,3 -DA:27,4 -DA:35,2 -DA:37,1 -DA:38,1 -DA:39,2 -DA:40,2 -DA:42,2 -DA:43,2 -DA:44,1 -DA:51,1 -DA:56,1 -DA:60,1 -DA:65,1 -DA:69,1 -DA:80,1 -DA:82,2 -DA:83,1 -DA:85,1 -DA:87,1 -DA:88,1 -DA:96,1 -DA:101,1 -DA:105,1 -DA:110,1 -LF:32 -LH:32 +DA:63,3 +DA:64,15 +DA:65,12 +DA:67,3 +DA:73,3 +DA:74,3 +DA:80,2 +DA:81,2 +DA:97,2 +DA:98,6 +DA:100,4 +DA:101,2 +DA:102,0 +DA:121,2 +DA:122,2 +DA:123,4 +DA:125,4 +DA:126,4 +DA:127,2 +DA:145,1 +DA:146,2 +DA:148,2 +DA:149,4 +DA:150,4 +DA:151,4 +DA:158,1 +DA:181,1 +DA:184,1 +DA:186,2 +DA:189,0 +DA:190,0 +LF:33 +LH:30 end_of_record -SF:lib/src/widgets/icon_widget.dart -DA:7,1 -DA:18,1 -DA:20,2 -DA:22,1 -DA:24,1 -DA:25,1 -DA:26,1 +SF:lib/src/variants/variant.dart +DA:45,65 +DA:57,3 +DA:69,3 +DA:75,1 +DA:97,1 +DA:100,1 +DA:102,2 +DA:105,3 +DA:106,6 +LF:9 +LH:9 +end_of_record +SF:lib/src/widgets/empty_widget.dart +DA:4,1 +DA:6,0 +LF:2 +LH:1 +end_of_record +SF:lib/src/widgets/gap_widget.dart +DA:9,1 +DA:13,1 +DA:15,2 +DA:18,0 +DA:20,0 DA:27,1 -DA:28,1 -DA:29,1 -DA:36,0 -DA:49,0 -DA:51,0 +DA:30,1 +DA:31,1 +DA:33,1 +DA:34,1 +DA:38,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:45,1 +DA:47,1 +DA:48,1 +DA:52,2 DA:53,0 -DA:55,0 +DA:54,1 +DA:55,2 DA:56,0 -DA:57,0 -DA:58,0 -DA:59,0 -DA:60,0 -DA:61,0 -LF:21 -LH:10 +DA:57,1 +DA:58,1 +DA:59,3 +DA:60,3 +DA:63,1 +DA:68,0 +DA:70,0 +DA:71,0 +LF:31 +LH:19 end_of_record SF:lib/src/widgets/pressable/pressable.notifier.dart DA:6,3 @@ -2447,228 +3247,534 @@ DA:129,2 LF:54 LH:35 end_of_record -SF:lib/src/widgets/stack_widget.dart -DA:8,1 -DA:17,1 -DA:19,2 -DA:20,1 -DA:24,1 -DA:25,1 -DA:26,1 -DA:27,1 -DA:28,1 -DA:29,1 -DA:36,1 -DA:45,1 -DA:47,2 -DA:48,1 -DA:50,2 -LF:15 -LH:15 -end_of_record SF:lib/src/widgets/styled_widget.dart DA:8,3 DA:22,3 -DA:23,3 +DA:23,6 +DA:31,0 +LF:4 +LH:3 +end_of_record +SF:lib/src/attributes/color/color_util.dart +DA:11,66 +DA:16,0 +LF:2 +LH:1 +end_of_record +SF:lib/src/attributes/color/color_attribute.dart +DA:11,2 +DA:16,1 +DA:17,2 +DA:19,0 +DA:20,0 +LF:5 +LH:3 +end_of_record +SF:lib/src/attributes/constraints/constraints_dto.dart +DA:8,7 +DA:18,7 DA:25,3 +DA:26,3 DA:27,3 -DA:36,0 -LF:6 -LH:5 +DA:28,3 +DA:29,3 +DA:30,3 +DA:34,0 +DA:35,0 +DA:38,4 +DA:42,4 +DA:43,2 +DA:44,1 +DA:45,1 +DA:46,4 +DA:47,4 +DA:48,4 +DA:49,4 +DA:50,4 +DA:57,2 +DA:61,2 +DA:62,2 +DA:63,4 +DA:64,2 +DA:65,4 +DA:69,3 +DA:70,15 +LF:28 +LH:26 end_of_record -SF:lib/src/widgets/text_widget.dart -DA:10,1 +SF:lib/src/attributes/decoration/decoration_dto.dart +DA:17,9 +DA:19,0 +DA:22,0 +DA:23,0 +DA:25,0 +DA:26,0 +DA:29,0 +DA:30,0 +DA:44,9 +DA:53,3 +DA:54,3 +DA:55,6 +DA:56,6 +DA:57,6 +DA:58,6 +DA:59,9 +DA:60,3 +DA:64,0 +DA:65,0 +DA:68,6 +DA:70,6 +DA:71,11 +DA:72,9 +DA:73,8 +DA:74,10 +DA:75,8 +DA:76,6 +DA:80,2 +DA:84,2 +DA:85,6 +DA:86,6 +DA:88,6 +DA:89,6 +DA:90,6 +DA:94,4 +DA:96,28 +DA:106,3 +DA:113,1 +DA:114,1 +DA:115,2 +DA:116,1 +DA:117,2 +DA:118,3 +DA:122,0 +DA:123,0 +DA:126,2 +DA:128,2 +DA:129,2 +DA:130,4 +DA:131,10 +DA:132,2 +DA:136,1 +DA:140,1 +DA:141,2 +DA:142,2 +DA:143,1 +DA:144,2 +DA:148,2 +DA:149,10 +LF:59 +LH:48 +end_of_record +SF:lib/src/attributes/gradient/gradient_dto.dart +DA:34,5 +DA:38,3 +DA:39,3 +DA:40,3 +DA:42,1 +DA:43,1 +DA:45,0 +DA:46,0 +DA:49,0 +DA:56,3 +DA:57,3 +DA:69,0 +DA:70,0 +DA:121,5 +DA:130,4 +DA:131,4 +DA:132,4 +DA:133,4 +DA:134,4 +DA:135,4 +DA:136,12 +DA:137,4 +DA:141,0 +DA:142,0 +DA:145,3 +DA:147,3 +DA:148,3 +DA:149,3 +DA:150,16 +DA:151,6 +DA:152,3 +DA:153,3 +DA:157,1 +DA:161,1 +DA:162,1 +DA:163,1 +DA:164,1 +DA:165,2 +DA:166,3 +DA:167,3 +DA:171,4 +DA:172,28 +DA:230,2 +DA:241,2 +DA:242,2 +DA:243,2 +DA:244,2 +DA:245,2 +DA:246,2 +DA:247,2 +DA:248,2 +DA:249,6 +DA:250,2 +DA:254,0 +DA:255,0 +DA:258,2 +DA:260,2 +DA:261,2 +DA:262,2 +DA:263,11 +DA:264,5 +DA:265,2 +DA:266,2 +DA:267,2 +DA:268,2 +DA:272,0 +DA:276,0 +DA:277,0 +DA:278,0 +DA:279,0 +DA:280,0 +DA:281,0 +DA:282,0 +DA:283,0 +DA:284,0 +DA:288,2 +DA:290,18 +DA:347,1 +DA:357,1 +DA:358,1 +DA:359,1 +DA:360,1 +DA:361,1 +DA:362,1 +DA:363,1 +DA:364,3 +DA:365,1 +DA:369,0 +DA:370,0 +DA:373,1 +DA:375,1 +DA:376,1 +DA:377,1 +DA:378,1 +DA:379,6 +DA:380,4 +DA:381,1 +DA:382,1 +DA:386,0 +DA:390,0 +DA:391,0 +DA:392,0 +DA:393,0 +DA:394,0 +DA:395,0 +DA:396,0 +DA:397,0 +DA:401,1 +DA:403,8 +LF:109 +LH:79 +end_of_record +SF:lib/src/attributes/spacing/spacing_dto.dart +DA:10,8 +DA:19,1 +DA:20,1 +DA:21,1 +DA:22,1 DA:23,1 -DA:25,2 -DA:26,1 +DA:24,1 +DA:25,1 +DA:27,1 DA:28,1 -DA:29,2 +DA:29,1 DA:30,1 DA:31,1 DA:32,1 -DA:33,1 -DA:34,1 -DA:35,1 -DA:36,1 -DA:37,1 -DA:38,1 -DA:39,1 -DA:40,1 -DA:41,1 -LF:18 -LH:18 -end_of_record -SF:lib/src/theme/tokens/color_token.dart -DA:10,5 -DA:12,1 -DA:16,4 -DA:19,1 -DA:20,2 -DA:27,2 -LF:6 -LH:6 -end_of_record -SF:lib/src/core/attributes_map.dart -DA:15,31 -DA:17,31 -DA:18,31 -DA:19,40 -DA:20,9 -DA:21,9 -DA:22,6 -DA:24,9 -DA:28,31 -DA:31,9 -DA:33,9 -DA:35,3 -DA:37,0 -DA:39,0 -DA:41,9 -DA:43,9 -DA:44,36 -DA:46,1 +DA:36,0 +DA:43,0 +DA:44,0 DA:47,3 -DA:50,3 -DA:51,9 +DA:51,2 +DA:52,3 DA:53,3 -DA:56,0 -DA:57,0 -LF:24 -LH:20 +DA:54,3 +DA:55,3 +DA:56,4 +DA:57,4 +DA:61,4 +DA:63,4 +DA:64,1 +DA:65,3 +DA:66,3 +DA:67,3 +DA:68,3 +DA:70,4 +DA:71,12 +DA:72,12 +DA:73,12 +DA:74,12 +LF:37 +LH:34 end_of_record -SF:lib/src/decorators/clip_decorator.dart -DA:11,3 -DA:19,0 -DA:20,0 +SF:lib/src/attributes/strut_style/strut_style_dto.dart +DA:17,7 +DA:28,3 +DA:29,3 DA:30,3 -DA:32,1 -DA:34,1 +DA:31,3 +DA:32,3 +DA:33,3 +DA:34,3 DA:35,3 DA:36,3 -DA:40,1 -DA:45,1 -DA:46,0 -DA:47,0 -DA:51,0 -DA:52,0 +DA:37,3 +DA:41,0 +DA:42,0 +DA:45,3 +DA:49,2 +DA:50,3 +DA:51,4 +DA:52,4 +DA:53,3 +DA:54,4 +DA:55,3 DA:56,3 -DA:58,1 -DA:60,1 -DA:61,1 -DA:62,2 -DA:66,2 -DA:68,6 -DA:71,2 -DA:73,2 -DA:74,2 -DA:75,2 -DA:82,3 -DA:84,1 -DA:86,1 -DA:87,1 -DA:88,2 +DA:57,4 +DA:61,5 +DA:65,5 +DA:66,6 +DA:67,7 +DA:68,5 +DA:69,6 +DA:70,6 +DA:71,6 +DA:72,6 +DA:73,7 +DA:77,4 +DA:78,4 +DA:79,4 +DA:80,4 +DA:81,4 +DA:82,4 +DA:83,4 +DA:84,4 +DA:85,4 +DA:86,4 +LF:43 +LH:41 +end_of_record +SF:lib/src/attributes/strut_style/strut_style_util.dart +DA:10,1 +DA:11,0 +DA:13,1 +DA:14,3 +DA:17,1 +DA:18,3 +DA:21,1 +DA:22,3 +DA:25,1 +DA:26,3 +DA:29,1 +DA:30,1 +DA:31,2 +DA:35,2 +DA:37,2 +DA:39,0 +DA:40,0 +DA:42,1 +DA:52,1 +DA:63,2 +LF:20 +LH:17 +end_of_record +SF:lib/src/attributes/text_style/text_style_dto.dart +DA:41,5 +DA:65,0 +DA:88,1 +DA:89,1 +DA:90,1 +DA:91,1 DA:92,2 -DA:94,2 -DA:95,2 -DA:96,2 -DA:100,2 -DA:102,2 -DA:103,2 -DA:104,2 -DA:113,2 -DA:119,1 -DA:121,1 -DA:122,3 -DA:123,3 -DA:124,3 +DA:93,1 +DA:94,1 +DA:95,1 +DA:96,1 +DA:97,1 +DA:98,1 +DA:99,1 +DA:100,1 +DA:101,1 +DA:102,1 +DA:103,1 +DA:104,1 +DA:105,1 +DA:106,1 +DA:107,1 +DA:108,1 +DA:109,1 +DA:110,1 +DA:114,0 +DA:115,0 +DA:118,10 +DA:120,1 +DA:124,2 DA:128,1 +DA:129,2 +DA:130,2 +DA:131,1 +DA:132,2 +DA:133,1 DA:134,1 -DA:135,0 -DA:136,0 -DA:137,0 -DA:141,0 -DA:142,0 +DA:135,1 +DA:136,2 +DA:137,2 +DA:138,1 +DA:139,1 +DA:140,1 +DA:142,2 +DA:143,1 +DA:144,1 +DA:145,1 DA:146,2 +DA:147,1 DA:148,1 -DA:150,1 +DA:149,1 +DA:150,2 DA:151,1 -DA:152,2 -DA:156,1 -DA:158,1 -DA:159,1 -DA:160,1 -DA:164,0 -DA:165,0 -DA:167,1 -DA:169,1 -DA:170,1 -DA:171,1 -DA:179,3 -DA:185,1 -DA:187,1 -DA:188,1 -DA:189,1 -DA:190,1 -DA:194,1 -DA:196,1 -DA:197,1 -DA:198,1 -DA:199,1 -DA:203,0 -DA:204,0 -DA:206,1 -DA:208,1 -DA:209,1 -DA:210,1 -DA:211,1 -DA:218,0 -DA:224,0 -DA:226,0 -DA:231,0 -DA:239,0 -DA:241,0 -DA:242,0 -DA:243,0 -DA:244,0 -DA:246,0 -DA:252,50 -DA:253,1 -DA:255,1 -DA:256,3 -DA:257,3 -DA:258,2 -DA:259,1 -DA:264,0 -LF:102 -LH:76 -end_of_record -SF:lib/src/utils/decorators_util.dart -DA:15,2 +DA:152,1 +DA:156,5 +DA:158,5 +DA:159,0 +DA:160,5 +DA:161,10 +DA:162,6 +DA:163,5 +DA:164,5 +DA:165,5 +DA:166,5 +DA:167,5 +DA:168,5 +DA:169,5 +DA:170,5 +DA:171,5 +DA:172,5 +DA:173,9 +DA:174,5 +DA:175,5 +DA:176,7 +DA:177,5 +DA:178,5 +DA:179,5 +DA:180,5 +DA:181,5 +DA:185,2 +DA:186,2 +DA:187,2 +DA:188,2 +DA:189,2 +DA:190,2 +DA:191,2 +DA:192,2 +DA:193,2 +DA:194,2 +DA:195,2 +DA:196,2 +DA:197,2 +DA:198,2 +DA:199,2 +DA:200,2 +DA:201,2 +DA:202,2 +DA:203,2 +DA:204,2 +DA:205,2 +DA:206,2 +DA:207,2 +DA:208,2 +DA:215,5 +DA:217,4 +DA:240,8 +DA:265,5 +DA:266,10 +DA:269,0 +DA:270,0 +DA:273,1 +DA:274,2 +DA:277,0 +DA:278,0 +DA:287,5 +DA:289,5 +DA:290,15 +DA:291,7 +DA:292,5 +DA:295,2 +DA:297,4 +DA:300,2 +DA:301,4 +LF:123 +LH:115 +end_of_record +SF:lib/src/helpers/build_context_ext.dart +DA:8,8 +DA:13,2 DA:16,2 -DA:17,2 -DA:25,1 -DA:30,1 -DA:33,1 -DA:39,1 -DA:40,1 -LF:8 -LH:8 -end_of_record -SF:lib/src/theme/tokens/radius_token.dart -DA:12,1 -DA:24,52 -DA:26,1 -DA:30,4 -DA:33,1 +DA:19,0 +DA:22,3 +DA:25,2 +DA:28,3 +DA:31,3 DA:34,2 -DA:42,1 -DA:51,1 -DA:53,0 -DA:57,0 +DA:37,3 +DA:40,3 +DA:43,3 +LF:12 +LH:11 +end_of_record +SF:lib/src/helpers/deep_collection_equality.dart +DA:2,65 +DA:3,5 +DA:4,15 +DA:5,8 +DA:6,16 +DA:14,6 +DA:16,18 +DA:18,12 +DA:19,18 +DA:20,18 +DA:31,1 +DA:32,3 +DA:33,2 +DA:34,1 +DA:42,13 +DA:44,33 +DA:46,10 +DA:47,5 +DA:48,6 +DA:49,1 +DA:50,6 +DA:51,6 +DA:54,6 +DA:57,1 +DA:58,1 +DA:59,3 DA:60,2 -DA:62,2 -DA:64,2 -DA:66,3 -LF:14 -LH:12 +DA:61,1 +DA:62,1 +DA:63,1 +DA:65,2 +DA:66,2 +DA:72,1 +LF:33 +LH:33 +end_of_record +SF:lib/src/helpers/lerp_helpers.dart +DA:15,1 +DA:16,5 +DA:33,1 +DA:34,1 +LF:4 +LH:4 end_of_record diff --git a/demo/ios/Flutter/AppFrameworkInfo.plist b/demo/ios/Flutter/AppFrameworkInfo.plist index 8d4492f97..9625e105d 100644 --- a/demo/ios/Flutter/AppFrameworkInfo.plist +++ b/demo/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/demo/ios/Podfile b/demo/ios/Podfile index 1e8c3c90a..88359b225 100644 --- a/demo/ios/Podfile +++ b/demo/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +# platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/demo/ios/Podfile.lock b/demo/ios/Podfile.lock new file mode 100644 index 000000000..6d8ea0488 --- /dev/null +++ b/demo/ios/Podfile.lock @@ -0,0 +1,29 @@ +PODS: + - Flutter (1.0.0) + - flutter_native_splash (0.0.1): + - Flutter + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + +DEPENDENCIES: + - Flutter (from `Flutter`) + - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + flutter_native_splash: + :path: ".symlinks/plugins/flutter_native_splash/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" + +SPEC CHECKSUMS: + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 + +PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 + +COCOAPODS: 1.13.0 diff --git a/demo/ios/Runner.xcodeproj/project.pbxproj b/demo/ios/Runner.xcodeproj/project.pbxproj index b4fc9d712..c22627cd4 100644 --- a/demo/ios/Runner.xcodeproj/project.pbxproj +++ b/demo/ios/Runner.xcodeproj/project.pbxproj @@ -3,10 +3,11 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ + 0B65E10B22F7CE092B033F2E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BBE22BD976632970E6EA4E49 /* Pods_Runner.framework */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; @@ -31,7 +32,10 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 245B7EE95F1947A359F9FA42 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 5FA7C96CD4D4DBD8E1D3D0C0 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 705EDF8A0CFF2CCD15173FDB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -42,6 +46,7 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BBE22BD976632970E6EA4E49 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -49,12 +54,32 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 0B65E10B22F7CE092B033F2E /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 3BB17147C6EECAADF99096C7 /* Frameworks */ = { + isa = PBXGroup; + children = ( + BBE22BD976632970E6EA4E49 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 972336DC1E1835B3587BEDAD /* Pods */ = { + isa = PBXGroup; + children = ( + 5FA7C96CD4D4DBD8E1D3D0C0 /* Pods-Runner.debug.xcconfig */, + 245B7EE95F1947A359F9FA42 /* Pods-Runner.release.xcconfig */, + 705EDF8A0CFF2CCD15173FDB /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -72,6 +97,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + 972336DC1E1835B3587BEDAD /* Pods */, + 3BB17147C6EECAADF99096C7 /* Frameworks */, ); sourceTree = ""; }; @@ -105,12 +132,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 90CA4FB9D6F73B4DBDC6EB0C /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + A2DB3B311AF2DA5240EF0621 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -127,7 +156,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -171,10 +200,12 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -183,8 +214,31 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 90CA4FB9D6F73B4DBDC6EB0C /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -197,6 +251,23 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + A2DB3B311AF2DA5240EF0621 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -272,7 +343,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -290,7 +361,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.example.demo; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -346,7 +420,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -395,7 +469,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -414,7 +488,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.example.demo; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -433,7 +510,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.example.demo; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -468,4 +548,4 @@ /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; -} \ No newline at end of file +} diff --git a/demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140cfd..b52b2e698 100644 --- a/demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ + + diff --git a/demo/ios/Runner/Info.plist b/demo/ios/Runner/Info.plist index bba37af52..475066888 100644 --- a/demo/ios/Runner/Info.plist +++ b/demo/ios/Runner/Info.plist @@ -43,5 +43,9 @@ UIStatusBarHidden - - \ No newline at end of file + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/demo/lib/components/examples/box/box.mix.dart b/demo/lib/components/examples/box/box.mix.dart index 1efc8c8bb..acb8c762b 100644 --- a/demo/lib/components/examples/box/box.mix.dart +++ b/demo/lib/components/examples/box/box.mix.dart @@ -2,15 +2,15 @@ import 'package:mix/mix.dart'; StyleMix get button { return StyleMix( - textStyle(as: MaterialTextStyles.bodyMedium), - bold(), - textStyle(fontSize: 16.0), - backgroundColor($M3Color.primary), + text.style.as($textStyles.bodyMedium), + text.style.bold(), + text.style(fontSize: 16.0), + backgroundColor($colors.primary), onHover( - backgroundColor($M3Color.secondary), + backgroundColor($colors.secondary), ), - paddingHorizontal(15.0), - paddingVertical(8.0), - rounded(5), + padding.horizontal(15.0), + padding.vertical(8.0), + borderRadius(5), ); } diff --git a/demo/lib/components/m2_typography.dart b/demo/lib/components/m2_typography.dart deleted file mode 100644 index c9e4773b5..000000000 --- a/demo/lib/components/m2_typography.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:mix/mix.dart'; - -class M2TokensTypographyExampleTile extends HookWidget { - const M2TokensTypographyExampleTile({super.key}); - @override - Widget build(BuildContext context) { - final onSurfaceMix = StyleMix( - textStyle(color: Colors.black), - onDark( - textStyle(color: Colors.white), - ), - ); - - final headingMix = StyleMix.create([ - textStyle(fontSize: 22), - ...onSurfaceMix.values, - ]); - - return ExpansionTile( - expandedCrossAxisAlignment: CrossAxisAlignment.start, - expandedAlignment: Alignment.topLeft, - title: StyledText( - "Material 2 Typography", - style: headingMix, - ), - tilePadding: const EdgeInsets.all(0), - children: [ - ...$M2Text.tokens - .map( - (token, style) => MapEntry( - token, - StyledText( - "This is ${token.name}.", - style: onSurfaceMix.merge( - StyleMix( - textStyle(as: token), - ), - ), - ), - ), - ) - .values - .toList(), - ], - ); - } -} diff --git a/demo/lib/components/m3_typography.dart b/demo/lib/components/m3_typography.dart deleted file mode 100644 index e4143e361..000000000 --- a/demo/lib/components/m3_typography.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:mix/mix.dart'; - -class M3TokensTypographyExampleTile extends HookWidget { - const M3TokensTypographyExampleTile({super.key}); - @override - Widget build(BuildContext context) { - final onSurfaceMix = StyleMix( - textStyle(color: Colors.black), - onDark( - textStyle(color: Colors.white), - ), - ); - - final headingMix = StyleMix.create([ - textStyle(fontSize: 22), - ...onSurfaceMix.values, - ]); - - return ExpansionTile( - expandedCrossAxisAlignment: CrossAxisAlignment.start, - expandedAlignment: Alignment.topLeft, - title: StyledText( - "Material 3 Typography", - style: headingMix, - ), - tilePadding: const EdgeInsets.all(0), - children: [ - ...MaterialTextStyles.tokens - .map( - (token, style) => MapEntry( - token, - StyledText( - "This is ${token.name}.", - style: onSurfaceMix.merge( - StyleMix( - textStyle(as: token), - ), - ), - ), - ), - ) - .values - .toList(), - ], - ); - } -} diff --git a/demo/lib/docs/variants/catalog/pressable.dart b/demo/lib/docs/variants/catalog/pressable.dart index c505cda0c..67c3664a4 100644 --- a/demo/lib/docs/variants/catalog/pressable.dart +++ b/demo/lib/docs/variants/catalog/pressable.dart @@ -10,7 +10,7 @@ class VariantsCatalogPressable extends StatelessWidget { buildBlock( 'Hover', StyleMix(onHover( - border(color: $M3Color.primary, width: 2), + border(color: $colors.primary, width: 2), padding(4.0), )), const Text('Hover this to show the highlight'), @@ -19,7 +19,7 @@ class VariantsCatalogPressable extends StatelessWidget { buildBlock( 'Focus', StyleMix(onFocus( - border(color: $M3Color.primary, width: 2), + border(color: $colors.primary, width: 2), padding(4.0), )), const Text('Focus this to show the highlight'), @@ -27,10 +27,15 @@ class VariantsCatalogPressable extends StatelessWidget { const VerticalDivider(), buildBlock( 'Press', - StyleMix(onPress( - border(color: $M3Color.primary, width: 2), - padding(4.0), - )), + StyleMix( + onPress( + border( + color: $colors.primary, + width: 2, + ), + padding(4.0), + ), + ), const Text('Press this to show the highlight'), ), ]); diff --git a/demo/lib/docs/variants/default.dart b/demo/lib/docs/variants/default.dart index a4541177e..bd027de63 100644 --- a/demo/lib/docs/variants/default.dart +++ b/demo/lib/docs/variants/default.dart @@ -7,11 +7,11 @@ class VariantsDefaultExample extends StatelessWidget { @override Widget build(BuildContext context) { final style = StyleMix( - backgroundColor($M3Color.secondary), - textStyle(color: $M3Color.onSecondary), + backgroundColor($colors.secondary), + textStyle(color: $colors.onSecondary), onHover( - backgroundColor($M3Color.primary), - textStyle(color: $M3Color.onPrimary), + backgroundColor($colors.primary), + textStyle(color: $colors.onPrimary), ), ); diff --git a/demo/lib/styles.dart b/demo/lib/styles.dart index 846f4f698..4388c6ff3 100644 --- a/demo/lib/styles.dart +++ b/demo/lib/styles.dart @@ -4,20 +4,20 @@ import 'package:mix/mix.dart'; // Making this a getter so that it works with hot reload StyleMix get onSurfaceMix => StyleMix( - textStyle(color: Colors.black), + text.style(color: Colors.black), onDark( - textStyle(color: Colors.white), + text.style(color: Colors.white), ), ); StyleMix get headingMix => StyleMix.create([ - textStyle(fontSize: 24), + text.style(fontSize: 24), ...onSurfaceMix.values, ]); StyleMix get flexAlign => StyleMix( - mainAxisAlignment(MainAxisAlignment.start), - crossAxis(CrossAxisAlignment.start), - mainAxisSize(MainAxisSize.max), + flex.mainAxisAlignment.start(), + flex.crossAxisAlignment.start(), + flex.mainAxisSize.max(), width(double.infinity), ); diff --git a/demo/lib/views/basic_example.dart b/demo/lib/views/basic_example.dart index fd1d891ea..8862e106a 100644 --- a/demo/lib/views/basic_example.dart +++ b/demo/lib/views/basic_example.dart @@ -12,13 +12,12 @@ class BasicExample extends HookWidget { final mix = StyleMix.create([ height(300), width(300), - rounded(10), - animation(), + borderRadius(10), elevation(2), margin(10), - alignmentCenter(), + alignment.center(), backgroundColor(Colors.purple), - textStyle(color: Colors.white), + text.style(color: Colors.white), onPress( backgroundColor(Colors.black), ), @@ -61,7 +60,7 @@ class BasicExample extends HookWidget { "This is another StyledText, but yet another a different mix!", style: onSurfaceMix.merge( StyleMix( - textStyle( + text.style( fontSize: 18, fontWeight: FontWeight.bold, fontStyle: FontStyle.italic, @@ -74,10 +73,10 @@ class BasicExample extends HookWidget { style: onSurfaceMix.merge( StyleMix( onLight( - textStyle(color: $M3Color.error), + text.style(color: $colors.error), ), onDark( - textStyle(color: $M3Color.primary), + text.style(color: $colors.primary), ), ), ), @@ -95,7 +94,7 @@ class BasicExample extends HookWidget { Icons.move_to_inbox, style: onSurfaceMix.merge( StyleMix( - iconSize(50), + icon(size: 50), ), ), ), @@ -103,7 +102,7 @@ class BasicExample extends HookWidget { Icons.one_k, style: onSurfaceMix.merge( StyleMix( - iconSize(60), + icon(size: 60), ), ), ), @@ -111,8 +110,8 @@ class BasicExample extends HookWidget { Icons.waving_hand_rounded, style: onSurfaceMix.merge( StyleMix( - iconSize(70), - iconColor($M3Color.secondary), + icon(size: 70), + icon(color: $colors.secondary), ), ), ), @@ -120,8 +119,8 @@ class BasicExample extends HookWidget { Icons.warning_amber, style: onSurfaceMix.merge( StyleMix( - iconSize(90), - iconColor(Colors.yellow.shade900), + icon(size: 90), + icon(color: Colors.yellow.shade900), ), ), ), diff --git a/demo/lib/views/button_example.dart b/demo/lib/views/button_example.dart index baec88cef..eebf85ac3 100644 --- a/demo/lib/views/button_example.dart +++ b/demo/lib/views/button_example.dart @@ -22,45 +22,41 @@ class ButtonSizeVariants { } StyleMix get _baseStyle => StyleMix( - rounded(4), - animation( - curve: Curves.easeIn, - duration: 100, - ), + borderRadius(4), onPress( scale(0.95), ), - mainAxisAlignment(MainAxisAlignment.center), - textStyle( + flex.mainAxisAlignment.center(), + text.style( // added because of lack of style parameters (yellow lines) decoration: TextDecoration.none, fontWeight: FontWeight.w600, - fontFamily: MaterialTextStyles.bodySmall.fontFamily, + fontFamily: $textStyles.bodySmall.fontFamily, ), - mainAxisSize(MainAxisSize.min), // For flexbox + flex.mainAxisSize.min(), ButtonSizeVariants.small( - paddingHorizontal(10), - paddingVertical(10), - textStyle( + padding.horizontal(10), + padding.vertical(10), + text.style( fontSize: 16, ), - iconSize(24), + icon(size: 24), ), ButtonSizeVariants.medium( - paddingHorizontal(4), - paddingVertical(16), - textStyle( + padding.horizontal(4), + padding.vertical(16), + text.style( fontSize: 16, ), - iconSize(24), + icon(size: 24), ), ButtonSizeVariants.large( - paddingHorizontal(4), - paddingVertical(2), - textStyle( + padding.horizontal(4), + padding.vertical(2), + text.style( fontSize: 16, ), - iconSize(24), + icon(size: 24), ), ); @@ -144,15 +140,15 @@ abstract class Button extends StatelessWidget { } StyleMix get _style => StyleMix( - textStyle( + text.style( color: const Color(0xFFFF004C), ), backgroundColor(const Color(0x0F07E2FF)), - iconColor(_MaterialDesignColors.onBackground), + icon(color: $colors.onBackground), onDisabled( - backgroundColor(_MaterialDesignColors.background.withOpacity(0.3)), - textStyle(color: _MaterialDesignColors.onBackground.withOpacity(0.3)), - iconColor(_MaterialDesignColors.onBackground.withOpacity(0.3)), + backgroundColor($colors.background.withOpacity(0.3)), + text.style(color: $colors.onBackground.withOpacity(0.3)), + icon(color: $colors.onBackground.withOpacity(0.3)), ), ); diff --git a/demo/lib/views/example.dart b/demo/lib/views/example.dart index ad86b78ba..1046df6cd 100644 --- a/demo/lib/views/example.dart +++ b/demo/lib/views/example.dart @@ -62,18 +62,17 @@ class CustomMixWidget extends StatelessWidget { Widget build(BuildContext context) { final style = StyleMix( height(100), - animation(), - marginVertical(10), + margin.vertical(10), elevation(10), - rounded(10), - backgroundColor($M3Color.primary), - textStyle(as: MaterialTextStyles.bodyMedium), - textStyle(color: $M3Color.onPrimary), + borderRadius(10), + backgroundColor($colors.primary), + text.style.as($textStyles.bodyMedium), + text.style(color: $colors.onPrimary), onHover( elevation(2), padding(20), - backgroundColor($M3Color.secondary), - textStyle(color: $M3Color.onSecondary), + backgroundColor($colors.secondary), + text.style(color: $colors.onSecondary), ), ); diff --git a/demo/lib/views/layout_example.dart b/demo/lib/views/layout_example.dart index ab9fc38c8..e4126383f 100644 --- a/demo/lib/views/layout_example.dart +++ b/demo/lib/views/layout_example.dart @@ -6,11 +6,11 @@ import '../styles.dart'; StyleMix get mix => StyleMix( height(300), width(300), - rounded(10), + borderRadius(10), elevation(2), - backgroundColor($M3Color.surface), - alignmentCenter(), - textStyle(color: $M3Color.onSurface), + backgroundColor($colors.surface), + alignment.center(), + text.style(color: $colors.onSurface), ); class LayoutExample extends StatelessWidget { diff --git a/demo/lib/views/typography_example.dart b/demo/lib/views/typography_example.dart index f1a9c9dff..3f7b4d7b5 100644 --- a/demo/lib/views/typography_example.dart +++ b/demo/lib/views/typography_example.dart @@ -1,20 +1,18 @@ import 'package:flutter/material.dart'; import 'package:mix/mix.dart'; -import '../components/m2_typography.dart'; -import '../components/m3_typography.dart'; import '../styles.dart'; StyleMix get button => StyleMix( - textStyle(as: MaterialTextStyles.bodyMedium), - bold(), - textStyle(fontSize: 6.0), - backgroundColor($M3Color.primary), + text.style.as($textStyles.bodyMedium), + padding.top(10), + text.style.bold(), + text.style(fontSize: 6.0), + backgroundColor($colors.primary), onHover( - backgroundColor($M3Color.secondary), + backgroundColor($colors.secondary), ), - paddingHorizontal(15.0), - paddingVertical(8.0), + padding(8, 15), ); class TypographyExample extends StatelessWidget { @@ -34,30 +32,28 @@ class TypographyExample extends StatelessWidget { style: headingMix, ), const SizedBox(height: 20), - const M3TokensTypographyExampleTile(), - const M2TokensTypographyExampleTile(), const SizedBox(height: 20), StyledText( "This is a StyledText with a custom textStyle!", style: headingMix.merge( StyleMix( textStyle( - color: $M3Color.surface, + color: $colors.surface, fontWeight: FontWeight.bold, fontStyle: FontStyle.italic, letterSpacing: 2, wordSpacing: 2, height: 1.5, shadows: [ - const Shadow( - color: $M3Color.secondary, - offset: Offset(2, 2), + Shadow( + color: $colors.secondary, + offset: const Offset(2, 2), blurRadius: 2, ), ], ), onDark( - textStyle(color: $M3Color.surface), + textStyle(color: $colors.surface), ), ), ), diff --git a/demo/lib/views/variants.dart b/demo/lib/views/variants.dart index c94bb1a5d..144d08e23 100644 --- a/demo/lib/views/variants.dart +++ b/demo/lib/views/variants.dart @@ -12,27 +12,27 @@ class VariantsExample extends HookWidget { final baseStyle = StyleMix( height(300), width(300), - rounded(10), + borderRadius(10), elevation(2), margin(10), - alignmentCenter(), + alignment.center(), ); final style = StyleMix( - backgroundColor($M3Color.primary), - textStyle(color: $M3Color.onPrimary), + backgroundColor($colors.primary), + text.style(color: $colors.onPrimary), onHover( - backgroundColor($M3Color.secondary), - textStyle(color: $M3Color.onPrimary), + backgroundColor($colors.secondary), + text.style(color: $colors.onPrimary), ), ).merge(baseStyle); final onDarkStyle = StyleMix( - backgroundColor($M3Color.primary), - textStyle(color: $M3Color.onPrimary), + backgroundColor($colors.primary), + text.style(color: $colors.onPrimary), onDark( backgroundColor(Colors.red), - textStyle(color: $M3Color.onPrimary), + text.style(color: $colors.onPrimary), ), ).merge(baseStyle); diff --git a/demo/macos/Podfile.lock b/demo/macos/Podfile.lock index 689c202d5..23ce6d555 100644 --- a/demo/macos/Podfile.lock +++ b/demo/macos/Podfile.lock @@ -26,4 +26,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 -COCOAPODS: 1.12.1 +COCOAPODS: 1.13.0 diff --git a/lib/exports.dart b/lib/exports.dart index 60755ac2f..2576490e3 100644 --- a/lib/exports.dart +++ b/lib/exports.dart @@ -1,69 +1,90 @@ -export 'mix.dart'; -export 'src/attributes/alignment_attribute.dart'; -export 'src/attributes/attribute.dart'; export 'src/attributes/border/border_attribute.dart'; +export 'src/attributes/border/border_dto.dart'; export 'src/attributes/border/border_radius_attribute.dart'; -export 'src/attributes/color_attribute.dart'; -export 'src/attributes/constraints_attribute.dart'; -export 'src/attributes/decoration_attribute.dart'; -export 'src/attributes/edge_insets_attribute.dart'; -export 'src/attributes/scalar_attribute.dart'; -export 'src/attributes/shadow_attribute.dart'; -export 'src/attributes/space_attribute.dart'; -export 'src/attributes/strut_style_attribute.dart'; +export 'src/attributes/border/border_radius_dto.dart'; +export 'src/attributes/border/border_radius_util.dart'; +export 'src/attributes/border/border_util.dart'; +export 'src/attributes/color/color_dto.dart'; +export 'src/attributes/constraints/constraints_attribute.dart'; +export 'src/attributes/constraints/constraints_util.dart'; +export 'src/attributes/decoration/decoration_attribute.dart'; +export 'src/attributes/decoration/decoration_util.dart'; +export 'src/attributes/gradient/gradient_attribute.dart'; +export 'src/attributes/gradient/gradient_util.dart'; +export 'src/attributes/scalars/scalar_util.dart'; +export 'src/attributes/scalars/scalars_attribute.dart'; +export 'src/attributes/shadow/shadow_dto.dart'; +export 'src/attributes/shadow/shadow_util.dart'; +export 'src/attributes/spacing/edge_insets_dto.dart'; +export 'src/attributes/spacing/spacing_attribute.dart'; +export 'src/attributes/spacing/spacing_util.dart'; +export 'src/attributes/strut_style/strut_style_attribute.dart'; export 'src/attributes/style_mix_attribute.dart'; -export 'src/attributes/text_style_attribute.dart'; +export 'src/attributes/text_directives_util.dart'; +export 'src/attributes/text_style/text_style_attribute.dart'; +export 'src/attributes/text_style/text_style_util.dart'; export 'src/attributes/variant_attribute.dart'; +export 'src/core/attribute.dart'; +export 'src/core/attributes_map.dart'; +export 'src/core/directive.dart'; +export 'src/core/extensions/iterable_ext.dart'; +export 'src/core/extensions/values_ext.dart'; +export 'src/decorators/clip_decorator.dart'; export 'src/decorators/decorator.dart'; export 'src/decorators/default_decorators.dart'; export 'src/decorators/widget_decorator_wrapper.dart'; export 'src/deprecations.dart'; -export 'src/directives/directive_attribute.dart'; -export 'src/directives/directives/controllers.dart'; -export 'src/directives/text_directive.dart'; export 'src/factory/mix_provider.dart'; export 'src/factory/mix_provider_data.dart'; export 'src/factory/style_group.dart'; export 'src/factory/style_mix.dart'; -export 'src/helpers/extensions/build_context_ext.dart'; -export 'src/helpers/extensions/iterable_ext.dart'; -export 'src/helpers/extensions/string_ext.dart'; -export 'src/helpers/extensions/style_mix_ext.dart'; -export 'src/helpers/extensions/values_ext.dart'; -export 'src/specs/container_spec.dart'; -export 'src/specs/flex_spec.dart'; -export 'src/specs/icon_spec.dart'; -export 'src/specs/image_spec.dart'; -export 'src/specs/stack_spec.dart'; -export 'src/specs/text_spec.dart'; +export 'src/factory/style_mix_ext.dart'; +export 'src/recipes/container/container_attribute.dart'; +export 'src/recipes/container/container_spec.dart'; +export 'src/recipes/container/container_util.dart'; +export 'src/recipes/container/container_widget.dart'; +export 'src/recipes/flex/flex_attribute.dart'; +export 'src/recipes/flex/flex_spec.dart'; +export 'src/recipes/flex/flex_util.dart'; +export 'src/recipes/flex/flex_widget.dart'; +export 'src/recipes/icon/icon_attribute.dart'; +export 'src/recipes/icon/icon_spec.dart'; +export 'src/recipes/icon/icon_util.dart'; +export 'src/recipes/icon/icon_widget.dart'; +export 'src/recipes/image/image_attribute.dart'; +export 'src/recipes/image/image_spec.dart'; +export 'src/recipes/image/image_util.dart'; +export 'src/recipes/stack/stack_attribute.dart'; +export 'src/recipes/stack/stack_spec.dart'; +export 'src/recipes/stack/stack_util.dart'; +export 'src/recipes/stack/stack_widget.dart'; +export 'src/recipes/text/text_attribute.dart'; +export 'src/recipes/text/text_spec.dart'; +export 'src/recipes/text/text_util.dart'; +export 'src/recipes/text/text_widget.dart'; export 'src/theme/mix_theme.dart'; export 'src/theme/tokens/breakpoints.dart'; +export 'src/theme/tokens/color_token.dart'; export 'src/theme/tokens/material_tokens.dart'; export 'src/theme/tokens/mix_token.dart'; +export 'src/theme/tokens/radius_token.dart'; export 'src/theme/tokens/space_token.dart'; export 'src/theme/tokens/text_style_token.dart'; -export 'src/utils/alignment_util.dart'; -export 'src/utils/border_radius_util.dart'; -export 'src/utils/border_util.dart'; -export 'src/utils/box_constraints_util.dart'; -export 'src/utils/context_variant_util.dart'; -export 'src/utils/decoration_util.dart'; -export 'src/utils/gradient_util.dart'; +export 'src/theme/tokens/token_util.dart'; +export 'src/utils/context_variant_util/on_breakpoint_util.dart'; +export 'src/utils/context_variant_util/on_brightness_util.dart'; +export 'src/utils/context_variant_util/on_directionality_util.dart'; +export 'src/utils/context_variant_util/on_helper_util.dart'; +export 'src/utils/context_variant_util/on_orientation_util.dart'; +export 'src/utils/decorators_util.dart'; export 'src/utils/helper_util.dart'; export 'src/utils/pressable_util.dart'; -export 'src/utils/scalar_util.dart'; -export 'src/utils/space_util.dart'; -export 'src/utils/text_directives_util.dart'; -export 'src/utils/text_util.dart'; export 'src/variants/context_variant.dart'; +export 'src/variants/multi_variant.dart'; export 'src/variants/variant.dart'; -export 'src/widgets/container_widget.dart'; export 'src/widgets/empty_widget.dart'; -export 'src/widgets/flex_widget.dart'; -export 'src/widgets/icon_widget.dart'; +export 'src/widgets/gap_widget.dart'; export 'src/widgets/pressable/pressable.notifier.dart'; export 'src/widgets/pressable/pressable_state.dart'; export 'src/widgets/pressable/pressable_widget.dart'; -export 'src/widgets/stack_widget.dart'; export 'src/widgets/styled_widget.dart'; -export 'src/widgets/text_widget.dart'; diff --git a/lib/src/attributes/alignment_attribute.dart b/lib/src/attributes/alignment_attribute.dart deleted file mode 100644 index 363442330..000000000 --- a/lib/src/attributes/alignment_attribute.dart +++ /dev/null @@ -1,123 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../factory/mix_provider_data.dart'; -import 'attribute.dart'; - -@immutable -abstract class AlignmentGeometryAttribute - extends VisualAttribute { - final double? start; - final double? x; - final double? y; - - const AlignmentGeometryAttribute({this.start, this.x, this.y}); - - @override - AlignmentGeometryAttribute merge( - covariant AlignmentGeometryAttribute? other, - ); - - @override - T resolve(MixData mix); - - @override - get props => [start, x, y]; -} - -@immutable -class AlignmentAttribute extends AlignmentGeometryAttribute { - /// The top left corner. - static const topLeft = AlignmentAttribute.pos(-1.0, -1.0); - - /// The center point along the top edge. - static const topCenter = AlignmentAttribute.pos(0.0, -1.0); - - /// The top right corner. - static const topRight = AlignmentAttribute.pos(1.0, -1.0); - - /// The center point along the left edge. - static const centerLeft = AlignmentAttribute.pos(-1.0, 0.0); - - /// The center point, both horizontally and vertically. - static const center = AlignmentAttribute.pos(0.0, 0.0); - - /// The center point along the right edge. - static const centerRight = AlignmentAttribute.pos(1.0, 0.0); - - /// The bottom left corner. - static const bottomLeft = AlignmentAttribute.pos(-1.0, 1.0); - - /// The center point along the bottom edge. - static const bottomCenter = AlignmentAttribute.pos(0.0, 1.0); - - /// The bottom right corner. - static const bottomRight = AlignmentAttribute.pos(1.0, 1.0); - - const AlignmentAttribute({super.x, super.y}); - - const AlignmentAttribute.pos(double x, double y) : this(x: x, y: y); - - @override - AlignmentAttribute merge(AlignmentAttribute? other) { - return AlignmentAttribute(x: other?.x ?? x, y: other?.y ?? y); - } - - @override - Alignment resolve(MixData mix) => Alignment(x ?? 0, y ?? 0); -} - -@immutable -class AlignmentDirectionalAttribute - extends AlignmentGeometryAttribute { - /// The top corner on the "start" side. - static const topStart = AlignmentDirectionalAttribute.pos(-1.0, -1.0); - - /// The center point along the top edge. - /// - /// Consider using [Alignment.topCenter] instead, as it does not need - /// to be [resolve]d to be used. - static const topCenter = AlignmentDirectionalAttribute.pos(0.0, -1.0); - - /// The top corner on the "end" side. - static const topEnd = AlignmentDirectionalAttribute.pos(1.0, -1.0); - - /// The center point along the "start" edge. - static const centerStart = AlignmentDirectionalAttribute.pos(-1.0, 0.0); - - /// The center point, both horizontally and vertically. - /// - /// Consider using [Alignment.center] instead, as it does not need to - /// be [resolve]d to be used. - static const center = AlignmentDirectionalAttribute.pos(0.0, 0.0); - - /// The center point along the "end" edge. - static const centerEnd = AlignmentDirectionalAttribute.pos(1.0, 0.0); - - /// The bottom corner on the "start" side. - static const bottomStart = AlignmentDirectionalAttribute.pos(-1.0, 1.0); - - /// The center point along the bottom edge. - /// - /// Consider using [Alignment.bottomCenter] instead, as it does not - /// need to be [resolve]d to be used. - static const bottomCenter = AlignmentDirectionalAttribute.pos(0.0, 1.0); - - /// The bottom corner on the "end" side. - static const bottomEnd = AlignmentDirectionalAttribute.pos(1.0, 1.0); - - const AlignmentDirectionalAttribute({super.start, super.y}); - - const AlignmentDirectionalAttribute.pos(double start, double y) - : this(start: start, y: y); - @override - AlignmentDirectionalAttribute merge(AlignmentDirectionalAttribute? other) { - return AlignmentDirectionalAttribute( - start: other?.start ?? start, - y: other?.y ?? y, - ); - } - - @override - AlignmentDirectional resolve(MixData mix) => - AlignmentDirectional(start ?? 0, y ?? 0); -} diff --git a/lib/src/attributes/attribute.dart b/lib/src/attributes/attribute.dart deleted file mode 100644 index 2a8d2dcf1..000000000 --- a/lib/src/attributes/attribute.dart +++ /dev/null @@ -1,109 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -import '../core/equality/compare_mixin.dart'; -import '../factory/mix_provider_data.dart'; - -@immutable -abstract class Attribute with Comparable, Mergeable { - const Attribute(); -} - -mixin Resolvable { - T resolve(MixData mix); -} - -mixin Mergeable { - T merge(covariant T? other); - - List mergeMergeableList( - List? current, - List? other, - ) { - if (current == null && other == null) return []; - if (current == null) return other ?? []; - if (other == null) return current; - - if (current.isEmpty) return other; - - final listLength = current.length; - final otherLength = other.length; - final maxLength = max(listLength, otherLength); - - return List.generate(maxLength, (int index) { - if (index < listLength && index < otherLength) { - final currentValue = current[index]; - final otherValue = other[index]; - - return currentValue.merge(otherValue); - } else if (index < listLength) { - return current[index]; - } - - return other[index]; - }); - } -} - -@immutable -abstract class ScalarAttribute, R> - extends VisualAttribute { - final R value; - - const ScalarAttribute(this.value); - - // Factory for merge methods - // ignore: avoid-shadowing - T create(R value); - - @override - R resolve(MixData mix) { - return value is Resolvable ? (value as Resolvable).resolve(mix) : value; - } - - @override - T merge(T? other) { - if (other == null) return create(value); - - return value is Mergeable - ? create((value as Mergeable).merge(other.value)) - : create(other.value); - } - - @override - get props => [value]; -} - -@immutable -abstract class VisualAttribute extends Attribute with Resolvable { - const VisualAttribute(); -} - -@immutable -abstract class MixExtension> extends ThemeExtension - with Comparable { - const MixExtension(); - - Duration lerpDuration(Duration a, Duration b, double t) { - int lerpTicks = ((1 - t) * a.inMilliseconds + t * b.inMilliseconds).round(); - - return Duration(milliseconds: lerpTicks); - } - - int lerpInt(int a, int b, double t) { - return ((1 - t) * a + t * b).round(); - } - - N? genericNumLerp(N? a, N? b, double t) { - if (a == null && b == null) return null; - if (a == null) return b; - if (b == null) return a; - - return (a * (1 - t) + b * t) as N; - } - - P snap

(P from, P to, double t) { - return t < 0.5 ? from : to; - } -} diff --git a/lib/src/attributes/border/border_attribute.dart b/lib/src/attributes/border/border_attribute.dart index 90a4ea18a..4fade6f73 100644 --- a/lib/src/attributes/border/border_attribute.dart +++ b/lib/src/attributes/border/border_attribute.dart @@ -1,189 +1,34 @@ import 'package:flutter/material.dart'; +import '../../core/attribute.dart'; import '../../factory/mix_provider_data.dart'; -import '../attribute.dart'; -import '../color_attribute.dart'; +import 'border_dto.dart'; @immutable -abstract class BoxBorderAttribute - extends VisualAttribute { - final BorderSideAttribute? top; +class BoxBorderAttribute + extends DtoAttribute { + const BoxBorderAttribute(super.value); - final BorderSideAttribute? bottom; + BorderSideDto? get top => value.top; - const BoxBorderAttribute({this.top, this.bottom}); + BorderSideDto? get bottom => value.bottom; - @override - BoxBorderAttribute merge(covariant BoxBorderAttribute? other); - - @override - T resolve(MixData mix); - - @override - get props => [top, bottom]; -} + BorderSideDto? get left => value.left; -class BorderAttribute extends BoxBorderAttribute { - final BorderSideAttribute? left; - final BorderSideAttribute? right; + BorderSideDto? get right => value.right; - const BorderAttribute({this.left, this.right, super.top, super.bottom}); + BorderSideDto? get start => value.start; - const BorderAttribute.all(BorderSideAttribute side) - : this(left: side, right: side, top: side, bottom: side); - - const BorderAttribute.symmetric({ - BorderSideAttribute? vertical, - BorderSideAttribute? horizontal, - }) : this( - left: vertical, - right: vertical, - top: horizontal, - bottom: horizontal, - ); - - @override - BorderAttribute merge(BoxBorderAttribute? other) { - if (other == null) return this; - final otherAttribute = other as BorderAttribute; // cast the other instance - - return BorderAttribute( - left: left?.merge(otherAttribute.left) ?? otherAttribute.left, - right: right?.merge(otherAttribute.right) ?? otherAttribute.right, - top: top?.merge(other.top) ?? other.top, - bottom: bottom?.merge(other.bottom) ?? other.bottom, - ); - } + BorderSideDto? get end => value.end; @override - Border resolve(MixData mix) { - return Border( - top: top?.resolve(mix) ?? BorderSide.none, - right: right?.resolve(mix) ?? BorderSide.none, - bottom: bottom?.resolve(mix) ?? BorderSide.none, - left: left?.resolve(mix) ?? BorderSide.none, - ); + BoxBorderAttribute merge(BoxBorderAttribute? other) { + return other == null ? this : BoxBorderAttribute(value.merge(other.value)); } @override - List get props => [top, bottom, left, right]; -} - -class BorderDirectionalAttribute extends BoxBorderAttribute { - final BorderSideAttribute? start; - final BorderSideAttribute? end; - - const BorderDirectionalAttribute({ - this.start, - this.end, - super.top, - super.bottom, - }); - - const BorderDirectionalAttribute.all(BorderSideAttribute side) - : this(start: side, end: side, top: side, bottom: side); - - const BorderDirectionalAttribute.symmetric({ - BorderSideAttribute? vertical, - BorderSideAttribute? horizontal, - }) : this( - start: horizontal, - end: horizontal, - top: vertical, - bottom: vertical, - ); - - BorderAttribute toBorder() { - return BorderAttribute( - left: start, - right: end, - top: top, - bottom: bottom, - ); - } - - @override - BorderDirectionalAttribute merge( - BoxBorderAttribute? other, - ) { - if (other == null) return this; - final otherAttribute = - other as BorderDirectionalAttribute; // cast the other instance - - return BorderDirectionalAttribute( - start: start?.merge(otherAttribute.start) ?? otherAttribute.start, - end: end?.merge(otherAttribute.end) ?? otherAttribute.end, - top: top?.merge(other.top) ?? other.top, - bottom: bottom?.merge(other.bottom) ?? other.bottom, - ); - } - - @override - BorderDirectional resolve(MixData mix) { - return BorderDirectional( - top: top?.resolve(mix) ?? BorderSide.none, - start: start?.resolve(mix) ?? BorderSide.none, - end: end?.resolve(mix) ?? BorderSide.none, - bottom: bottom?.resolve(mix) ?? BorderSide.none, - ); - } - - @override - List get props => [top, bottom, start, end]; -} - -@immutable -class BorderSideAttribute extends VisualAttribute { - final ColorAttribute? color; - final double? width; - final BorderStyle? style; - final double? strokeAlign; - - const BorderSideAttribute({ - this.color, - this.strokeAlign, - this.style, - this.width, - }); - - BorderSideAttribute copyWith({ - ColorAttribute? color, - double? width, - BorderStyle? style, - double? strokeAlign, - }) { - return BorderSideAttribute( - color: color ?? this.color, - strokeAlign: strokeAlign ?? this.strokeAlign, - style: style ?? this.style, - width: width ?? this.width, - ); - } - - @override - BorderSideAttribute merge(BorderSideAttribute? other) { - if (other == null) return this; - - return BorderSideAttribute( - color: color?.merge(other.color) ?? other.color, - strokeAlign: other.strokeAlign ?? strokeAlign, - style: other.style ?? style, - width: other.width ?? width, - ); - } - - @override - BorderSide resolve(MixData mix) { - const defaultValue = BorderSide(); - - return BorderSide( - color: color?.resolve(mix) ?? defaultValue.color, - width: width ?? defaultValue.width, - style: style ?? defaultValue.style, - strokeAlign: strokeAlign ?? defaultValue.strokeAlign, - ); - } + BoxBorder resolve(MixData mix) => value.resolve(mix); @override - get props => [color, width, style, strokeAlign]; + List get props => [value]; } diff --git a/lib/src/attributes/border/border_dto.dart b/lib/src/attributes/border/border_dto.dart new file mode 100644 index 000000000..1e77661ce --- /dev/null +++ b/lib/src/attributes/border/border_dto.dart @@ -0,0 +1,189 @@ +// ignore_for_file: prefer-returning-conditional-expressions + +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; +import '../color/color_dto.dart'; + +@immutable +class BoxBorderDto extends Dto with Mergeable { + final BorderSideDto? top; + final BorderSideDto? bottom; + + final BorderSideDto? left; + final BorderSideDto? right; + + // Directional + final BorderSideDto? start; + final BorderSideDto? end; + + const BoxBorderDto({ + this.top, + this.bottom, + this.left, + this.right, + this.start, + this.end, + }); + + static BoxBorderDto from(BoxBorder border) { + if (border is Border) { + return BoxBorderDto( + top: BorderSideDto.from(border.top), + bottom: BorderSideDto.from(border.bottom), + left: BorderSideDto.from(border.left), + right: BorderSideDto.from(border.right), + ); + } + + if (border is BorderDirectional) { + return BoxBorderDto( + top: BorderSideDto.from(border.top), + bottom: BorderSideDto.from(border.bottom), + start: BorderSideDto.from(border.start), + end: BorderSideDto.from(border.end), + ); + } + + throw ArgumentError.value( + border, + 'border', + 'Border type is not supported', + ); + } + + static BoxBorderDto? maybeFrom(BoxBorder? border) { + return border == null ? null : from(border); + } + + bool get _hasStartOrEnd => start != null || end != null; + + bool get _hasLeftOrRight => left != null || right != null; + + bool get isDirectional => _hasStartOrEnd && !_hasLeftOrRight; + + Type? checkMergeType(BoxBorderDto other) { + if (_hasLeftOrRight && !other._hasStartOrEnd) { + return Border; + } + if (_hasStartOrEnd && !other._hasLeftOrRight) { + return BorderDirectional; + } + + return null; + } + + @override + BoxBorderDto merge(BoxBorderDto? other) { + if (other == null) return this; + final type = checkMergeType(other); + assert(type != null, 'Cannot merge Border with BoxBorderDirectional'); + if (type == Border) { + return BoxBorderDto( + top: other.top ?? top, + bottom: other.bottom ?? bottom, + left: other.left ?? left, + right: other.right ?? right, + ); + } + + if (type == BorderDirectional) { + return BoxBorderDto( + top: other.top ?? top, + bottom: other.bottom ?? bottom, + start: other.start ?? start, + end: other.end ?? end, + ); + } + + return other; + } + + @override + BoxBorder resolve(MixData mix) { + if (isDirectional) { + return BorderDirectional( + top: top?.resolve(mix) ?? BorderSide.none, + start: start?.resolve(mix) ?? BorderSide.none, + end: end?.resolve(mix) ?? BorderSide.none, + bottom: bottom?.resolve(mix) ?? BorderSide.none, + ); + } + + return Border( + top: top?.resolve(mix) ?? BorderSide.none, + right: right?.resolve(mix) ?? BorderSide.none, + bottom: bottom?.resolve(mix) ?? BorderSide.none, + left: left?.resolve(mix) ?? BorderSide.none, + ); + } + + @override + get props => [top, bottom, left, right, start, end]; +} + +@immutable +class BorderSideDto extends Dto with Mergeable { + final ColorDto? color; + final double? width; + final BorderStyle? style; + final double? strokeAlign; + + const BorderSideDto({ + this.color, + this.strokeAlign, + this.style, + this.width, + }); + + static BorderSideDto from(BorderSide side) { + return BorderSideDto( + color: ColorDto(side.color), + strokeAlign: side.strokeAlign, + style: side.style, + width: side.width, + ); + } + + BorderSideDto copyWith({ + ColorDto? color, + double? width, + BorderStyle? style, + double? strokeAlign, + }) { + return BorderSideDto( + color: color ?? this.color, + strokeAlign: strokeAlign ?? this.strokeAlign, + style: style ?? this.style, + width: width ?? this.width, + ); + } + + @override + BorderSideDto merge(BorderSideDto? other) { + if (other == null) return this; + + return BorderSideDto( + color: color?.merge(other.color) ?? other.color, + strokeAlign: other.strokeAlign ?? strokeAlign, + style: other.style ?? style, + width: other.width ?? width, + ); + } + + @override + BorderSide resolve(MixData mix) { + const defaultValue = BorderSide(); + + return BorderSide( + color: color?.resolve(mix) ?? defaultValue.color, + width: width ?? defaultValue.width, + style: style ?? defaultValue.style, + strokeAlign: strokeAlign ?? defaultValue.strokeAlign, + ); + } + + @override + get props => [color, width, style, strokeAlign]; +} diff --git a/lib/src/attributes/border/border_radius_attribute.dart b/lib/src/attributes/border/border_radius_attribute.dart index 196368a3d..1a0a87a59 100644 --- a/lib/src/attributes/border/border_radius_attribute.dart +++ b/lib/src/attributes/border/border_radius_attribute.dart @@ -1,256 +1,162 @@ import 'package:flutter/material.dart'; +import '../../core/attribute.dart'; import '../../factory/mix_provider_data.dart'; -import '../attribute.dart'; +import 'border_radius_dto.dart'; @immutable -abstract class BorderRadiusGeometryAttribute - extends VisualAttribute { - final Radius? topLeft; - final Radius? topRight; - final Radius? bottomLeft; - final Radius? bottomRight; +class BorderRadiusGeometryAttribute extends DtoAttribute< + BorderRadiusGeometryAttribute, + BorderRadiusGeometryDto, + BorderRadiusGeometry> { + const BorderRadiusGeometryAttribute(super.value); - // Directional values - final Radius? topStart; - final Radius? topEnd; - final Radius? bottomStart; - final Radius? bottomEnd; + @visibleForTesting + Radius? get topLeft => value.topLeft; - const BorderRadiusGeometryAttribute({ - this.topLeft, - this.topRight, - this.bottomLeft, - this.bottomRight, - this.topStart, - this.topEnd, - this.bottomStart, - this.bottomEnd, - }); + @visibleForTesting + Radius? get topRight => value.topRight; - @override - BorderRadiusGeometryAttribute merge( - covariant BorderRadiusGeometryAttribute? other, - ); - - @override - T resolve(MixData mix); - - @override - get props => [ - topLeft, - topRight, - bottomLeft, - bottomRight, - topStart, - topEnd, - bottomStart, - bottomEnd, - ]; -} - -@immutable -class BorderRadiusAttribute - extends BorderRadiusGeometryAttribute { - const BorderRadiusAttribute({ - super.topLeft, - super.topRight, - super.bottomLeft, - super.bottomRight, - }); - - const BorderRadiusAttribute.zero() : this.all(Radius.zero); + @visibleForTesting + Radius? get bottomLeft => value.bottomLeft; - const BorderRadiusAttribute.all(Radius radius) - : super( - topLeft: radius, - topRight: radius, - bottomLeft: radius, - bottomRight: radius, - ); + @visibleForTesting + Radius? get bottomRight => value.bottomRight; - BorderRadiusAttribute.circular(double radius) - : this.all(Radius.circular(radius)); + @visibleForTesting + Radius? get topStart => value.topStart; - const BorderRadiusAttribute.horizontal({Radius? left, Radius? right}) - : super( - topLeft: left, - topRight: right, - bottomLeft: left, - bottomRight: right, - ); + @visibleForTesting + Radius? get topEnd => value.topEnd; - factory BorderRadiusAttribute.positional( - Radius p1, [ - Radius? p2, - Radius? p3, - Radius? p4, - ]) { - Radius topLeft = p1; - Radius topRight = p1; - Radius bottomLeft = p1; - Radius bottomRight = p1; + @visibleForTesting + Radius? get bottomStart => value.bottomStart; - if (p2 != null) { - bottomRight = p2; - bottomLeft = p2; - } - - if (p3 != null) { - topLeft = p1; - topRight = p2!; - bottomLeft = p2; - bottomRight = p3; - } - - if (p4 != null) { - topLeft = p1; - topRight = p2!; - bottomLeft = p3!; - bottomRight = p4; - } - - return BorderRadiusAttribute( - topLeft: topLeft, - topRight: topRight, - bottomLeft: bottomLeft, - bottomRight: bottomRight, - ); - } - - const BorderRadiusAttribute.vertical({Radius? top, Radius? bottom}) - : super( - topLeft: top, - topRight: top, - bottomLeft: bottom, - bottomRight: bottom, - ); - - @override - BorderRadiusAttribute merge(BorderRadiusGeometryAttribute? other) { - if (other == null) return this; - - return BorderRadiusAttribute( - topLeft: other.topLeft ?? topLeft, - topRight: other.topRight ?? topRight, - bottomLeft: other.bottomLeft ?? bottomLeft, - bottomRight: other.bottomRight ?? bottomRight, - ); - } + @visibleForTesting + Radius? get bottomEnd => value.bottomEnd; @override - BorderRadius resolve(MixData mix) { - return BorderRadius.only( - topLeft: topLeft ?? Radius.zero, - topRight: topRight ?? Radius.zero, - bottomLeft: bottomLeft ?? Radius.zero, - bottomRight: bottomRight ?? Radius.zero, - ); - } -} - -@immutable -class BorderRadiusDirectionalAttribute - extends BorderRadiusGeometryAttribute { - const BorderRadiusDirectionalAttribute({ - super.topStart, - super.topEnd, - super.bottomStart, - super.bottomEnd, - }); - - factory BorderRadiusDirectionalAttribute.positional( - Radius p1, [ - Radius? p2, - Radius? p3, - Radius? p4, - ]) { - Radius topStart = p1; - Radius topEnd = p1; - Radius bottomStart = p1; - Radius bottomEnd = p1; - - if (p2 != null) { - bottomEnd = p2; - bottomStart = p2; - } - - if (p3 != null) { - topStart = p1; - topEnd = p2!; - bottomStart = p2; - bottomEnd = p3; - } - - if (p4 != null) { - topStart = p1; - topEnd = p2!; - bottomStart = p3!; - bottomEnd = p4; - } - - return BorderRadiusDirectionalAttribute( - topStart: topStart, - topEnd: topEnd, - bottomStart: bottomStart, - bottomEnd: bottomEnd, - ); + BorderRadiusGeometryAttribute merge(BorderRadiusGeometryAttribute? other) { + return other == null + ? this + : BorderRadiusGeometryAttribute(value.merge(other.value)); } - BorderRadiusDirectionalAttribute.circular(double radius) - : this.all(Radius.circular(radius)); - - const BorderRadiusDirectionalAttribute.zero() : this.all(Radius.zero); - - const BorderRadiusDirectionalAttribute.all(Radius radius) - : super( - topStart: radius, - topEnd: radius, - bottomStart: radius, - bottomEnd: radius, - ); - - const BorderRadiusDirectionalAttribute.horizontal({ - Radius? start, - Radius? end, - }) : super( - topStart: start, - topEnd: end, - bottomStart: start, - bottomEnd: end, - ); - - const BorderRadiusDirectionalAttribute.vertical({ - Radius? top, - Radius? bottom, - }) : super( - topStart: top, - topEnd: top, - bottomStart: bottom, - bottomEnd: bottom, - ); - @override - BorderRadiusDirectionalAttribute merge( - BorderRadiusGeometryAttribute? other, - ) { - if (other == null) return this; - - return BorderRadiusDirectionalAttribute( - topStart: other.topStart ?? topStart, - topEnd: other.topEnd ?? topEnd, - bottomStart: other.bottomStart ?? bottomStart, - bottomEnd: other.bottomEnd ?? bottomEnd, - ); - } + BorderRadiusGeometry resolve(MixData mix) => value.resolve(mix); @override - BorderRadiusDirectional resolve(MixData mix) { - return BorderRadiusDirectional.only( - topStart: topStart ?? Radius.zero, - topEnd: topEnd ?? Radius.zero, - bottomStart: bottomStart ?? Radius.zero, - bottomEnd: bottomEnd ?? Radius.zero, - ); - } + List get props => [value]; } + +// class BorderRadiusAttribute extends BorderRadiusGeometryAttribute { +// const BorderRadiusAttribute(super.value); + +// factory BorderRadiusAttribute.all(Radius radius) { +// return BorderRadiusAttribute( +// BorderRadiusGeometryDto( +// topLeft: radius, +// topRight: radius, +// bottomLeft: radius, +// bottomRight: radius, +// ), +// ); +// } + +// factory BorderRadiusAttribute.only({ +// Radius? topLeft, +// Radius? topRight, +// Radius? bottomLeft, +// Radius? bottomRight, +// }) { +// return BorderRadiusAttribute( +// BorderRadiusGeometryDto( +// topLeft: topLeft, +// topRight: topRight, +// bottomLeft: bottomLeft, +// bottomRight: bottomRight, +// ), +// ); +// } + +// factory BorderRadiusAttribute.horizontal({Radius? left, Radius? right}) { +// return BorderRadiusAttribute.only( +// topLeft: left, +// topRight: right, +// bottomLeft: left, +// bottomRight: right, +// ); +// } + +// factory BorderRadiusAttribute.vertical({Radius? top, Radius? bottom}) { +// return BorderRadiusAttribute.only( +// topLeft: top, +// topRight: top, +// bottomLeft: bottom, +// bottomRight: bottom, +// ); +// } + +// factory BorderRadiusAttribute.circular(double radius) { +// return BorderRadiusAttribute.all(Radius.circular(radius)); +// } +// } + +// class BorderRadiusDirectionalAttribute extends BorderRadiusGeometryAttribute { +// const BorderRadiusDirectionalAttribute.raw(super.value); + +// factory BorderRadiusDirectionalAttribute.all(Radius radius) { +// return BorderRadiusDirectionalAttribute.raw( +// BorderRadiusGeometryDto( +// topStart: radius, +// topEnd: radius, +// bottomStart: radius, +// bottomEnd: radius, +// ), +// ); +// } + +// factory BorderRadiusDirectionalAttribute.only({ +// Radius? topStart, +// Radius? topEnd, +// Radius? bottomStart, +// Radius? bottomEnd, +// }) { +// return BorderRadiusDirectionalAttribute.raw( +// BorderRadiusGeometryDto( +// topStart: topStart, +// topEnd: topEnd, +// bottomStart: bottomStart, +// bottomEnd: bottomEnd, +// ), +// ); +// } + +// factory BorderRadiusDirectionalAttribute.horizontal({ +// Radius? start, +// Radius? end, +// }) { +// return BorderRadiusDirectionalAttribute.only( +// topStart: start, +// topEnd: end, +// bottomStart: start, +// bottomEnd: end, +// ); +// } + +// factory BorderRadiusDirectionalAttribute.vertical({ +// Radius? top, +// Radius? bottom, +// }) { +// return BorderRadiusDirectionalAttribute.only( +// topStart: top, +// topEnd: top, +// bottomStart: bottom, +// bottomEnd: bottom, +// ); +// } + +// factory BorderRadiusDirectionalAttribute.circular(double radius) { +// return BorderRadiusDirectionalAttribute.all(Radius.circular(radius)); +// } +// } diff --git a/lib/src/attributes/border/border_radius_dto.dart b/lib/src/attributes/border/border_radius_dto.dart new file mode 100644 index 000000000..98d7e8dd1 --- /dev/null +++ b/lib/src/attributes/border/border_radius_dto.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; + +@immutable +class BorderRadiusGeometryDto extends Dto + with Mergeable { + final Radius? topLeft; + final Radius? topRight; + final Radius? bottomLeft; + final Radius? bottomRight; + + // Directional values + final Radius? topStart; + final Radius? topEnd; + final Radius? bottomStart; + final Radius? bottomEnd; + + const BorderRadiusGeometryDto({ + this.topLeft, + this.topRight, + this.bottomLeft, + this.bottomRight, + this.topStart, + this.topEnd, + this.bottomStart, + this.bottomEnd, + }); + + static BorderRadiusGeometryDto from(BorderRadiusGeometry radius) { + if (radius is BorderRadius) { + return BorderRadiusGeometryDto( + topLeft: radius.topLeft, + topRight: radius.topRight, + bottomLeft: radius.bottomLeft, + bottomRight: radius.bottomRight, + ); + } + + if (radius is BorderRadiusDirectional) { + return BorderRadiusGeometryDto( + topStart: radius.topStart, + topEnd: radius.topEnd, + bottomStart: radius.bottomStart, + bottomEnd: radius.bottomEnd, + ); + } + + throw ArgumentError.value( + radius, + 'radius', + 'BorderRadiusGeometry type is not supported', + ); + } + + static BorderRadiusGeometryDto? maybeFrom(BorderRadiusGeometry? radius) { + return radius == null ? null : from(radius); + } + + bool get isDirectional => + topStart != null || + topEnd != null || + bottomStart != null || + bottomEnd != null; + + @override + BorderRadiusGeometryDto merge(BorderRadiusGeometryDto? other) { + if (other == null) return this; + + return BorderRadiusGeometryDto( + topLeft: other.topLeft ?? topLeft, + topRight: other.topRight ?? topRight, + bottomLeft: other.bottomLeft ?? bottomLeft, + bottomRight: other.bottomRight ?? bottomRight, + topStart: other.topStart ?? topStart, + topEnd: other.topEnd ?? topEnd, + bottomStart: other.bottomStart ?? bottomStart, + bottomEnd: other.bottomEnd ?? bottomEnd, + ); + } + + @override + BorderRadiusGeometry resolve(MixData mix) { + const defaultRadius = Radius.zero; + + return isDirectional + ? BorderRadiusDirectional.only( + topStart: topStart ?? defaultRadius, + topEnd: topEnd ?? defaultRadius, + bottomStart: bottomStart ?? defaultRadius, + bottomEnd: bottomEnd ?? defaultRadius, + ) + : BorderRadius.only( + topLeft: topLeft ?? defaultRadius, + topRight: topRight ?? defaultRadius, + bottomLeft: bottomLeft ?? defaultRadius, + bottomRight: bottomRight ?? defaultRadius, + ); + } + + @override + get props => [ + topLeft, + topRight, + bottomLeft, + bottomRight, + topStart, + topEnd, + bottomStart, + bottomEnd, + ]; +} diff --git a/lib/src/attributes/border/border_radius_util.dart b/lib/src/attributes/border/border_radius_util.dart new file mode 100644 index 000000000..abb03f4ce --- /dev/null +++ b/lib/src/attributes/border/border_radius_util.dart @@ -0,0 +1,413 @@ +// ignore_for_file: avoid-non-null-assertion + +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../core/extensions/values_ext.dart'; +import '../scalars/scalar_util.dart'; +import 'border_radius_attribute.dart'; +import 'border_radius_dto.dart'; + +/// Utility class for creating and manipulating attributes with [BorderRadiusGeometry] +/// +/// Allows setting of radius for a border. This class provides a convenient way to configure and apply border radius to [T] +/// +/// Accepts a builder function that returns [T] and takes a [BorderRadiusGeometryDto] as a parameter. +/// +/// Example usage: +/// +/// ```dart +/// final borderRadius = BorderRadiusGeometryUtility(builder); +/// final attribute = borderRadius(10.0); +/// ``` +/// +/// See also: +/// * [BorderRadiusGeometryDto], the data transfer object for [BorderRadiusGeometry] +/// * [MixUtility], the utility class that [BorderRadiusGeometryUtility] extends +/// * [RadiusUtility], the utility class for manipulating [Radius] +/// * [RadiusAttribute], the attribute class for [Radius] +/// * [BorderRadiusDirectionalUtility], the utility class for manipulating [BorderRadiusDirectional] +class BorderRadiusGeometryUtility + extends DtoUtility { + const BorderRadiusGeometryUtility(super.builder) + : super(valueToDto: BorderRadiusGeometryDto.from); + + /// Returns a [BorderRadiusDirectionalUtility] to manipulate [BorderRadiusDirectional]. + /// + /// Example usage: + /// + /// ```dart + /// final borderRadius = BorderRadiusGeometryUtility(builder); + /// final attribute = borderRadius.directional(10.0); + /// ``` + /// + /// See also: + /// * [BorderRadiusDirectionalUtility], the utility class for manipulating [BorderRadiusDirectional] + /// * [BorderRadiusDirectionalAttribute], the attribute class for [BorderRadiusDirectional] + BorderRadiusDirectionalUtility get directional { + return BorderRadiusDirectionalUtility(builder); + } + + /// Returns a [RadiusUtility] to manipulate [Radius]. + /// + /// Example usage: + /// + /// ```dart + /// final borderRadius = BorderRadiusGeometryUtility(builder); + /// final attribute = borderRadius.circular(10.0); + /// ``` + /// + /// See also: + /// * [RadiusUtility], the utility class for manipulating [Radius] + RadiusUtility get bottomLeft { + return RadiusUtility((radius) => only(bottomLeft: radius)); + } + + /// Returns a [RadiusUtility] to manipulate [Radius]. + /// + /// Example usage: + /// + /// ```dart + /// final borderRadius = BorderRadiusGeometryUtility(builder); + /// final attribute = borderRadius.circular(10.0); + /// ``` + /// + /// See also: + /// * [RadiusUtility], the utility class for manipulating [Radius] + RadiusUtility get bottomRight { + return RadiusUtility((radius) => only(bottomRight: radius)); + } + + /// Returns a [RadiusUtility] to manipulate [Radius]. + /// + /// Example usage: + /// + /// ```dart + /// final borderRadius = BorderRadiusGeometryUtility(builder); + /// final attribute = borderRadius.circular(10.0); + /// ``` + /// + /// See also: + /// * [RadiusUtility], the utility class for manipulating [Radius] + RadiusUtility get topLeft { + return RadiusUtility((radius) => only(topLeft: radius)); + } + + /// Returns a [RadiusUtility] to manipulate [Radius]. + /// + /// Example usage: + /// + /// ```dart + /// final borderRadius = BorderRadiusGeometryUtility(builder); + /// final attribute = borderRadius.circular(10.0); + /// ``` + /// + /// See also: + /// * [RadiusUtility], the utility class for manipulating [Radius] + RadiusUtility get topRight { + return RadiusUtility((radius) => only(topRight: radius)); + } + + /// Applies radius to all corners. + /// + /// Example usage: + /// + /// ```dart + /// final borderRadius = BorderRadiusGeometryUtility(builder); + /// final attribute = borderRadius.all(10.0); + /// ``` + /// + /// See also: + /// * [RadiusUtility], the utility class for manipulating [Radius] + RadiusUtility get all { + return RadiusUtility((radius) => only( + topLeft: radius, + topRight: radius, + bottomLeft: radius, + bottomRight: radius, + )); + } + + /// Applies radius to topRight and topLeft corners. + /// + /// Example usage: + /// + /// ```dart + /// final borderRadius = BorderRadiusGeometryUtility(builder); + /// final attribute = borderRadius.top(10.0); + /// ``` + /// + /// See also: + /// * [RadiusUtility], the utility class for manipulating [Radius] + RadiusUtility get top { + return RadiusUtility( + (radius) => only(topLeft: radius, topRight: radius), + ); + } + + /// Applies radius to bottomRight and bottomLeft corners. + /// + /// Example usage: + /// + /// ```dart + /// final borderRadius = BorderRadiusGeometryUtility(builder); + /// final attribute = borderRadius.bottom(10.0); + /// ``` + /// + /// See also: + /// * [RadiusUtility], the utility class for manipulating [Radius] + RadiusUtility get bottom { + return RadiusUtility( + (radius) => only(bottomLeft: radius, bottomRight: radius), + ); + } + + /// Applies radius to topLeft and bottomLeft corners. + /// + /// Example usage: + /// + /// ```dart + /// final borderRadius = BorderRadiusGeometryUtility(builder); + /// final attribute = borderRadius.left(10.0); + /// ``` + /// + /// See also: + /// * [RadiusUtility], the utility class for manipulating [Radius] + RadiusUtility get left { + return RadiusUtility( + (radius) => only(topLeft: radius, bottomLeft: radius), + ); + } + + /// Applies radius to topRight and bottomRight corners. + /// + /// Example usage: + /// + /// ```dart + /// final borderRadius = BorderRadiusGeometryUtility(builder); + /// final attribute = borderRadius.right(10.0); + /// ``` + /// + /// See also: + /// * [RadiusUtility], the utility class for manipulating [Radius] + RadiusUtility get right { + return RadiusUtility( + (radius) => only(topRight: radius, bottomRight: radius), + ); + } + + /// Applies a Radius.zero to all corners + T zero() => all.zero(); + + // Only specific corners + T only({ + Radius? topLeft, + Radius? topRight, + Radius? bottomLeft, + Radius? bottomRight, + }) { + return builder( + BorderRadiusGeometryDto( + topLeft: topLeft, + topRight: topRight, + bottomLeft: bottomLeft, + bottomRight: bottomRight, + ), + ); + } + + /// Sets the border radius for each corner of a [BorderRadiusGeometry]. + /// + /// This method allows setting distinct radii for each corner. Each parameter + /// represents a corner's radius, starting from top-left and moving clockwise. + /// + /// Parameters: + /// - [topLeft]: Radius for the top-left corner. + /// - [topRight]: Radius for the top-right corner. Defaults to [topLeft] if not provided. + /// - [bottomLeft]: Radius for the bottom-left corner. Defaults to [topLeft] if not provided. + /// - [bottomRight]: Radius for the bottom-right corner. Defaults to [topLeft] if not provided. + /// + /// Example usage: + /// ```dart + /// final borderRadiusUtility = BorderRadiusGeometryUtility(builder); + /// final attribute = borderRadiusUtility( + /// topLeft: 10.0, + /// topRight: 12.0, + /// bottomLeft: 8.0, + /// bottomRight: 10.0, + /// ); + /// ``` + + T call(double p1, [double? p2, double? p3, double? p4]) { + double topLeft = p1; + double topRight = p1; + double bottomLeft = p1; + double bottomRight = p1; + + if (p2 != null) { + bottomRight = p2; + bottomLeft = p2; + } + + if (p3 != null) { + topLeft = p1; + topRight = p2!; + bottomLeft = p2; + bottomRight = p3; + } + + if (p4 != null) { + topLeft = p1; + topRight = p2!; + bottomLeft = p3!; + bottomRight = p4; + } + + return only( + topLeft: topLeft.toRadius(), + topRight: topRight.toRadius(), + bottomLeft: bottomLeft.toRadius(), + bottomRight: bottomRight.toRadius(), + ); + } +} + +class BorderRadiusDirectionalUtility + extends DtoUtility { + const BorderRadiusDirectionalUtility(super.builder) + : super(valueToDto: BorderRadiusGeometryDto.from); + + /// Returns a [RadiusUtility] to manipulate [Radius]. + /// + /// Example usage: + /// + /// ```dart + /// final borderRadius = BorderRadiusDirectionalUtility(builder); + /// final attribute = borderRadius.bottomStart(10.0); + /// ``` + /// + /// See also: + /// * [RadiusUtility], the utility class for manipulating [Radius] + RadiusUtility get bottomStart { + return RadiusUtility((radius) => only(bottomStart: radius)); + } + + /// Returns a [RadiusUtility] to manipulate [Radius]. + /// + /// Example usage: + /// + /// ```dart + /// final borderRadius = BorderRadiusDirectionalUtility(builder); + /// final attribute = borderRadius.bottomEnd(10.0); + /// ``` + /// + /// See also: + /// * [RadiusUtility], the utility class for manipulating [Radius] + RadiusUtility get bottomEnd { + return RadiusUtility((radius) => only(bottomEnd: radius)); + } + + /// Returns a [RadiusUtility] to manipulate [Radius]. + /// + /// Example usage: + /// + /// ```dart + /// final borderRadius = BorderRadiusDirectionalUtility(builder); + /// final attribute = borderRadius.topStart(10.0); + /// ``` + /// + /// See also: + /// * [RadiusUtility], the utility class for manipulating [Radius] + RadiusUtility get topStart { + return RadiusUtility((radius) => only(topStart: radius)); + } + + /// Returns a [RadiusUtility] to manipulate [Radius]. + /// + /// Example usage: + /// + /// ```dart + /// final borderRadius = BorderRadiusDirectionalUtility(builder); + /// final attribute = borderRadius.topEnd(10.0); + /// ``` + /// + /// See also: + /// * [RadiusUtility], the utility class for manipulating [Radius] + RadiusUtility get topEnd { + return RadiusUtility((radius) => only(topEnd: radius)); + } + + /// Applies radius to all corners. + /// + /// Example usage: + /// + /// ```dart + /// final borderRadius = BorderRadiusDirectionalUtility(builder); + /// final attribute = borderRadius.all(10.0); + /// ``` + /// + /// See also: + /// * [RadiusUtility], the utility class for manipulating [Radius] + RadiusUtility get all { + return RadiusUtility((radius) => only( + topStart: radius, + topEnd: radius, + bottomStart: radius, + bottomEnd: radius, + )); + } + + T call(double p1, [double? p2, double? p3, double? p4]) { + double topStart = p1; + double topEnd = p1; + double bottomStart = p1; + double bottomEnd = p1; + + if (p2 != null) { + bottomEnd = p2; + bottomStart = p2; + } + + if (p3 != null) { + topStart = p1; + topEnd = p2!; + bottomStart = p2; + bottomEnd = p3; + } + + if (p4 != null) { + topStart = p1; + topEnd = p2!; + bottomStart = p3!; + bottomEnd = p4; + } + + return builder( + BorderRadiusGeometryDto( + topStart: topStart.toRadius(), + topEnd: topEnd.toRadius(), + bottomStart: bottomStart.toRadius(), + bottomEnd: bottomEnd.toRadius(), + ), + ); + } + + // Only specific corners + T only({ + Radius? topStart, + Radius? topEnd, + Radius? bottomStart, + Radius? bottomEnd, + }) { + return builder( + BorderRadiusGeometryDto( + topStart: topStart, + topEnd: topEnd, + bottomStart: bottomStart, + bottomEnd: bottomEnd, + ), + ); + } + + T zero() => all.zero(); +} diff --git a/lib/src/attributes/border/border_util.dart b/lib/src/attributes/border/border_util.dart new file mode 100644 index 000000000..62ee2d614 --- /dev/null +++ b/lib/src/attributes/border/border_util.dart @@ -0,0 +1,472 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../core/extensions/values_ext.dart'; +import '../color/color_dto.dart'; +import '../color/color_util.dart'; +import '../scalars/scalar_util.dart'; +import 'border_dto.dart'; + +/// Utility class for creating and manipulating [BoxBorder] attributes. +/// +/// Accepts a [builder] function that takes a [BoxBorderDto] and returns an object of type [T]. +/// +/// Example usage: +/// +/// ```dart +/// final boxBorder = BoxBorderUtility(builder); +/// final attribute = boxBorder.all( +/// color: Colors.red, +/// width: 2.0, +/// ); +/// +/// final borderAttribute = boxBorder( +/// color: Colors.red, +/// width: 2.0, +/// style: BorderStyle.solid, +/// strokeAlign: 0.5, +/// ); +/// ``` +/// +/// See also: +/// * [BoxBorder], which is a class for creating box borders. +/// * [BoxBorderDto], which is a data transfer object for [BoxBorder]. +class BoxBorderUtility + extends DtoUtility { + /// Constructor for creating an instance of the class. + const BoxBorderUtility(super.builder) : super(valueToDto: BoxBorderDto.from); + + BoxBorderDto _symmetric({ + BorderSideDto? vertical, + BorderSideDto? horizontal, + }) { + return BoxBorderDto( + top: horizontal, + bottom: horizontal, + left: vertical, + right: vertical, + ); + } + + BoxBorderDto _fromBorderSide(BorderSideDto side) { + return BoxBorderDto(top: side, bottom: side, left: side, right: side); + } + + /// Method to set the border on all sides. + /// + /// Returns a `BorderSideUtility` object for applying border attributes + /// to all sides of a box. This method is useful for setting consistent border styles + /// around the entire box. + /// + /// Example usage: + /// + /// ```dart + /// final border = BorderUtility(builder); + /// final attribute = border.all( + /// color: Colors.red, + /// width: 2.0, + /// ); + /// ``` + /// + /// `attribute` will hold a [T] with a [BorderAttribute] that applies + /// a red color and a width of 2.0 uniformly to all border sides. + /// + /// See also: + /// * [BorderSideUtility], which is effective for simultaneously configuring [BorderSide] attributes on all sides. + BorderSideUtility get all { + return BorderSideUtility((side) => builder(_fromBorderSide(side))); + } + + /// Method to set the border on the bottom side. + /// + /// Returns a `BorderSideUtility` object for configuring border attributes + /// specifically for the bottom side of a box. This utility enables precise control + /// over the appearance of the bottom border. + /// + /// Example usage: + /// + /// ```dart + /// final border = BorderUtility(builder); + /// final attribute = border.bottom( + /// color: Colors.blue, + /// width: 3.0, + /// style: BorderStyle.solid, + /// strokeAlign: 0.5, + /// ); + /// ``` + /// + /// `attribute` will create a [T] with a [BorderAttribute] for the bottom border, + /// + /// See also: + /// * [BorderSideUtility], which offers specialized manipulation of individual [BorderSide] attributes. + BorderSideUtility get bottom { + return BorderSideUtility((side) => only(bottom: side)); + } + + /// Method to set the border on the top side. + /// + /// Returns a `BorderSideUtility` object for configuring border attributes + /// specifically for the top side of a box. This utility enables precise control + /// over the appearance of the top border. + /// + /// Example usage: + /// + /// ```dart + /// final border = BorderUtility(builder); + /// final attribute = border.top( + /// color: Colors.blue, + /// width: 3.0, + /// style: BorderStyle.solid, + /// strokeAlign: 0.5, + /// ); + /// ``` + /// + /// `attribute` will create a [T] with a [BorderAttribute] for the top border, + /// + /// See also: + /// * [BorderSideUtility], which offers specialized manipulation of individual [BorderSide] attributes. + BorderSideUtility get top { + return BorderSideUtility((side) => only(top: side)); + } + + /// Method to set the border on the left side. + /// + /// Returns a `BorderSideUtility` object for configuring border attributes + /// specifically for the left side of a box. This utility enables precise control + /// over the appearance of the left border. + /// + /// Example usage: + /// + /// ```dart + /// final border = BorderUtility(builder); + /// final attribute = border.left( + /// color: Colors.blue, + /// width: 3.0, + /// style: BorderStyle.solid, + /// strokeAlign: 0.5, + /// ); + /// ``` + /// + /// `attribute` will create a [T] with a [BoxBorderDto] for the left border, + /// + /// See also: + /// * [BorderSideUtility], which offers specialized manipulation of individual [BorderSide] attributes. + BorderSideUtility get left { + return BorderSideUtility((side) => only(left: side)); + } + + /// Method to set the border on the right side. + /// + /// Returns a `BorderSideUtility` object for configuring border attributes + /// specifically for the right side of a box. This utility enables precise control + /// over the appearance of the right border. + /// + /// Example usage: + /// + /// ```dart + /// final border = BorderUtility(builder); + /// final attribute = border.right( + /// color: Colors.blue, + /// width: 3.0, + /// style: BorderStyle.solid, + /// strokeAlign: 0.5, + /// ); + /// ``` + /// + /// `attribute` will create a [T] with a [BoxBorderDto] for the right border, + /// + /// See also: + /// * [BorderSideUtility], which offers specialized manipulation of individual [BorderSide] attributes. + BorderSideUtility get right { + return BorderSideUtility((side) => only(right: side)); + } + + /// Method to set the border on the start side in a directional context. + /// + /// Returns a `BorderSideUtility` designed for applying border attributes + /// specifically to the start side of a directional box layout. + /// + /// Example usage: + /// + /// ```dart + /// final boxBorder = BoxBorderUtility(builder); + /// final attribute = boxBorder.start( + /// color: Colors.purple, + /// width: 2.5, + /// ); + /// ``` + /// + /// In this example, `attribute` will be built with a [BoxBorderDto] + /// that applies a purple color and a width of 2.5 to the start side of the border. + /// + /// See also: + /// * [BorderSideUtility], which is utilized for modifying [BorderSide] attributes. + BorderSideUtility get start => + BorderSideUtility((side) => only(start: side)); + + /// Method to set the border on the end side in a directional context. + /// + /// Creates a `BorderSideUtility` object for applying border attributes + /// to the end side of a directional box layout. + /// + /// Example usage: + /// + /// ```dart + /// final boxBorder = BoxBorderUtility(builder); + /// final attribute = boxBorder.end( + /// color: Colors.orange, + /// width: 2.0, + /// ); + /// ``` + /// + /// Here, `attribute` will be built with a [BoxBorderDto] configured + /// with an orange color and a width of 2.0 for the end border side. + /// + /// See also: + /// * [BorderSideUtility], which helps in configuring [BorderSide] attributes for directional layouts. + BorderSideUtility get end => BorderSideUtility((side) => only(end: side)); + + /// Method to set the borders on the vertical sides. + /// + /// Returns a `BorderSideUtility` object for applying border attributes + /// to both the left and right sides of a box at the same time. + /// + /// Example usage: + /// + /// ```dart + /// final border = BorderUtility(builder); + /// final attribute = border.vertical( + /// color: Colors.indigo, + /// width: 1.5, + /// style: BorderStyle.solid, + /// strokeAlign: 0.5, + /// ); + /// + /// In this instance, `attribute` will be built with a [BoxBorderDto] + /// that applies an indigo color and a width of 1.5 to both the left and right border sides. + /// + /// See also: + /// * [BorderSideUtility], which aids in setting [BorderSide] attributes for vertical borders. + BorderSideUtility get vertical { + return BorderSideUtility((side) => builder(_symmetric(vertical: side))); + } + + /// Method to set the borders on the horizontal sides. + /// + /// Returns a `BorderSideUtility` object for applying border attributes + /// to both the top and bottom sides of a box simultaneously. + /// + /// Example usage: + /// + /// ```dart + /// final border = BorderUtility(builder); + /// final attribute = border.horizontal( + /// color: Colors.teal, + /// width: 1.0, + /// style: BorderStyle.solid, + /// strokeAlign: 0.5, + /// ); + /// + /// `attribute` will contain a [T] with a [BoxBorderDto] that applies + /// a teal color and a width of 1.0 to both the top and bottom border sides. + /// + /// See also: + /// * [BorderSideUtility], which facilitates simultaneous configuration of horizontal [BorderSide] attributes. + BorderSideUtility get horizontal { + return BorderSideUtility( + (side) => builder(_symmetric(horizontal: side)), + ); + } + + BoxBorderDto fromBorderSide(BorderSideDto side) { + return BoxBorderDto(top: side, bottom: side, left: side, right: side); + } + + T only({ + BorderSideDto? top, + BorderSideDto? bottom, + BorderSideDto? left, + BorderSideDto? right, + BorderSideDto? start, + BorderSideDto? end, + }) { + return builder( + BoxBorderDto( + top: top, + bottom: bottom, + left: left, + right: right, + start: start, + end: end, + ), + ); + } + + /// Creates a [BoxBorderDto] with the provided parameters and calls the [builder] function. + /// + /// Example usage: + /// + /// ```dart + /// final boxBorder = BoxBorderUtility(builder); + /// final attribute = boxBorder( + /// color: Colors.red, + /// width: 2.0, + /// style: BorderStyle.solid, + /// strokeAlign: 0.5, + /// ); + /// ``` + /// + /// Attribute now holds a [StyleAttribute] with a [BoxBorderDto] that + /// has color as red, width as 2.0, and BorderStyle as solid. + T call({ + Color? color, + double? width, + BorderStyle? style, + double? strokeAlign, + }) { + return all( + color: color, + strokeAlign: strokeAlign, + style: style, + width: width, + ); + } +} + +/// Utility class for creating and manipulating [BorderSideDto] attributes. +/// +/// Accepts a [builder] function that takes a [BorderSideDto] and returns an object of type [T]. +/// +/// Example usage: +/// +/// ```dart +/// final borderSide = BorderSideUtility(builder); +/// +/// final attribute = borderSide.call( +/// color: Colors.red, +/// width: 2.0, +/// style: BorderStyle.solid, +/// strokeAlign: 0.5, +/// ); +/// ``` +/// +/// `attribute` will hold a [T] with a [BorderSideDto] that has color as red, width as 2.0, and BorderStyle as solid. +/// +/// See also: +/// * [BorderSideDto], which is a data transfer object for [BorderSide]. +class BorderSideUtility + extends MixUtility { + const BorderSideUtility(super.builder); + + T _only({ + ColorDto? color, + double? width, + BorderStyle? style, + double? strokeAlign, + }) { + return builder(BorderSideDto( + color: color, + strokeAlign: strokeAlign, + style: style, + width: width, + )); + } + + /// Method to set the color of the [BorderSideDto] + /// + /// Returns a `ColorUtility` object for applying color attributes + /// to the [BorderSideDto]. + /// + /// Example usage: + /// + /// ```dart + /// final borderSide = BorderSideUtility(builder); + /// final attribute = borderSide.color(Colors.red); + /// ``` + /// + /// `attribute` will hold a [T] with a [BorderSideDto] that has color as red. + /// + /// See also: + /// * [ColorUtility], which is a utility class for manipulating [Color] attributes. + /// * [ColorDto], which is a data transfer object for [Color]. + ColorUtility get color => ColorUtility((color) => _only(color: color)); + + /// Method to set the style of the [BorderSideDto] + /// + /// Returns a [BorderStyleUtility] object for applying scalar attributes + /// + /// Example usage: + /// + /// ```dart + /// final borderSide = BorderSideUtility(builder); + /// final attribute = borderSide.style.solid(); + /// ``` + /// + /// `attribute` will hold a [T] with a [BorderSideDto] that has style as solid. + /// + /// See also: + /// * [BorderStyleUtility], which is a utility class for manipulating [BorderStyle] attributes. + BorderStyleUtility get style => + BorderStyleUtility((style) => _only(style: style)); + + /// Method to set the width of the [BorderSideDto] + /// + /// Accepts a [double] value as a parameter. + /// + /// Example usage: + /// + /// ```dart + /// final borderSide = BorderSideUtility(builder); + /// final attribute = borderSide.width(2.0); + /// ``` + /// + /// `attribute` will hold a [T] with a [BorderSideDto] that has width as 2.0. + T width(double width) => call(width: width); + + /// Method to set the stroke align of the [BorderSideDto] + /// + /// Accepts a [double] value as a parameter. + /// + /// Example usage: + /// + /// ```dart + /// final borderSide = BorderSideUtility(builder); + /// final attribute = borderSide.strokeAlign(0.5); + /// ``` + /// + /// `attribute` will hold a [T] with a [BorderSideDto] that has stroke align as 0.5. + T strokeAlign(double strokeAlign) => call(strokeAlign: strokeAlign); + + /// Method that allows to set individual properties of the [BorderSideDto] to all sides. + /// + /// Example usage: + /// + /// ```dart + /// final borderSide = BorderSideUtility(builder); + /// final attribute = borderSide( + /// color: Colors.red, + /// width: 2.0, + /// style: BorderStyle.solid, + /// strokeAlign: 0.5, + /// ); + /// + /// attribute will hold a [T] with a [BorderSideDto] that has color as red, width as 2.0, style as solid, and stroke align as 0.5. + /// + /// See also: + /// * [BorderSideDto], which is a data transfer object for [BorderSide]. + T call({ + Color? color, + double? width, + BorderStyle? style, + double? strokeAlign, + }) { + final side = BorderSideDto( + color: color?.toDto(), + strokeAlign: strokeAlign, + style: style, + width: width, + ); + + return builder(side); + } +} diff --git a/lib/src/attributes/color/color_attribute.dart b/lib/src/attributes/color/color_attribute.dart new file mode 100644 index 000000000..a11c85648 --- /dev/null +++ b/lib/src/attributes/color/color_attribute.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; +import 'color_dto.dart'; + +@immutable +abstract class ColorAttribute> + extends ResolvableAttribute { + final ColorDto value; + const ColorAttribute(this.value); + + @override + Self merge(Self? other); + + @override + Color resolve(MixData mix) => value.resolve(mix); + + @override + get props => [value]; +} diff --git a/lib/src/attributes/color/color_dto.dart b/lib/src/attributes/color/color_dto.dart new file mode 100644 index 000000000..a9e9945c7 --- /dev/null +++ b/lib/src/attributes/color/color_dto.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; +import '../../theme/tokens/color_token.dart'; + +@immutable +class ColorDto extends Dto with Mergeable { + final Color value; + const ColorDto(this.value); + + static ColorDto? maybeFrom(Color? value) => + value == null ? null : ColorDto(value); + + @override + Color resolve(MixData mix) { + final colorRef = value; + + return colorRef is ColorRef ? mix.tokens.colorRef(colorRef) : colorRef; + } + + @override + ColorDto merge(covariant ColorDto? other) { + return other == null ? this : ColorDto(other.value); + } + + @override + get props => [value]; +} diff --git a/lib/src/attributes/color/color_util.dart b/lib/src/attributes/color/color_util.dart new file mode 100644 index 000000000..b59b50698 --- /dev/null +++ b/lib/src/attributes/color/color_util.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../scalars/scalar_util.dart'; +import 'color_dto.dart'; + +@immutable +class ColorUtility + extends DtoUtility + with CallableDtoUtilityMixin { + const ColorUtility(super.builder) : super(valueToDto: ColorDto.new); +} + +@immutable +class ColorAttributeUtility extends ColorUtility { + const ColorAttributeUtility(super.builder); +} diff --git a/lib/src/attributes/color_attribute.dart b/lib/src/attributes/color_attribute.dart deleted file mode 100644 index b3801ab5a..000000000 --- a/lib/src/attributes/color_attribute.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../factory/mix_provider_data.dart'; -import '../theme/tokens/color_token.dart'; -import 'attribute.dart'; - -@immutable -class ColorAttribute extends ScalarAttribute { - const ColorAttribute(super.value); - - @override - ColorAttribute create(Color value) => ColorAttribute(value); - - @override - Color resolve(MixData mix) { - final resolvedColor = value; - - return resolvedColor is ColorToken - ? mix.resolver.colorToken(resolvedColor) - : resolvedColor; - } -} - -@immutable -class IconColorAttribute extends ColorAttribute { - const IconColorAttribute(super.color); - - @override - IconColorAttribute create(value) => IconColorAttribute(value); -} - -@immutable -class ImageColorAttribute extends ColorAttribute { - const ImageColorAttribute(super.color); - - @override - ImageColorAttribute create(value) => ImageColorAttribute(value); -} diff --git a/lib/src/attributes/constraints/constraints_attribute.dart b/lib/src/attributes/constraints/constraints_attribute.dart new file mode 100644 index 000000000..f25cca240 --- /dev/null +++ b/lib/src/attributes/constraints/constraints_attribute.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; +import '../scalars/scalars_attribute.dart'; +import 'constraints_dto.dart'; + +abstract class ConstraintsAttribute, + Value extends Constraints> extends DtoAttribute { + const ConstraintsAttribute(super.value); +} + +class BoxConstraintsAttribute extends ConstraintsAttribute< + BoxConstraintsAttribute, + BoxConstraintsDto, + BoxConstraints> with SingleChildRenderAttributeMixin { + const BoxConstraintsAttribute(super.value); + + static BoxConstraintsAttribute from(BoxConstraints constraints) => + BoxConstraintsAttribute(BoxConstraintsDto.from(constraints)); + + static BoxConstraintsAttribute? maybeFrom(BoxConstraints? constraints) { + return constraints == null ? null : from(constraints); + } + + @override + BoxConstraintsAttribute merge(BoxConstraintsAttribute? other) { + return other == null + ? this + : BoxConstraintsAttribute(value.merge(other.value)); + } + + @override + ConstrainedBox build(MixData mix, Widget child) { + return ConstrainedBox(constraints: resolve(mix), child: child); + } +} + +@immutable +class WidthAttribute extends ScalarAttribute { + const WidthAttribute(super.value); + + static WidthAttribute? maybeFrom(double? value) { + return value == null ? null : WidthAttribute(value); + } +} + +@immutable +class HeightAttribute extends ScalarAttribute { + const HeightAttribute(super.value); + + static HeightAttribute? maybeFrom(double? value) { + return value == null ? null : HeightAttribute(value); + } +} diff --git a/lib/src/attributes/constraints_attribute.dart b/lib/src/attributes/constraints/constraints_dto.dart similarity index 51% rename from lib/src/attributes/constraints_attribute.dart rename to lib/src/attributes/constraints/constraints_dto.dart index 66e3c5ef1..f48fa2ec4 100644 --- a/lib/src/attributes/constraints_attribute.dart +++ b/lib/src/attributes/constraints/constraints_dto.dart @@ -1,53 +1,40 @@ -import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import '../factory/mix_provider_data.dart'; -import 'attribute.dart'; +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; -@immutable -abstract class ConstraintsAttribute - extends VisualAttribute { - const ConstraintsAttribute(); - - @override - ConstraintsAttribute merge(covariant ConstraintsAttribute? other); - - @override - T resolve(MixData mix); +abstract class ConstraintsDto, + Value extends Constraints> extends Dto { + const ConstraintsDto(); } -@immutable -class BoxConstraintsAttribute extends ConstraintsAttribute { - final double? width; - final double? height; +class BoxConstraintsDto + extends ConstraintsDto { final double? minWidth; final double? maxWidth; final double? minHeight; final double? maxHeight; - const BoxConstraintsAttribute({ - this.width, - this.height, + const BoxConstraintsDto({ this.minWidth, this.maxWidth, this.minHeight, this.maxHeight, }); - @override - BoxConstraintsAttribute merge(covariant BoxConstraintsAttribute? other) { - if (other == null) return this; - - return BoxConstraintsAttribute( - width: other.width ?? width, - height: other.height ?? height, - minWidth: other.minWidth ?? minWidth, - maxWidth: other.maxWidth ?? maxWidth, - minHeight: other.minHeight ?? minHeight, - maxHeight: other.maxHeight ?? maxHeight, + static BoxConstraintsDto from(BoxConstraints constraints) { + return BoxConstraintsDto( + minWidth: constraints.minWidth, + maxWidth: constraints.maxWidth, + minHeight: constraints.minHeight, + maxHeight: constraints.maxHeight, ); } + static BoxConstraintsDto? maybeFrom(BoxConstraints? constraints) { + return constraints == null ? null : from(constraints); + } + @override BoxConstraints resolve(MixData mix) { BoxConstraints? constraints; @@ -64,14 +51,21 @@ class BoxConstraintsAttribute extends ConstraintsAttribute { ); } - constraints = (width != null || height != null) - ? constraints?.tighten(width: width, height: height) ?? - BoxConstraints.tightFor(width: width, height: height) - : constraints; - return constraints ?? const BoxConstraints(); } + @override + BoxConstraintsDto merge(BoxConstraintsDto? other) { + if (other == null) return this; + + return BoxConstraintsDto( + minWidth: other.minWidth ?? minWidth, + maxWidth: other.maxWidth ?? maxWidth, + minHeight: other.minHeight ?? minHeight, + maxHeight: other.maxHeight ?? maxHeight, + ); + } + @override get props => [minWidth, maxWidth, minHeight, maxHeight]; } diff --git a/lib/src/attributes/constraints/constraints_util.dart b/lib/src/attributes/constraints/constraints_util.dart new file mode 100644 index 000000000..34f3fec12 --- /dev/null +++ b/lib/src/attributes/constraints/constraints_util.dart @@ -0,0 +1,102 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../scalars/scalar_util.dart'; +import 'constraints_attribute.dart'; +import 'constraints_dto.dart'; + +/// Utility class for building box constraints-related style attributes. +/// +/// Accepts a builder function that returns [T] and takes a [BoxConstraintsAttribute] as a parameter. +/// +/// This utility allows for detailed configuration of box constraints, +/// supporting attributes like `minWidth`, `maxWidth`, `minHeight`, and `maxHeight`. +/// +/// Example: +/// ```dart +/// BoxConstraintsUtility().minWidth(100).maxHeight(200) +/// ``` +class BoxConstraintsUtility + extends DtoUtility { + /// Creates a [BoxConstraintsUtility] with a builder function. + /// + /// The builder function takes a [BoxConstraintsAttribute] as a parameter and returns [T]. + /// + /// Example: + /// ```dart + /// final boxConstraints = BoxConstraintsUtility(builder); + /// ``` + const BoxConstraintsUtility(super.builder) + : super(valueToDto: BoxConstraintsDto.from); + + /// Sets the minimum width of the box constraints. + /// + /// The `width` parameter sets the smallest width that the box can be. + /// + /// Example: + /// ```dart + /// final boxConstraints = BoxConstraintsUtility(builder); + /// final attribute = boxConstraints.minWidth(100); + /// ``` + /// + /// Attribute now holds a [T] with a [BoxConstraintsAttribute] that has a minWidth value of `100`. + T minWidth(double width) => call(minWidth: width); + + /// Sets the maximum width of the box constraints. + /// + /// The `width` parameter sets the largest width that the box can be. + /// + /// Example: + /// + /// ```dart + /// final boxConstraints = BoxConstraintsUtility(builder); + /// final attribute = boxConstraints.maxWidth(100); + /// ``` + /// + /// Attribute now holds a [T] with a [BoxConstraintsAttribute] that has a maxWidth value of `100`. + T maxWidth(double width) => call(maxWidth: width); + + /// Sets the minimum height of the box constraints. + /// + /// The `height` parameter sets the smallest height that the box can be. + /// + /// Example: + /// + /// ```dart + /// final boxConstraints = BoxConstraintsUtility(builder); + /// final attribute = boxConstraints.minHeight(100); + /// ``` + /// + /// Attribute now holds a [T] with a [BoxConstraintsAttribute] that has a minHeight value of `100`. + T minHeight(double height) => call(minHeight: height); + + /// Sets the maximum height of the box constraints. + /// + /// The `height` parameter sets the largest height that the box can be. + /// + /// Example: + /// + /// ```dart + /// final boxConstraints = BoxConstraintsUtility(builder); + /// final attribute = boxConstraints.maxHeight(100); + /// ``` + /// + /// Attribute now holds a [T] with a [BoxConstraintsAttribute] that has a maxHeight value of `100`. + T maxHeight(double height) => call(maxHeight: height); + + T call({ + double? minWidth, + double? maxWidth, + double? minHeight, + double? maxHeight, + }) { + return builder( + BoxConstraintsDto( + minWidth: minWidth, + maxWidth: maxWidth, + minHeight: minHeight, + maxHeight: maxHeight, + ), + ); + } +} diff --git a/lib/src/attributes/decoration/decoration_attribute.dart b/lib/src/attributes/decoration/decoration_attribute.dart new file mode 100644 index 000000000..179a45a29 --- /dev/null +++ b/lib/src/attributes/decoration/decoration_attribute.dart @@ -0,0 +1,85 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers + +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import 'decoration_dto.dart'; + +@immutable +class DecorationAttribute + extends DtoAttribute + with SingleChildRenderAttributeMixin { + const DecorationAttribute(super.value); + + @override + DecorationAttribute merge(covariant DecorationAttribute? other) { + if (other == null) return this; + + if (value.runtimeType != other.value.runtimeType) return other; + + return DecorationAttribute(value.merge(other.value)); + } + + @override + DecoratedBox build(mix, child) { + return DecoratedBox(decoration: resolve(mix), child: child); + } +} + +// @immutable +// class BoxDecorationAttribute extends DecorationAttribute { +// const BoxDecorationAttribute(super.value); + +// factory BoxDecorationAttribute.only({ +// ColorDto? color, +// GradientDto? gradient, +// BoxShape? shape, +// BorderRadiusGeometryDto? borderRadius, +// List? boxShadow, +// BoxBorderDto? border, +// }) { +// return BoxDecorationAttribute( +// BoxDecorationDto( +// color: color, +// border: border, +// borderRadius: borderRadius, +// gradient: gradient, +// boxShadow: boxShadow, +// shape: shape, +// ), +// ); +// } + +// @override +// BoxDecorationAttribute _mergeWith(BoxDecorationAttribute other) { +// return BoxDecorationAttribute(value.merge(other.value)); +// } +// } + +// @immutable +// class ShapeDecorationAttribute extends DecorationAttribute< +// ShapeDecorationAttribute, ShapeDecorationDto, ShapeDecoration> { +// const ShapeDecorationAttribute(super.value); + +// factory ShapeDecorationAttribute.only({ +// ColorDto? color, +// GradientDto? gradient, +// ShapeBorder? shape, +// List? shadows, +// }) { +// return ShapeDecorationAttribute( +// ShapeDecorationDto( +// color: color, +// shape: shape, +// gradient: gradient, +// shadows: shadows, +// ), +// ); +// } + +// @override +// ShapeDecorationAttribute _mergeWith(ShapeDecorationAttribute other) { +// return ShapeDecorationAttribute(value.merge(other.value)); +// } +// } diff --git a/lib/src/attributes/decoration/decoration_dto.dart b/lib/src/attributes/decoration/decoration_dto.dart new file mode 100644 index 000000000..d99d0ff5c --- /dev/null +++ b/lib/src/attributes/decoration/decoration_dto.dart @@ -0,0 +1,150 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers + +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../core/extensions/iterable_ext.dart'; +import '../../factory/mix_provider_data.dart'; +import '../border/border_dto.dart'; +import '../border/border_radius_dto.dart'; +import '../color/color_dto.dart'; +import '../gradient/gradient_dto.dart'; +import '../shadow/shadow_dto.dart'; + +@immutable +abstract class DecorationDto extends Dto + with Mergeable> { + const DecorationDto(); + + static DecorationDto from( + Decoration decoration, + ) { + if (decoration is BoxDecoration) { + return BoxDecorationDto.from(decoration) as DecorationDto; + } + if (decoration is ShapeDecoration) { + return ShapeDecorationDto.from(decoration) as DecorationDto; + } + + throw UnimplementedError( + 'DecorationDto.from: $decoration is not supported', + ); + } +} + +@immutable +class BoxDecorationDto extends DecorationDto { + final ColorDto? color; + final BoxBorderDto? border; + final BorderRadiusGeometryDto? borderRadius; + final GradientDto? gradient; + final List? boxShadow; + final BoxShape? shape; + + const BoxDecorationDto({ + this.color, + this.border, + this.borderRadius, + this.gradient, + this.boxShadow, + this.shape, + }); + + static BoxDecorationDto from(BoxDecoration decoration) { + return BoxDecorationDto( + color: ColorDto.maybeFrom(decoration.color), + border: BoxBorderDto.maybeFrom(decoration.border), + borderRadius: BorderRadiusGeometryDto.maybeFrom(decoration.borderRadius), + gradient: GradientDto.maybeFrom(decoration.gradient), + boxShadow: decoration.boxShadow?.map(BoxShadowDto.from).toList(), + shape: decoration.shape, + ); + } + + static BoxDecorationDto? maybeFrom(BoxDecoration? decoration) { + return decoration == null ? null : from(decoration); + } + + @override + BoxDecoration resolve(MixData mix) { + return BoxDecoration( + color: color?.resolve(mix), + border: border?.resolve(mix), + borderRadius: borderRadius?.resolve(mix), + boxShadow: boxShadow?.map((e) => e.resolve(mix)).toList(), + gradient: gradient?.resolve(mix), + shape: shape ?? BoxShape.rectangle, + ); + } + + @override + BoxDecorationDto merge(BoxDecorationDto? other) { + if (other == null) return this; + + return BoxDecorationDto( + color: color?.merge(other.color) ?? other.color, + border: border?.merge(other.border) ?? other.border, + borderRadius: + borderRadius?.merge(other.borderRadius) ?? other.borderRadius, + gradient: gradient?.merge(other.gradient) ?? other.gradient, + boxShadow: boxShadow?.merge(other.boxShadow) ?? other.boxShadow, + ); + } + + @override + List get props => + [color, border, borderRadius, gradient, boxShadow, shape]; +} + +@immutable +class ShapeDecorationDto extends DecorationDto { + final ColorDto? color; + final ShapeBorder? shape; + final GradientDto? gradient; + final List? shadows; + + const ShapeDecorationDto({ + this.color, + this.shape, + this.gradient, + this.shadows, + }); + + static ShapeDecorationDto from(ShapeDecoration decoration) { + return ShapeDecorationDto( + color: ColorDto.maybeFrom(decoration.color), + shape: decoration.shape, + gradient: GradientDto.maybeFrom(decoration.gradient), + shadows: decoration.shadows?.map(BoxShadowDto.from).toList(), + ); + } + + static ShapeDecorationDto? maybeFrom(ShapeDecoration? decoration) { + return decoration == null ? null : from(decoration); + } + + @override + ShapeDecoration resolve(MixData mix) { + return ShapeDecoration( + color: color?.resolve(mix), + gradient: gradient?.resolve(mix), + shadows: shadows?.map((e) => e.resolve(mix)).toList(), + shape: shape ?? const RoundedRectangleBorder(), + ); + } + + @override + ShapeDecorationDto merge(ShapeDecorationDto? other) { + if (other == null) return this; + + return ShapeDecorationDto( + color: other.color ?? color, + shape: other.shape ?? shape, + gradient: other.gradient ?? gradient, + shadows: other.shadows ?? shadows, + ); + } + + @override + List get props => [color, shape, gradient, shadows]; +} diff --git a/lib/src/attributes/decoration/decoration_util.dart b/lib/src/attributes/decoration/decoration_util.dart new file mode 100644 index 000000000..0bd61c2d9 --- /dev/null +++ b/lib/src/attributes/decoration/decoration_util.dart @@ -0,0 +1,168 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../core/extensions/values_ext.dart'; +import '../border/border_dto.dart'; +import '../border/border_radius_dto.dart'; +import '../border/border_radius_util.dart'; +import '../border/border_util.dart'; +import '../color/color_dto.dart'; +import '../color/color_util.dart'; +import '../gradient/gradient_dto.dart'; +import '../gradient/gradient_util.dart'; +import '../scalars/scalar_util.dart'; +import '../shadow/shadow_dto.dart'; +import '../shadow/shadow_util.dart'; +import 'decoration_dto.dart'; + +class DecorationUtility + extends DtoUtility { + const DecorationUtility(super.builder) + : super(valueToDto: DecorationDto.from); + + BoxDecorationUtility get box { + return BoxDecorationUtility((BoxDecorationDto boxDecoration) { + return builder(boxDecoration); + }); + } + + ShapeDecorationUtility get shape { + return ShapeDecorationUtility((ShapeDecorationDto shapeDecoration) { + return builder(shapeDecoration); + }); + } +} + +class BoxDecorationUtility + extends DtoUtility { + const BoxDecorationUtility(super.builder) + : super(valueToDto: BoxDecorationDto.from); + + T _only({ + ColorDto? color, + BoxBorderDto? border, + BorderRadiusGeometryDto? borderRadius, + GradientDto? gradient, + List? boxShadow, + BoxShape? shape, + }) { + return builder( + BoxDecorationDto( + color: color, + border: border, + borderRadius: borderRadius, + gradient: gradient, + boxShadow: boxShadow, + shape: shape, + ), + ); + } + + ColorUtility get color { + return ColorUtility((ColorDto color) => _only(color: color)); + } + + BoxBorderUtility get border { + return BoxBorderUtility((border) => _only(border: border)); + } + + BorderRadiusGeometryUtility get borderRadius { + return BorderRadiusGeometryUtility( + (borderRadius) => _only(borderRadius: borderRadius), + ); + } + + BoxShapeUtility get shape { + return BoxShapeUtility((BoxShape shape) => _only(shape: shape)); + } + + ElevationUtility get elevation { + return ElevationUtility((List boxShadow) { + return _only(boxShadow: boxShadow); + }); + } + + GradientUtility get gradient { + return GradientUtility((GradientDto gradient) { + return _only(gradient: gradient); + }); + } + + BoxShadowListUtility get boxShadow { + return BoxShadowListUtility((List boxShadow) { + return _only(boxShadow: boxShadow); + }); + } + + T call({ + Color? color, + BoxBorder? border, + BorderRadiusGeometry? borderRadius, + Gradient? gradient, + List? boxShadow, + BoxShape? shape, + }) { + return _only( + color: color?.toDto(), + border: border?.toDto(), + borderRadius: borderRadius?.toDto(), + gradient: gradient?.toDto(), + boxShadow: boxShadow?.toDto(), + shape: shape, + ); + } +} + +class ShapeDecorationUtility + extends DtoUtility { + const ShapeDecorationUtility(super.builder) + : super(valueToDto: ShapeDecorationDto.from); + + T _only({ + ColorDto? color, + GradientDto? gradient, + List? shadows, + ShapeBorder? shape, + }) { + return builder( + ShapeDecorationDto( + color: color, + shape: shape, + gradient: gradient, + shadows: shadows, + ), + ); + } + + ColorUtility get color { + return ColorUtility((ColorDto color) => _only(color: color)); + } + + GradientUtility get gradient { + return GradientUtility((GradientDto gradient) => _only(gradient: gradient)); + } + + BoxShadowListUtility get shadows { + return BoxShadowListUtility( + (List shadows) => _only(shadows: shadows), + ); + } + + ShapeBorderUtility get shape { + return ShapeBorderUtility((ShapeBorder shape) => _only(shape: shape)); + } + + T call({ + Color? color, + Gradient? gradient, + List? shadows, + ShapeBorder? shape, + }) { + return _only( + color: color?.toDto(), + gradient: gradient?.toDto(), + shadows: shadows?.map((e) => e.toDto()).toList(), + shape: shape, + ); + } +} diff --git a/lib/src/attributes/decoration_attribute.dart b/lib/src/attributes/decoration_attribute.dart deleted file mode 100644 index 58ea9c8e6..000000000 --- a/lib/src/attributes/decoration_attribute.dart +++ /dev/null @@ -1,125 +0,0 @@ -// ignore_for_file: no_leading_underscores_for_local_identifiers - -import 'package:flutter/material.dart'; - -import '../factory/mix_provider_data.dart'; -import 'attribute.dart'; -import 'border/border_attribute.dart'; -import 'border/border_radius_attribute.dart'; -import 'color_attribute.dart'; -import 'scalar_attribute.dart'; -import 'shadow_attribute.dart'; - -@immutable -abstract class DecorationAttribute - extends VisualAttribute { - const DecorationAttribute(); - - @override - DecorationAttribute merge(covariant DecorationAttribute? other); - - @override - T resolve(MixData mix); -} - -@immutable -class BoxDecorationAttribute extends DecorationAttribute { - final ColorAttribute? color; - final BoxBorderAttribute? border; - final BorderRadiusGeometryAttribute? borderRadius; - final GradientAttribute? gradient; - final List? boxShadow; - final BoxShapeAttribute? shape; - const BoxDecorationAttribute({ - this.border, - this.borderRadius, - this.gradient, - this.boxShadow, - this.color, - this.shape, - }); - - @override - BoxDecorationAttribute merge(BoxDecorationAttribute? other) { - if (other == null) return this; - - return BoxDecorationAttribute( - border: border?.merge(other.border) ?? other.border, - borderRadius: - borderRadius?.merge(other.borderRadius) ?? other.borderRadius, - gradient: gradient?.merge(other.gradient) ?? other.gradient, - boxShadow: mergeMergeableList(boxShadow, other.boxShadow), - color: color?.merge(other.color) ?? other.color, - shape: other.shape ?? shape, - ); - } - - @override - BoxDecoration resolve(MixData mix) { - return BoxDecoration( - color: color?.resolve(mix), - border: border?.resolve(mix), - borderRadius: borderRadius?.resolve(mix), - boxShadow: boxShadow?.map((e) => e.resolve(mix)).toList(), - gradient: gradient?.resolve(mix), - ); - } - - @override - get props => [border, borderRadius, gradient, boxShadow, color, shape]; -} - -@immutable -class ShapeDecorationAttribute extends DecorationAttribute { - // The color to fill in the background of the shape. - final ColorAttribute? color; - - // The shape of the box. - final ShapeBorder? shape; - // A gradient to use when filling the box. - final GradientAttribute? gradient; - - // Shadows cast by this box behind the box. - final List? boxShadow; - - const ShapeDecorationAttribute({ - this.color, - this.shape, - this.gradient, - this.boxShadow, - }); - - @override - ShapeDecorationAttribute merge(ShapeDecorationAttribute? other) { - if (other == null) return this; - - return ShapeDecorationAttribute( - color: color?.merge(other.color) ?? other.color, - shape: other.shape ?? shape, - gradient: gradient?.merge(other.gradient) ?? other.gradient, - boxShadow: mergeMergeableList(boxShadow, other.boxShadow), - ); - } - - @override - ShapeDecoration resolve(MixData mix) { - return ShapeDecoration( - color: color?.resolve(mix), - gradient: gradient?.resolve(mix), - shadows: boxShadow?.map((e) => e.resolve(mix)).toList(), - shape: shape ?? const RoundedRectangleBorder(), - ); - } - - @override - List get props => [color, shape, gradient, boxShadow]; -} - -class ForegroundDecorationAttribute - extends ScalarAttribute { - const ForegroundDecorationAttribute(super.value); - - @override - ForegroundDecorationAttribute create(value) => - ForegroundDecorationAttribute(value); -} diff --git a/lib/src/attributes/edge_insets_attribute.dart b/lib/src/attributes/edge_insets_attribute.dart deleted file mode 100644 index 64046992c..000000000 --- a/lib/src/attributes/edge_insets_attribute.dart +++ /dev/null @@ -1,100 +0,0 @@ -// ignore_for_file: avoid-shadowing - -import 'package:flutter/material.dart'; - -import '../factory/mix_provider_data.dart'; -import 'attribute.dart'; - -@immutable -abstract class EdgeInsetsGeometryAttribute - extends VisualAttribute { - final double? top; - final double? bottom; - final double? left; - final double? right; - - // Directional - final double? start; - final double? end; - - const EdgeInsetsGeometryAttribute({ - this.top, - this.bottom, - this.left, - this.right, - this.start, - this.end, - }); - - @override - EdgeInsetsGeometryAttribute merge( - covariant EdgeInsetsGeometryAttribute? other, - ); - - @override - T resolve(MixData mix); - - @override - get props => [top, bottom, left, right, start, end]; -} - -@immutable -class EdgeInsetsAttribute extends EdgeInsetsGeometryAttribute { - const EdgeInsetsAttribute({ - super.top, - super.bottom, - super.left, - super.right, - }); - - @override - EdgeInsetsAttribute merge(EdgeInsetsAttribute? other) { - return EdgeInsetsAttribute( - top: other?.top ?? top, - bottom: other?.bottom ?? bottom, - left: other?.left ?? left, - right: other?.right ?? right, - ); - } - - @override - EdgeInsets resolve(MixData mix) { - return EdgeInsets.only( - left: left ?? 0, - top: top ?? 0, - right: right ?? 0, - bottom: bottom ?? 0, - ); - } -} - -@immutable -class EdgeInsetsDirectionalAttribute - extends EdgeInsetsGeometryAttribute { - const EdgeInsetsDirectionalAttribute({ - super.top, - super.bottom, - super.start, - super.end, - }); - - @override - EdgeInsetsDirectionalAttribute merge(EdgeInsetsDirectionalAttribute? other) { - return EdgeInsetsDirectionalAttribute( - top: other?.top ?? top, - bottom: other?.bottom ?? bottom, - start: other?.start ?? start, - end: other?.end ?? end, - ); - } - - @override - EdgeInsetsDirectional resolve(MixData mix) { - return EdgeInsetsDirectional.only( - start: start ?? 0, - top: top ?? 0, - end: end ?? 0, - bottom: bottom ?? 0, - ); - } -} diff --git a/lib/src/attributes/gradient/gradient_attribute.dart b/lib/src/attributes/gradient/gradient_attribute.dart new file mode 100644 index 000000000..6c95a7afa --- /dev/null +++ b/lib/src/attributes/gradient/gradient_attribute.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; +import 'gradient_dto.dart'; + +/// A class representing a gradient attribute. +/// +/// This attribute holds a [GradientDto] object, which represents a gradient in Flutter. +/// It extends the [ResolvableAttribute] class and implements the [merge] and [resolve] methods. +@immutable +class GradientAttribute + extends ResolvableAttribute { + final GradientDto value; + + /// Creates a new [GradientAttribute] with the given [value]. + const GradientAttribute(this.value); + + /// Checks if this [GradientAttribute] can be merged with another [GradientAttribute]. + /// + /// Two attributes can be merged if they have the same runtime type. + bool _canMerge(GradientAttribute other) { + return value.runtimeType == other.value.runtimeType; + } + + /// Merges this [GradientAttribute] with another [GradientAttribute]. + /// + /// If the [other] attribute is null, this method returns this attribute. + /// Otherwise, it checks if the two attributes can be merged. + /// If they can be merged, it creates a new [GradientAttribute] with the merged value. + /// Otherwise, it returns this attribute. + @override + GradientAttribute merge(GradientAttribute? other) { + if (other == null) return this; + + return _canMerge(other) + ? GradientAttribute(value.merge(other.value)) + : this; + } + + @override + + /// Resolves this [GradientAttribute] using the given [mix] data. + /// + /// It resolves the [value] of this attribute using the [mix] data and returns the resulting [Gradient]. + Gradient resolve(MixData mix) => value.resolve(mix); + + @override + + /// Returns the list of properties that define this [GradientAttribute]. + List get props => [value]; +} diff --git a/lib/src/attributes/gradient/gradient_dto.dart b/lib/src/attributes/gradient/gradient_dto.dart new file mode 100644 index 000000000..745a71bdb --- /dev/null +++ b/lib/src/attributes/gradient/gradient_dto.dart @@ -0,0 +1,404 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../core/extensions/iterable_ext.dart'; +import '../../factory/mix_provider_data.dart'; +import '../color/color_dto.dart'; + +/// Abstract class representing a [Dto] Data transfer object for a gradient. +/// It provides common properties and methods for different types of gradients. +/// +/// This is used to allow for resolvable value tokens, and also the correct merge and combining behavior. +/// It extends the [Dto] class and implements the [resolve] and [merge] methods. +/// It also overrides the [props] getter to provide a list of properties used for equality comparison. +/// +/// See also: +/// * [GradientDto], which is the base class for this class. +/// * [LinearGradientDto], which is a type of gradient DTO. +/// * [RadialGradientDto], which is a type of gradient DTO. +/// * [SweepGradientDto], which is a type of gradient DTO. +/// * [Gradient], which is the Flutter counterpart of this class. +@immutable +abstract class GradientDto extends Dto + with Mergeable> { + /// The list of stops for the gradient. + final List? stops; + + /// The list of color DTOs for the gradient. + final List? colors; + + /// The transform applied to the gradient. + final GradientTransform? transform; + + /// Constructs a [GradientDto] with the given [stops], [colors], and [transform]. + const GradientDto({this.stops, this.colors, this.transform}); + + /// Creates a [GradientDto] from a given [gradient]. + /// Throws an [UnimplementedError] if the gradient type is unknown. + static GradientDto from(Gradient gradient) { + if (gradient is LinearGradient) { + return LinearGradientDto.from(gradient); + } + if (gradient is RadialGradient) { + return RadialGradientDto.from(gradient); + } + if (gradient is SweepGradient) { + return SweepGradientDto.from(gradient); + } + + throw UnimplementedError('Unknown gradient type: $gradient'); + } + + /// Creates a [GradientDto] from a given [gradient]. + /// + /// Utility method for internal implementation + /// Returns null if the gradient is null. + static GradientDto? maybeFrom(Gradient? gradient) { + return gradient == null ? null : from(gradient); + } + + /// Resolves the gradient DTO into a concrete gradient based on the given [mix] data. + @override + T resolve(MixData mix); + + /// Merges this gradient DTO with another [other] gradient DTO. + @override + GradientDto merge(covariant GradientDto? other); + + /// The list of properties used for equality comparison. + @override + get props => [stops, colors, transform]; +} + +/// A data transfer object [Dto] representing a linear gradient. +/// +/// This DTO extends the [GradientDto] class and provides additional properties +/// specific to linear gradients, such as the [begin] and [end] alignment points, +/// [tileMode], and [transform]. +/// +/// Linear gradients can be created using the [LinearGradientDto.from] method, +/// which takes a [LinearGradient] object and returns a corresponding +/// [LinearGradientDto] instance. +/// +/// Linear gradients can also be resolved using the [resolve] method, which +/// takes a [MixData] object and returns a resolved [LinearGradient] instance. +/// +/// Linear gradients can be merged using the [merge] method, which takes +/// another [LinearGradientDto] object and returns a new [LinearGradientDto] +/// instance with merged properties. If the provided [LinearGradientDto] is null, +/// the merge operation returns the original [LinearGradientDto] instance. +/// +/// Example usage: +/// ```dart +/// final gradient1 = LinearGradientDto( +/// begin: Alignment.topLeft, +/// end: Alignment.bottomRight, +/// colors: [Colors.red, Colors.blue], +/// stops: [0.0, 1.0], +/// ); +/// +/// final gradient2 = LinearGradientDto( +/// begin: Alignment.centerLeft, +/// end: Alignment.centerRight, +/// colors: [Colors.green, Colors.yellow], +/// stops: [0.0, 1.0], +/// ); +/// +/// final mergedGradient = gradient1.merge(gradient2); +/// ``` +/// See also: +/// * [LinearGradient], which is the Flutter counterpart of this class. +/// * [GradientDto], which is the base class for this class. +/// * [RadialGradientDto], which is another type of gradient DTO. +/// * [SweepGradientDto], which is another type of gradient DTO. +@immutable +class LinearGradientDto extends GradientDto { + final AlignmentGeometry? begin; + final AlignmentGeometry? end; + + final TileMode? tileMode; + + const LinearGradientDto({ + this.begin, + this.end, + this.tileMode, + super.transform, + super.colors, + super.stops, + }); + + static LinearGradientDto from(LinearGradient gradient) { + return LinearGradientDto( + begin: gradient.begin, + end: gradient.end, + tileMode: gradient.tileMode, + transform: gradient.transform, + colors: gradient.colors.map(ColorDto.new).toList(), + stops: gradient.stops, + ); + } + + static LinearGradientDto? maybeFrom(LinearGradient? gradient) { + return gradient == null ? null : from(gradient); + } + + @override + LinearGradient resolve(MixData mix) { + return LinearGradient( + begin: begin ?? Alignment.centerLeft, + end: end ?? Alignment.centerRight, + colors: colors?.map((e) => e.resolve(mix)).toList() ?? [], + stops: stops?.map((e) => e).toList(), + tileMode: tileMode ?? TileMode.clamp, + transform: transform, + ); + } + + @override + LinearGradientDto merge(LinearGradientDto? other) { + if (other == null) return this; + + return LinearGradientDto( + begin: other.begin ?? begin, + end: other.end ?? end, + tileMode: other.tileMode ?? tileMode, + transform: other.transform ?? transform, + colors: colors?.merge(other.colors), + stops: stops?.merge(other.stops), + ); + } + + @override + List get props => [begin, end, colors, stops, tileMode, transform]; +} + +/// A data transfer object [Dto] representing a radial gradient. +/// +/// This DTO extends the [GradientDto] class and provides additional properties +/// specific to radial gradients, such as the [center], [radius], [focalRadius], +/// [focal], and [tileMode]. +/// +/// Radial gradients can be created using the [RadialGradientDto.from] method, +/// which takes a [RadialGradient] object and returns a corresponding +/// [RadialGradientDto] instance. +/// +/// Radial gradients can also be resolved using the [resolve] method, which +/// takes a [MixData] object and returns a resolved [RadialGradient] instance. +/// +/// Radial gradients can be merged using the [merge] method, which takes +/// another [RadialGradientDto] object and returns a new [RadialGradientDto] +/// instance with merged properties. If the provided [RadialGradientDto] is null, +/// the merge operation returns the original [RadialGradientDto] instance. +/// +/// Example usage: +/// +/// ```dart +/// final gradient1 = RadialGradientDto( +/// center: Alignment.center, +/// radius: 0.5, +/// colors: [Colors.red, Colors.blue], +/// stops: [0.0, 1.0], +/// ); +/// +/// final gradient2 = RadialGradientDto( +/// center: Alignment.center, +/// radius: 0.5, +/// colors: [Colors.green, Colors.yellow], +/// stops: [0.0, 1.0], +/// ); +/// +/// final mergedGradient = gradient1.merge(gradient2); +/// ``` +/// +/// See also: +/// * [RadialGradient], which is the Flutter counterpart of this class. +/// * [GradientDto], which is the base class for this class. +/// * [LinearGradientDto], which is another type of gradient DTO. +/// * [SweepGradientDto], which is another type of gradient DTO. +/// +@immutable +class RadialGradientDto extends GradientDto { + final AlignmentGeometry? center; + final double? radius; + + // focalRadius + final TileMode? tileMode; + final AlignmentGeometry? focal; + + final double? focalRadius; + + const RadialGradientDto({ + this.center, + this.radius, + this.tileMode, + this.focal, + this.focalRadius, + super.transform, + super.colors, + super.stops, + }); + + static RadialGradientDto from(RadialGradient gradient) { + return RadialGradientDto( + center: gradient.center, + radius: gradient.radius, + tileMode: gradient.tileMode, + focal: gradient.focal, + focalRadius: gradient.focalRadius, + transform: gradient.transform, + colors: gradient.colors.map(ColorDto.new).toList(), + stops: gradient.stops, + ); + } + + static RadialGradientDto? maybeFrom(RadialGradient? gradient) { + return gradient == null ? null : from(gradient); + } + + @override + RadialGradient resolve(MixData mix) { + return RadialGradient( + center: center ?? Alignment.center, + radius: radius ?? 0.5, + colors: colors?.map((e) => e.resolve(mix)).toList() ?? [], + stops: stops?.map((e) => e).toList(), + tileMode: tileMode ?? TileMode.clamp, + focal: focal, + focalRadius: focalRadius ?? 0.0, + transform: transform, + ); + } + + @override + RadialGradientDto merge(RadialGradientDto? other) { + if (other == null) return this; + + return RadialGradientDto( + center: center, + radius: radius ?? other.radius, + tileMode: other.tileMode ?? tileMode, + focal: focal, + focalRadius: other.focalRadius ?? focalRadius, + transform: other.transform ?? transform, + colors: colors?.merge(other.colors), + stops: stops?.merge(other.stops), + ); + } + + @override + List get props => + [center, radius, colors, stops, tileMode, focal, transform, focalRadius]; +} + +/// A data transfer object [Dto] representing a sweep gradient. +/// +/// This DTO extends the [GradientDto] class and provides additional properties +/// specific to sweep gradients, such as the [center], [startAngle], [endAngle], +/// and [tileMode]. +/// +/// Sweep gradients can be created using the [SweepGradientDto.from] method, +/// which takes a [SweepGradient] object and returns a corresponding +/// [SweepGradientDto] instance. +/// +/// Sweep gradients can also be resolved using the [resolve] method, which +/// takes a [MixData] object and returns a resolved [SweepGradient] instance. +/// +/// Sweep gradients can be merged using the [merge] method, which takes +/// another [SweepGradientDto] object and returns a new [SweepGradientDto] +/// instance with merged properties. If the provided [SweepGradientDto] is null, +/// the merge operation returns the original [SweepGradientDto] instance. +/// +/// Example usage: +/// +/// ```dart +/// final gradient1 = SweepGradientDto( +/// center: Alignment.center, +/// startAngle: 0.0, +/// endAngle: 0.5, +/// colors: [Colors.red, Colors.blue], +/// stops: [0.0, 1.0], +/// ); +/// +/// final gradient2 = SweepGradientDto( +/// center: Alignment.center, +/// startAngle: 0.0, +/// endAngle: 0.5, +/// colors: [Colors.green, Colors.yellow], +/// stops: [0.0, 1.0], +/// ); +/// +/// final mergedGradient = gradient1.merge(gradient2); +/// ``` +/// +/// See also: +/// * [SweepGradient], which is the Flutter counterpart of this class. +/// * [GradientDto], which is the base class for this class. +/// * [LinearGradientDto], which is another type of gradient DTO. +/// * [RadialGradientDto], which is another type of gradient DTO. + +@immutable +class SweepGradientDto extends GradientDto { + final AlignmentGeometry? center; + final double? startAngle; + final double? endAngle; + + final TileMode? tileMode; + + const SweepGradientDto({ + this.center, + this.startAngle, + this.endAngle, + this.tileMode, + super.transform, + super.colors, + super.stops, + }); + + static SweepGradientDto from(SweepGradient gradient) { + return SweepGradientDto( + center: gradient.center, + startAngle: gradient.startAngle, + endAngle: gradient.endAngle, + tileMode: gradient.tileMode, + transform: gradient.transform, + colors: gradient.colors.map(ColorDto.new).toList(), + stops: gradient.stops, + ); + } + + static SweepGradientDto? maybeFrom(SweepGradient? gradient) { + return gradient == null ? null : from(gradient); + } + + @override + SweepGradient resolve(MixData mix) { + return SweepGradient( + center: center ?? Alignment.center, + startAngle: startAngle ?? 0.0, + endAngle: endAngle ?? 0.0, + colors: colors?.map((e) => e.resolve(mix)).toList() ?? [], + stops: stops?.map((e) => e).toList(), + tileMode: tileMode ?? TileMode.clamp, + transform: transform, + ); + } + + @override + SweepGradientDto merge(SweepGradientDto? other) { + if (other == null) return this; + + return SweepGradientDto( + center: other.center ?? center, + startAngle: startAngle ?? other.startAngle, + endAngle: endAngle ?? other.endAngle, + tileMode: other.tileMode ?? tileMode, + transform: other.transform ?? transform, + colors: colors?.merge(other.colors), + stops: stops?.merge(other.stops), + ); + } + + @override + List get props => + [center, startAngle, endAngle, colors, stops, tileMode, transform]; +} diff --git a/lib/src/attributes/gradient/gradient_util.dart b/lib/src/attributes/gradient/gradient_util.dart new file mode 100644 index 000000000..8a04f693c --- /dev/null +++ b/lib/src/attributes/gradient/gradient_util.dart @@ -0,0 +1,676 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../core/extensions/values_ext.dart'; +import '../color/color_dto.dart'; +import '../scalars/scalar_util.dart'; +import 'gradient_attribute.dart'; +import 'gradient_dto.dart'; + +/// A utility class for working with gradients. +/// +/// This class provides convenient methods for creating different types of gradients, +/// such as radial gradients, linear gradients, and sweep gradients. +/// It also provides a method for converting a generic [Gradient] object to a specific type [T]. +/// +/// Accepts a [builder] function that takes a [GradientDto] and returns an object of type [T]. +/// +/// Example usage: +/// ```dart +/// final gradient = GradientUtility(builder); +/// +/// final linearGradient = gradient.linear( +/// begin: Alignment.topLeft, +/// end: Alignment.bottomRight, +/// colors: [Colors.red, Colors.blue], +/// ); +/// +/// final radialGradient = gradient.radial( +/// center: Alignment.center, +/// radius: 0.5, +/// colors: [Colors.red, Colors.blue], +/// ); +/// +/// final sweepGradient = gradient.sweep( +/// center: Alignment.center, +/// startAngle: 0.0, +/// endAngle: 0.5, +/// colors: [Colors.red, Colors.blue], +/// ); +/// +/// +/// final gradientAttribute = gradient.as(linearGradient); +/// ``` +class GradientUtility + extends MixUtility { + static const selfBuilder = GradientUtility(GradientAttribute.new); + + const GradientUtility(super.builder); + + /// Returns a [RadialGradientUtility] for creating radial gradients. + RadialGradientUtility get radial { + return RadialGradientUtility(builder); + } + + /// Returns a [LinearGradientUtility] for creating linear gradients. + LinearGradientUtility get linear { + return LinearGradientUtility(builder); + } + + /// Returns a [SweepGradientUtility] for creating sweep gradients. + SweepGradientUtility get sweep { + return SweepGradientUtility(builder); + } + + /// Converts a [Gradient] object to the specific type [T]. + /// + /// Throws an [UnimplementedError] if the given gradient type is not supported. + T as(Gradient gradient) { + if (gradient is RadialGradient) { + return radial.as(gradient); + } else if (gradient is LinearGradient) { + return linear.as(gradient); + } else if (gradient is SweepGradient) { + return sweep.as(gradient); + } + throw UnimplementedError( + 'Cannot create GradientAttribute from gradient of type ${gradient.runtimeType}', + ); + } +} + +/// Utility class for creating and manipulating [RadialGradient] attributes. +/// +/// Accepts a [builder] function that takes a [RadialGradientDto] and returns an object of type [T]. +/// +/// Example usage: +/// +/// ```dart +/// final gradient = RadialGradientUtility(builder); +/// final radialGradient = gradient( +/// center: Alignment.center, +/// radius: 0.5, +/// colors: [Colors.red, Colors.blue], +/// ); +/// ``` +/// +/// See also: +/// * [RadialGradient], a class for creating radial gradients. +/// * [RadialGradientDto], a class for creating radial gradient values. +/// * [GradientAttribute], a class for creating gradient attributes. +/// * [GradientDto], a class for creating gradient values. +@immutable +class RadialGradientUtility + extends MixUtility { + static const selfBuilder = RadialGradientUtility(GradientAttribute.new); + + const RadialGradientUtility(super.builder); + + /// Returns an [AlignmentUtility] for setting the center of the radial gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = RadialGradientUtility(builder); + /// final attribute = gradient.center.center(); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [RadialGradientDto] + /// that has a center value of Alignment.center. + /// + /// See also: + /// * [AlignmentUtility], a utility class for creating and manipulating [AlignmentGeometry] attributes. + AlignmentUtility get center { + return AlignmentUtility((center) => call(center: center)); + } + + /// Returns an [AlignmentUtility] for setting the focal point of the radial gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = RadialGradientUtility(builder); + /// final attribute = gradient.focal.center(); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [RadialGradientDto] + /// that has a focal value of Alignment.center. + /// + /// See also: + /// * [AlignmentUtility], a utility class for creating and manipulating [AlignmentGeometry] attributes. + AlignmentUtility get focal { + return AlignmentUtility((focal) => call(focal: focal)); + } + + /// Returns a [TileModeUtility] for setting the tile mode of the radial gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = RadialGradientUtility(builder); + /// final attribute = gradient.tileMode.clamp(); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [RadialGradientDto] + /// that has a tileMode value of TileMode.clamp. + /// + /// See also: + /// * [TileModeUtility], a utility class for creating and manipulating [TileMode] attributes. + TileModeUtility get tileMode { + return TileModeUtility((tileMode) => call(tileMode: tileMode)); + } + + /// Returns a [GradientTransformUtility] for setting the transform of the radial gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = RadialGradientUtility(builder); + /// final attribute = gradient.transform.rotate(pi / 4); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [RadialGradientDto] + /// that has a transform value of a Matrix4 rotation of pi / 4. + /// + /// See also: + /// * [GradientTransformUtility], a utility class for creating and manipulating [GradientTransform] attributes. + GradientTransformUtility get transform { + return GradientTransformUtility((transform) => call(transform: transform)); + } + + /// Method for setting the focal radius of the radial gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = RadialGradientUtility(builder); + /// final attribute = gradient.focalRadius(0.2); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [RadialGradientDto] + /// that has a focalRadius value of 0.2. + T focalRadius(double focalRadius) => call(focalRadius: focalRadius); + + /// Function for setting the radius of the radial gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = RadialGradientUtility(builder); + /// final attribute = gradient.radius(0.5); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [RadialGradientDto] + /// that has a radius value of 0.5. + T radius(double radius) => call(radius: radius); + + /// Sets the colors of the radial gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = RadialGradientUtility(builder); + /// final attribute = gradient.colors([Colors.red, Colors.blue]); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [RadialGradientDto] + /// that has a colors value of [Colors.red, Colors.blue]. + T colors(List colors) => call(colors: colors); + + /// Sets the stops of the radial gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = RadialGradientUtility(builder); + /// final attribute = gradient.stops([0.0, 1.0]); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [RadialGradientDto] + /// that has a stops value of [0.0, 1.0]. + T stops(List stops) => call(stops: stops); + + /// Converts the provided [RadialGradient] to the specified type [T]. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = RadialGradientUtility(builder); + /// final gradient = RadialGradient(center: Alignment.center, radius: 0.5); + /// final attribute = gradient.as(gradient); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [RadialGradientDto] + /// that has a center value of Alignment.center and a radius of 0.5. + T as(RadialGradient gradient) { + return builder(RadialGradientDto.from(gradient)); + } + + /// Creates a [RadialGradientDto] with the provided parameters and calls the [builder] function. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = RadialGradientUtility(builder); + /// final attribute = gradient(center: Alignment.center, radius: 0.5); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [RadialGradientDto] + /// that has a center value of Alignment.center and a radius of 0.5. + T call({ + List? colors, + List? stops, + AlignmentGeometry? center, + double? radius, + AlignmentGeometry? focal, + double? focalRadius, + TileMode? tileMode, + GradientTransform? transform, + }) { + final gradient = RadialGradientDto( + center: center, + radius: radius, + tileMode: tileMode, + focal: focal, + focalRadius: focalRadius, + transform: transform, + colors: colors?.map((e) => e.toDto()).toList(), + stops: stops, + ); + + return builder(gradient); + } +} + +/// Utility class for creating and manipulating [LinearGradient] attributes. +/// +/// Accepts a [builder] function that takes a [LinearGradientDto] and returns an object of type [T]. +/// +/// Example usage: +/// +// final gradient = LinearGradientUtility(builder); +// final linearGradient = gradient( +// begin: Alignment.topLeft, +// end: Alignment.bottomRight, +// colors: [Colors.red, Colors.blue], +// ); +/// +/// +/// See also: +/// * [LinearGradient], a class for creating linear gradients. +/// * [LinearGradientDto], a class for creating linear gradient values. +/// * [GradientAttribute], a class for creating gradient attributes. +/// * [GradientDto], a class for creating gradient values. +@immutable +class LinearGradientUtility + extends MixUtility { + static const selfBuilder = LinearGradientUtility(GradientAttribute.new); + + const LinearGradientUtility(super.builder); + + /// Returns an [AlignmentUtility] for setting the begin of the linear gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = LinearGradientUtility(builder); + /// final attribute = gradient.begin.topLeft(); + /// + /// ``` + /// Attribute now holds a [GradientAttribute] with a [LinearGradientDto] + /// that has a begin value of Alignment.topLeft. + /// + /// See also: + /// * [AlignmentUtility], a utility class for creating and manipulating [AlignmentGeometry] attributes. + /// * [AlignmentGeometry], a class for creating alignment values. + /// * [Alignment], a class for creating alignment values. + AlignmentUtility get begin { + return AlignmentUtility((AlignmentGeometry begin) { + return builder(LinearGradientDto(begin: begin)); + }); + } + + /// Returns an [AlignmentUtility] for setting the end of the linear gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = LinearGradientUtility(builder); + /// final attribute = gradient.end.bottomRight(); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [LinearGradientDto] + /// that has an end value of Alignment.bottomRight. + /// + /// See also: + /// * [AlignmentUtility], a utility class for creating and manipulating [AlignmentGeometry] attributes. + /// * [AlignmentGeometry], a class for creating alignment values. + /// * [Alignment], a class for creating alignment values. + AlignmentUtility get end { + return AlignmentUtility((AlignmentGeometry end) { + return builder(LinearGradientDto(end: end)); + }); + } + + /// Returns a [TileModeUtility] for setting the tile mode of the linear gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = LinearGradientUtility(builder); + /// final attribute = gradient.tileMode.clamp(); + /// + /// ``` + /// Attribute now holds a [GradientAttribute] with a [LinearGradientDto] + /// that has a tileMode value of TileMode.clamp. + /// + /// See also: + /// * [TileModeUtility], a utility class for creating and manipulating [TileMode] attributes. + /// * [TileMode], an enum for setting the tile mode of a gradient. + /// + TileModeUtility get tileMode { + return TileModeUtility( + (tileMode) => builder(LinearGradientDto(tileMode: tileMode)), + ); + } + + /// Returns a [GradientTransformUtility] for setting the transform of the linear gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = LinearGradientUtility(builder); + /// final attribute = gradient.transform.rotate(pi / 4); + /// + /// ``` + /// Attribute now holds a [GradientAttribute] with a [LinearGradientDto] + /// that has a transform value of a Matrix4 rotation of pi / 4. + /// + /// See also: + /// * [GradientTransformUtility], a utility class for creating and manipulating [GradientTransform] attributes. + /// * [Matrix4], a class for creating 4D matrices. + GradientTransformUtility get transform { + return GradientTransformUtility((GradientTransform transform) { + return builder(LinearGradientDto(transform: transform)); + }); + } + + /// Sets the colors of the linear gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = LinearGradientUtility(builder); + /// final attribute = gradient.colors([Colors.red, Colors.blue]); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [LinearGradientDto] + /// that has a colors value of [Colors.red, Colors.blue]. + /// + /// See also: + /// * [LinearGradient], a class for creating linear gradients. + /// * [LinearGradientDto], a class for creating linear gradient values. + T colors(List colors) => call(colors: colors); + + /// Sets the stops of the linear gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = LinearGradientUtility(builder); + /// final attribute = gradient.stops([0.0, 1.0]); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [LinearGradientDto] + /// that has a stops value of [0.0, 1.0]. + /// + /// See also: + /// * [LinearGradient], a class for creating linear gradients. + /// * [LinearGradientDto], a class for creating linear gradient values. + T stops(List stops) => builder(LinearGradientDto(stops: stops)); + + /// Converts the provided [LinearGradient] to the specified type [T]. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = LinearGradientUtility(builder); + /// final gradient = LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight); + /// + /// final attribute = gradient.as(gradient); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [LinearGradientDto] + /// that has a begin value of Alignment.topLeft and an end value of Alignment.bottomRight. + /// + /// See also: + /// * [LinearGradient], a class for creating linear gradients. + /// * [LinearGradientDto], a class for creating linear gradient values. + T as(LinearGradient gradient) { + return builder(LinearGradientDto.from(gradient)); + } + + /// Creates a [LinearGradientDto] with the provided parameters and calls the [builder] function. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = LinearGradientUtility(builder); + /// final attribute = gradient(begin: Alignment.topLeft, end: Alignment.bottomRight); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [LinearGradientDto] + /// that has a begin value of Alignment.topLeft and an end value of Alignment.bottomRight. + /// + /// See also: + /// * [LinearGradient], a class for creating linear gradients. + /// * [LinearGradientDto], a class for creating linear gradient values. + T call({ + List? colors, + List? stops, + AlignmentGeometry? begin, + AlignmentGeometry? end, + TileMode? tileMode, + GradientTransform? transform, + }) { + final gradient = LinearGradientDto( + begin: begin, + end: end, + tileMode: tileMode, + transform: transform, + colors: colors?.map(ColorDto.new).toList(), + stops: stops, + ); + + return builder(gradient); + } +} + +/// Utility class for creating and manipulating [SweepGradient] attributes. +/// +/// Accepts a [builder] function that takes a [SweepGradientDto] and returns an object of type [T]. +/// +/// Example usage: +/// +/// ```dart +/// final gradient = SweepGradientUtility(builder); +/// final sweepGradient = gradient( +/// center: Alignment.center, +/// startAngle: 0.0, +/// endAngle: pi, +/// colors: [Colors.red, Colors.blue], +/// ); +/// ``` +/// +/// See also: +/// * [SweepGradient], a class for creating sweep gradients. +/// * [SweepGradientDto], a class for creating sweep gradient values. +/// * [GradientAttribute], a class for creating gradient attributes. +/// * [GradientDto], a class for creating gradient values. +class SweepGradientUtility + extends MixUtility { + static const selfBuilder = SweepGradientUtility(GradientAttribute.new); + + const SweepGradientUtility(super.builder); + + /// Returns an [AlignmentUtility] for setting the center of the sweep gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = SweepGradientUtility(builder); + /// final attribute = gradient.center.center(); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [SweepGradientDto] + /// that has a center value of Alignment.center. + /// + /// See also: + /// * [AlignmentUtility], a utility class for creating and manipulating [AlignmentGeometry] attributes. + AlignmentUtility get center { + return AlignmentUtility((AlignmentGeometry center) { + return builder(SweepGradientDto(center: center)); + }); + } + + /// Returns a [TileModeUtility] for setting the tile mode of the sweep gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = SweepGradientUtility(builder); + /// final attribute = gradient.tileMode.clamp(); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [SweepGradientDto] + /// that has a tileMode value of TileMode.clamp. + /// + /// See also: + /// * [TileModeUtility], a utility class for creating and manipulating [TileMode] attributes. + TileModeUtility get tileMode { + return TileModeUtility((TileMode tileMode) { + return builder(SweepGradientDto(tileMode: tileMode)); + }); + } + + /// Returns a [GradientTransformUtility] for setting the transform of the sweep gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = SweepGradientUtility(builder); + /// final attribute = gradient.transform.rotate(pi / 4); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [SweepGradientDto] + /// that has a transform value of a Matrix4 rotation of pi / 4. + /// + /// See also: + /// * [GradientTransformUtility], a utility class for creating and manipulating [GradientTransform] attributes. + GradientTransformUtility get transform { + return GradientTransformUtility((GradientTransform transform) { + return builder(SweepGradientDto(transform: transform)); + }); + } + + /// Method for setting the end angle of the sweep gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = SweepGradientUtility(builder); + /// final attribute = gradient.endAngle(pi); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [SweepGradientDto] + /// that has an endAngle value of pi (3.14159...). + T endAngle(double endAngle) => call(endAngle: endAngle); + + /// Method for setting the start angle of the sweep gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = SweepGradientUtility(builder); + /// final attribute = gradient.startAngle(0.0); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [SweepGradientDto] + /// that has a startAngle value of 0.0. + T startAngle(double startAngle) => call(startAngle: startAngle); + + /// Sets the colors of the sweep gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = SweepGradientUtility(builder); + /// final attribute = gradient.colors([Colors.red, Colors.blue]); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [SweepGradientDto] + /// that has a colors value of [Colors.red, Colors.blue]. + T colors(List colors) => call(colors: colors); + + /// Sets the stops of the sweep gradient. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = SweepGradientUtility(builder); + /// final attribute = gradient.stops([0.0, 1.0]); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [SweepGradientDto] + /// that has a stops value of [0.0, 1.0]. + T stops(List stops) => call(stops: stops); + + /// Converts the provided [SweepGradient] to the specified type [T]. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = SweepGradientUtility(builder); + /// final gradient = SweepGradient(center: Alignment.center, startAngle: 0.0, endAngle: pi); + /// final attribute = gradient.as(gradient); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [SweepGradientDto] + /// that has a center value of Alignment.center, a startAngle of 0.0, and an endAngle of pi. + T as(SweepGradient gradient) { + return builder(SweepGradientDto.from(gradient)); + } + + /// Creates a [SweepGradientDto] with the provided parameters and calls the [builder] function. + /// + /// Example usage: + /// + /// ```dart + /// final gradient = SweepGradientUtility(builder); + /// final attribute = gradient(center: Alignment.center, startAngle: 0.0, endAngle: pi); + /// ``` + /// + /// Attribute now holds a [GradientAttribute] with a [SweepGradientDto] + /// that has a center value of Alignment.center, a startAngle of 0.0, and an endAngle of pi. + T call({ + List? colors, + List? stops, + AlignmentGeometry? center, + double? startAngle, + double? endAngle, + TileMode? tileMode, + GradientTransform? transform, + }) { + final gradient = SweepGradientDto( + center: center, + startAngle: startAngle, + endAngle: endAngle, + tileMode: tileMode, + transform: transform, + colors: colors?.map(ColorDto.new).toList(), + stops: stops, + ); + + return builder(gradient); + } +} diff --git a/lib/src/attributes/scalar_attribute.dart b/lib/src/attributes/scalar_attribute.dart deleted file mode 100644 index c43bf10d3..000000000 --- a/lib/src/attributes/scalar_attribute.dart +++ /dev/null @@ -1,240 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; - -import 'attribute.dart'; - -class AxisAttribute extends ScalarAttribute { - const AxisAttribute(super.value); - - @override - AxisAttribute create(value) => AxisAttribute(value); -} - -class GradientAttribute extends ScalarAttribute { - const GradientAttribute(super.value); - - @override - GradientAttribute create(value) => GradientAttribute(value); -} - -class MainAxisAlignmentAttribute - extends ScalarAttribute { - const MainAxisAlignmentAttribute(super.value); - - @override - MainAxisAlignmentAttribute create(value) => MainAxisAlignmentAttribute(value); -} - -class TransformAlignmentAttribute - extends ScalarAttribute { - const TransformAlignmentAttribute(super.value); - - @override - TransformAlignmentAttribute create(value) => - TransformAlignmentAttribute(value); -} - -class MainAxisSizeAttribute - extends ScalarAttribute { - const MainAxisSizeAttribute(super.value); - - @override - MainAxisSizeAttribute create(value) => MainAxisSizeAttribute(value); -} - -class CrossAxisAlignmentAttribute - extends ScalarAttribute { - const CrossAxisAlignmentAttribute(super.value); - - @override - CrossAxisAlignmentAttribute create(value) => - CrossAxisAlignmentAttribute(value); -} - -class VerticalDirectionAttribute - extends ScalarAttribute { - const VerticalDirectionAttribute(super.value); - - @override - VerticalDirectionAttribute create(value) => VerticalDirectionAttribute(value); -} - -class TextBaselineAttribute - extends ScalarAttribute { - const TextBaselineAttribute(super.value); - - @override - TextBaselineAttribute create(value) => TextBaselineAttribute(value); -} - -class ClipAttribute extends ScalarAttribute { - const ClipAttribute(super.value); - - @override - ClipAttribute create(value) => ClipAttribute(value); -} - -class ImageAlignmentAttribute - extends ScalarAttribute { - const ImageAlignmentAttribute(super.value); - - @override - ImageAlignmentAttribute create(value) => ImageAlignmentAttribute(value); -} - -class ImageScaleAttribute extends ScalarAttribute { - const ImageScaleAttribute(super.value); - - @override - ImageScaleAttribute create(value) => ImageScaleAttribute(value); -} - -class ImageFitAttribute extends ScalarAttribute { - const ImageFitAttribute(super.value); - - @override - ImageFitAttribute create(value) => ImageFitAttribute(value); -} - -class ImageRepeatAttribute - extends ScalarAttribute { - const ImageRepeatAttribute(super.value); - - @override - ImageRepeatAttribute create(value) => ImageRepeatAttribute(value); -} - -class ImageWidthAttribute extends ScalarAttribute { - const ImageWidthAttribute(super.value); - - @override - ImageWidthAttribute create(value) => ImageWidthAttribute(value); -} - -class ImageHeightAttribute - extends ScalarAttribute { - const ImageHeightAttribute(super.value); - - @override - ImageHeightAttribute create(value) => ImageHeightAttribute(value); -} - -class TextAlignAttribute - extends ScalarAttribute { - const TextAlignAttribute(super.value); - - @override - TextAlignAttribute create(value) => TextAlignAttribute(value); -} - -class TextDirectionAttribute - extends ScalarAttribute { - const TextDirectionAttribute(super.value); - - @override - TextDirectionAttribute create(value) => TextDirectionAttribute(value); -} - -class SoftWrapAttribute extends ScalarAttribute { - const SoftWrapAttribute(super.value); - - @override - SoftWrapAttribute create(value) => SoftWrapAttribute(value); -} - -class TextOverflowAttribute - extends ScalarAttribute { - const TextOverflowAttribute(super.value); - - @override - TextOverflowAttribute create(value) => TextOverflowAttribute(value); -} - -class TextScaleFactorAttribute - extends ScalarAttribute { - const TextScaleFactorAttribute(super.value); - - @override - TextScaleFactorAttribute create(value) => TextScaleFactorAttribute(value); -} - -class MaxLinesAttribute extends ScalarAttribute { - const MaxLinesAttribute(super.value); - - @override - MaxLinesAttribute create(value) => MaxLinesAttribute(value); -} - -class TextWidthBasisAttribute - extends ScalarAttribute { - const TextWidthBasisAttribute(super.value); - - @override - TextWidthBasisAttribute create(value) => TextWidthBasisAttribute(value); -} - -class TextHeightBehaviorAttribute - extends ScalarAttribute { - const TextHeightBehaviorAttribute(super.value); - - @override - TextHeightBehaviorAttribute create(value) => - TextHeightBehaviorAttribute(value); -} - -class IconSizeAttribute extends ScalarAttribute { - const IconSizeAttribute(super.value); - - @override - IconSizeAttribute create(value) => IconSizeAttribute(value); -} - -class BoxFitAttribute extends ScalarAttribute { - const BoxFitAttribute(super.value); - - @override - BoxFitAttribute create(value) => BoxFitAttribute(value); -} - -class StackFitAttribute extends ScalarAttribute { - const StackFitAttribute(super.value); - - @override - StackFitAttribute create(value) => StackFitAttribute(value); -} - -class FlexFitAttribute extends ScalarAttribute { - const FlexFitAttribute(super.value); - - @override - FlexFitAttribute create(value) => FlexFitAttribute(value); -} - -class TransformAttribute extends ScalarAttribute { - const TransformAttribute(super.value); - - @override - TransformAttribute create(value) => TransformAttribute(value); -} - -class BlendModeAttribute - extends ScalarAttribute { - const BlendModeAttribute(super.value); - - @override - BlendModeAttribute create(value) => BlendModeAttribute(value); -} - -class BoxShapeAttribute extends ScalarAttribute { - const BoxShapeAttribute(super.value); - - @override - BoxShapeAttribute create(value) => BoxShapeAttribute(value); -} - -class VisibleAttribute extends ScalarAttribute { - const VisibleAttribute(super.value); - - @override - VisibleAttribute create(value) => VisibleAttribute(value); -} diff --git a/lib/src/attributes/scalars/scalar_util.dart b/lib/src/attributes/scalars/scalar_util.dart new file mode 100644 index 000000000..3d415d180 --- /dev/null +++ b/lib/src/attributes/scalars/scalar_util.dart @@ -0,0 +1,761 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; + +typedef UtilityBuilder = ReturnType Function( + ParamType value, +); + +abstract class MixUtility { + final UtilityBuilder _builder; + const MixUtility(this._builder); + + static V selfBuilder(V value) => value; + + @protected + UtilityBuilder get builder => _builder; +} + +abstract class DtoUtility, + Value> extends MixUtility { + @protected + final UtilityBuilder valueToDto; + + const DtoUtility(super.builder, {required this.valueToDto}); + + Attr as(Value value) => _builder(valueToDto(value)); +} + +mixin CallableUtilityMixin + on MixUtility { + Attr call(Value value) => _builder(value); +} + +mixin CallableDtoUtilityMixin, + Value> on DtoUtility { + Attr call(Value value) => _builder(valueToDto(value)); +} + +abstract class ScalarUtility + extends MixUtility { + const ScalarUtility(super.builder); + + Return as(Param value) => _builder(value); + + Return call(Param value) => _builder(value); +} + +class AlignmentUtility + extends ScalarUtility { + const AlignmentUtility(super.builder); + + T topLeft() => builder(Alignment.topLeft); + T topCenter() => builder(Alignment.topCenter); + T topRight() => builder(Alignment.topRight); + T centerLeft() => builder(Alignment.centerLeft); + T center() => builder(Alignment.center); + T centerRight() => builder(Alignment.centerRight); + T bottomLeft() => builder(Alignment.bottomLeft); + T bottomCenter() => builder(Alignment.bottomCenter); + T bottomRight() => builder(Alignment.bottomRight); + T only(double? x, double? y, double? start) { + return start == null + ? builder(Alignment(x ?? 0, y ?? 0)) + : builder(AlignmentDirectional(start, y ?? 0)); + } +} + +/// Utility for creating `double` values. +/// +/// Includes predefined values such as zero and infinity. +/// +/// Example: +/// ```dart +/// final utility = DoubleUtility(builder); +/// final tenValue = utility(10); +/// ``` +abstract class DoubleUtility + extends ScalarUtility with CallableUtilityMixin { + const DoubleUtility(super.builder); +} + +/// Utility for Size values. Includes predefined values such as zero and infinity. +/// +/// Example: +/// ```dart +/// final utility = SizeUtility(builder); +/// final oneHundred = utility(100); +/// ``` +/// See also: +/// * [DoubleUtility] +abstract class SizingUtility + extends DoubleUtility { + const SizingUtility(super.builder); +} + +/// Utility for creating `int` values. +/// +/// Includes a predefined value for zero. +/// +/// Example: +/// ```dart +/// final utility = IntUtility(builder); +/// final zeroValue = utility.zero(); +/// final tenValue = utility(10); +/// // zeroValue is 0 +/// ``` +class IntUtility extends ScalarUtility + with CallableUtilityMixin { + const IntUtility(super.builder); + + T zero() => builder(0); +} + +/// Utility for creating `bool` values. +/// +/// Simplifies setting boolean values to true or false. +/// +/// Example: +/// ```dart +/// final boolUtility = BoolUtility(builder); +/// final enabled = boolUtility.on(); +/// final disabled = boolUtility.off(); +/// final boolValue = boolUtility(true); +/// ``` +class BoolUtility extends ScalarUtility + with CallableUtilityMixin { + const BoolUtility(super.builder); + + T on() => builder(true); + T off() => builder(false); +} + +/// Utility for setting `VerticalDirection` values. +/// +/// Useful for defining the direction of vertical arrangements. +/// +/// Example: +/// ```dart +/// final verticalDirection = VerticalDirectionUtility(builder); +/// +/// VerticalDirection.up: +/// final up = verticalDirection.up(); +/// +/// VerticalDirection.down: +/// final down = verticalDirection.down(); +/// ``` +/// See [VerticalDirection] for more information. +class VerticalDirectionUtility + extends ScalarUtility { + const VerticalDirectionUtility(super.builder); + T up() => builder(VerticalDirection.up); + T down() => builder(VerticalDirection.down); +} + +class BorderStyleUtility + extends ScalarUtility { + const BorderStyleUtility(super.builder); + T none() => builder(BorderStyle.none); + T solid() => builder(BorderStyle.solid); +} + +/// Utility for setting Clip`values. +/// +/// Useful for defining the clipping behavior of widgets. +/// Includes predefined values of `Clip` such as `none`, `hardEdge`, and `antiAlias`. +/// Example: +/// ```dart +/// final clip = ClipUtility(builder); +/// final noneClip = clip.none(); +/// final hardEdge = clip.hardEdge(); +/// final antiAlias = clip.antiAlias(); +/// ``` +/// See [Clip] for more information. +class ClipUtility extends ScalarUtility { + const ClipUtility(super.builder); + T antiAliasWithSaveLayer() => builder(Clip.antiAliasWithSaveLayer); + T none() => builder(Clip.none); + T hardEdge() => builder(Clip.hardEdge); + T antiAlias() => builder(Clip.antiAlias); +} + +/// Utility for setting `FlexFit` values. +/// +/// Useful for defining the fit of a widget within a flex layout. +/// Includes predefined values of `FlexFit` such as `tight` and `loose`. +/// Example: +/// ```dart +/// final flexFit = FlexFitUtility(builder); +/// final tight = flexFit.tight(); +/// final loose = flexFit.loose(); +/// ``` +/// See [FlexFit] for more information. +class FlexFitUtility + extends ScalarUtility { + const FlexFitUtility(super.builder); + T tight() => builder(FlexFit.tight); + T loose() => builder(FlexFit.loose); +} + +class TextHeightBehaviorUtility + extends ScalarUtility { + const TextHeightBehaviorUtility(super.builder); +} + +/// Utility for setting `ShapeBorder` values. +/// +/// Useful for defining the shape of widgets. +/// Includes subclasses of `ShapeBorder` such as `RoundedRectangleBorder`. +/// +/// Example: +/// +/// ```dart +/// final shapeBorder = ShapeBorderUtility(builder); +/// final roundedRectangle = shapeBorder.roundedRectangle(10); +/// ``` +/// +/// See [ShapeBorder] for more information. +class ShapeBorderUtility + extends ScalarUtility { + const ShapeBorderUtility(super.builder); + + T rounded(double radius) => builder( + RoundedRectangleBorder(borderRadius: BorderRadius.circular(radius)), + ); + + T circle() => builder(const CircleBorder()); + T stadium() => builder(const StadiumBorder()); + T beveled() => builder(const BeveledRectangleBorder()); +} + +/// Utility for setting `Axis` values. +/// +/// Useful for defining the direction of flex layouts. +/// Includes predefined values of `Axis` such as `horizontal` and `vertical`. +/// Example: +/// ```dart +/// final axis = AxisUtility(builder); +/// final horizontal = axis.horizontal(); +/// final vertical = axis.vertical(); +/// ``` +/// See [Axis] for more information. +class AxisUtility extends ScalarUtility { + const AxisUtility(super.builder); + T horizontal() => builder(Axis.horizontal); + T vertical() => builder(Axis.vertical); +} + +/// Utility for setting `StackFit` values. +/// +/// Useful for defining the fit of a widget within a stack layout. +/// Includes predefined values of `StackFit` such as `loose` and `expand`. +/// Example: +/// ```dart +/// final stackFit = StackFitUtility(builder); +/// final loose = stackFit.loose(); +/// final expand = stackFit.expand(); +/// ``` +/// See [StackFit] for more information. +class StackFitUtility + extends ScalarUtility { + const StackFitUtility(super.builder); + T loose() => builder(StackFit.loose); + T expand() => builder(StackFit.expand); + T passthrough() => builder(StackFit.passthrough); +} + +/// Utility for setting `TextDirection` values. +/// +/// Useful for defining the direction of text. +/// Includes predefined values of `TextDirection` such as `ltr` and `rtl`. +/// Example: +/// ```dart +/// final textDirection = TextDirectionUtility(builder); +/// final ltr = textDirection.ltr(); +/// final rtl = textDirection.rtl(); +/// ``` +/// See [TextDirection] for more information. +class TextDirectionUtility + extends ScalarUtility { + const TextDirectionUtility(super.builder); + T rtl() => builder(TextDirection.rtl); + T ltr() => builder(TextDirection.ltr); +} + +/// Utility for setting `TileMode` values. +/// +/// Useful for defining the tiling behavior of images. +/// Includes predefined values of `TileMode` such as `clamp` and `mirror`. +/// Example: +/// ```dart +/// final tileMode = TileModeUtility(builder); +/// final clamp = tileMode.clamp(); +/// final mirror = tileMode.mirror(); +/// ``` +/// See [TileMode] for more information. +class TileModeUtility + extends ScalarUtility { + const TileModeUtility(super.builder); + T clamp() => builder(TileMode.clamp); + T mirror() => builder(TileMode.mirror); + T repeat() => builder(TileMode.repeated); + T decal() => builder(TileMode.decal); +} + +/// Utility for setting `GradientTransform` values. +/// +/// Useful for defining the transformation of gradients. +/// Includes subclasses of `GradientTransform` such `GradientRotation`. +/// Example: +/// ```dart +/// final gradientTransform = GradientTransformUtility(builder); +/// final rotate90 = gradientTransform.rotate(90); +/// ``` +/// See [GradientTransform] for more information. + +class GradientTransformUtility + extends ScalarUtility { + const GradientTransformUtility(super.builder); + + T rotate(double radians) => builder(GradientRotation(radians)); +} + +/// Utility for setting `Matrix4` values. +/// +/// Useful for defining the transformation of widgets. +/// Example: +/// ```dart +/// final matrix4 = Matrix4Utility(builder); +/// final identity = matrix4.identity(); +/// final rotateX = matrix4.rotationX(0.5); +/// final rotateY = matrix4.rotationY(0.5); +/// final rotateZ = matrix4.rotationZ(0.5); +/// final translation = matrix4.translationValues(10, 10, 10); +/// final scale = matrix4.scale(2, 2, 2); +/// ``` +/// See [Matrix4] for more information. +class Matrix4Utility + extends ScalarUtility { + const Matrix4Utility(super.builder); + + T identity() => builder(Matrix4.identity()); + T rotationX(double radians) => builder(Matrix4.rotationX(radians)); + T rotationY(double radians) => builder(Matrix4.rotationY(radians)); + T rotationZ(double radians) => builder(Matrix4.rotationZ(radians)); + T translationValues(double x, double y, double z) => + builder(Matrix4.translationValues(x, y, z)); +} + +/// Utility for setting `MainAxisAlignment` values. +/// +/// Useful for defining the alignment of widgets within a flex layout. +/// Includes predefined values of `MainAxisAlignment` such as `start` and `end`. +/// Example: +/// ```dart +/// final mainAxisAlignment = MainAxisAlignmentUtility(builder); +/// final start = mainAxisAlignment.start(); +/// final end = mainAxisAlignment.end(); +/// ``` +/// See [MainAxisAlignment] for more information. +class MainAxisAlignmentUtility + extends ScalarUtility { + const MainAxisAlignmentUtility(super.builder); + T spaceBetween() => builder(MainAxisAlignment.spaceBetween); + T spaceAround() => builder(MainAxisAlignment.spaceAround); + T spaceEvenly() => builder(MainAxisAlignment.spaceEvenly); + + T start() => builder(MainAxisAlignment.start); + T end() => builder(MainAxisAlignment.end); + T center() => builder(MainAxisAlignment.center); +} + +/// Utility for setting `CrossAxisAlignment` values. +/// +/// Useful for defining the alignment of widgets within a flex layout. +/// Includes predefined values of `CrossAxisAlignment` such as `start` and `end`. +/// Example: +/// ```dart +/// final crossAxisAlignment = CrossAxisAlignmentUtility(builder); +/// final start = crossAxisAlignment.start(); +/// final end = crossAxisAlignment.end(); +/// ``` +/// See [CrossAxisAlignment] for more information. +class CrossAxisAlignmentUtility + extends ScalarUtility { + const CrossAxisAlignmentUtility(super.builder); + T start() => builder(CrossAxisAlignment.start); + T end() => builder(CrossAxisAlignment.end); + T center() => builder(CrossAxisAlignment.center); + T stretch() => builder(CrossAxisAlignment.stretch); + T baseline() => builder(CrossAxisAlignment.baseline); +} + +/// Utility for setting `MainAxisSize` values. +/// +/// Useful for defining the size of widgets within a flex layout. +/// Includes predefined values of `MainAxisSize` such as `min` and `max`. +/// Example: +/// ```dart +/// final mainAxisSize = MainAxisSizeUtility(builder); +/// final min = mainAxisSize.min(); +/// final max = mainAxisSize.max(); +/// ``` +/// See [MainAxisSize] for more information. +class MainAxisSizeUtility + extends ScalarUtility { + const MainAxisSizeUtility(super.builder); + T min() => builder(MainAxisSize.min); + T max() => builder(MainAxisSize.max); +} + +/// Utility for setting FontFamily names in other attributes +/// +/// Example: +/// ```dart +/// final fontFamily = FontFamilyUtility(builder); +/// final fontFamilyValue = fontFamily("Roboto"); +/// ``` +class FontFamilyUtility + extends ScalarUtility { + const FontFamilyUtility(super.builder); +} + +/// Utility for setting `ImageRepeat` values. +/// +/// Useful for defining the tiling behavior of images. +/// Includes predefined values of `ImageRepeat` such as `repeat` and `repeatX`. +/// Example: +/// ```dart +/// final imageRepeat = ImageRepeatUtility(builder); +/// final repeat = imageRepeat.repeat(); +/// final repeatX = imageRepeat.repeatX(); +/// final repeatY = imageRepeat.repeatY(); +/// final noRepeat = imageRepeat.noRepeat(); +/// ``` +/// See [ImageRepeat] for more information. +class ImageRepeatUtility + extends ScalarUtility { + const ImageRepeatUtility(super.builder); + T noRepeat() => builder(ImageRepeat.noRepeat); + T repeat() => builder(ImageRepeat.repeat); + T repeatX() => builder(ImageRepeat.repeatX); + T repeatY() => builder(ImageRepeat.repeatY); +} + +/// Utility for setting `Offset` values. +/// +/// Useful for defining the offset of widgets. +/// Example: +/// ```dart +/// final offset = OffsetUtility(builder); +/// final offsetValue = offset(10, 10); +/// ``` + +class OffsetUtility extends MixUtility { + const OffsetUtility(super.builder); + + T call(double dx, double dy) => builder(Offset(dx, dy)); +} + +class FontSizeUtility extends SizingUtility { + const FontSizeUtility(super.builder); +} + +/// Utility for setting `BoxFit` values. +/// +/// Useful for defining BoxFit values for widgets +/// Example: +/// ```dart +/// final boxFit = BoxFitUtility(builder); +/// final fill = boxFit.fill(); +/// final contain = boxFit.contain(); +/// final cover = boxFit.cover(); +/// final fitWidth = boxFit.fitWidth(); +/// final fitHeight = boxFit.fitHeight(); +/// final none = boxFit.none(); +/// final scaleDown = boxFit.scaleDown(); +/// ``` +/// See [BoxFit] for more information. +class BoxFitUtility extends ScalarUtility { + const BoxFitUtility(super.builder); + T fill() => builder(BoxFit.fill); + T contain() => builder(BoxFit.contain); + T cover() => builder(BoxFit.cover); + T fitWidth() => builder(BoxFit.fitWidth); + T fitHeight() => builder(BoxFit.fitHeight); + T none() => builder(BoxFit.none); + T scaleDown() => builder(BoxFit.scaleDown); +} + +/// Utility for setting `BlendMode` values. +/// +/// Useful for defining BlendMode values for widgets +/// Example: +/// ```dart +/// final blendMode = BlendModeUtility(builder); +/// final clear = blendMode.clear(); +/// final src = blendMode.src(); +/// ``` +/// +class BlendModeUtility + extends ScalarUtility { + const BlendModeUtility(super.builder); + + T clear() => builder(BlendMode.clear); + T src() => builder(BlendMode.src); + T dst() => builder(BlendMode.dst); + T srcOver() => builder(BlendMode.srcOver); + T dstOver() => builder(BlendMode.dstOver); + T srcIn() => builder(BlendMode.srcIn); + T dstIn() => builder(BlendMode.dstIn); + T srcOut() => builder(BlendMode.srcOut); + T dstOut() => builder(BlendMode.dstOut); + T srcATop() => builder(BlendMode.srcATop); + T dstATop() => builder(BlendMode.dstATop); + T xor() => builder(BlendMode.xor); + T plus() => builder(BlendMode.plus); + T modulate() => builder(BlendMode.modulate); + T screen() => builder(BlendMode.screen); + T overlay() => builder(BlendMode.overlay); + T darken() => builder(BlendMode.darken); + T lighten() => builder(BlendMode.lighten); + T colorDodge() => builder(BlendMode.colorDodge); + T colorBurn() => builder(BlendMode.colorBurn); + T hardLight() => builder(BlendMode.hardLight); + T softLight() => builder(BlendMode.softLight); + T difference() => builder(BlendMode.difference); + T exclusion() => builder(BlendMode.exclusion); + T multiply() => builder(BlendMode.multiply); + T hue() => builder(BlendMode.hue); + T saturation() => builder(BlendMode.saturation); + T color() => builder(BlendMode.color); + T luminosity() => builder(BlendMode.luminosity); +} + +/// Utility for setting `BoxShape` values. +/// +/// Useful for defining BoxShape values for widgets +/// +/// Example: +/// +/// ```dart +/// final boxShape = BoxShapeUtility(builder); +/// final circle = boxShape.circle(); +/// final rectangle = boxShape.rectangle(); +/// ``` +/// +/// See [BoxShape] for more information. +class BoxShapeUtility + extends ScalarUtility { + const BoxShapeUtility(super.builder); + T circle() => _builder(BoxShape.circle); + T rectangle() => _builder(BoxShape.rectangle); +} + +/// Utility for setting `FontWeight` values. +/// +/// Useful for defining FontWeight values for widgets +/// +/// Example: +/// +/// ```dart +/// final fontWeight = FontWeightUtility(builder); +/// final bold = fontWeight.bold(); +/// final normal = fontWeight.normal(); +/// ``` +/// +/// See [FontWeight] for more information. +class FontWeightUtility + extends ScalarUtility { + const FontWeightUtility(super.builder); + T bold() => _builder(FontWeight.bold); + T normal() => _builder(FontWeight.normal); +} + +/// Utility for setting `TextDecoration` values. +/// +/// Useful for defining TextDecoration values for widgets +/// +/// Example: +/// +/// ```dart +/// final textDecoration = TextDecorationUtility(builder); +/// final underline = textDecoration.underline(); +/// final overline = textDecoration.overline(); +/// final lineThrough = textDecoration.lineThrough(); +/// final none = textDecoration.none(); +/// ``` +/// +/// See [TextDecoration] for more information. +class TextDecorationUtility + extends ScalarUtility { + const TextDecorationUtility(super.builder); + + T underline() => _builder(TextDecoration.underline); + T overline() => _builder(TextDecoration.overline); + T lineThrough() => _builder(TextDecoration.lineThrough); + T none() => _builder(TextDecoration.none); +} + +/// Utility for setting `FontStyle` values. +/// +/// Useful for defining FontStyle values for widgets +/// +/// Example: +/// +/// ```dart +/// final fontStyle = FontStyleUtility(builder); +/// final italic = fontStyle.italic(); +/// final normal = fontStyle.normal(); +/// ``` +/// +/// See [FontStyle] for more information. +class FontStyleUtility + extends ScalarUtility { + const FontStyleUtility(super.builder); + + T italic() => _builder(FontStyle.italic); + T normal() => _builder(FontStyle.normal); +} + +/// Utility for setting `Radius` values. +/// +/// Useful for defining Radius values for widgets +/// +/// Example: +/// +/// ```dart +/// final radius = RadiusUtility(builder); +/// final zero = radius.zero(); +/// final circular = radius.circular(10); +/// final elliptical = radius.elliptical(10, 10); +/// ``` +/// +/// See [Radius] for more information. +class RadiusUtility extends MixUtility { + const RadiusUtility(super.builder); + + T zero() => _builder(Radius.zero); + + T elliptical(double x, double y) => _builder(Radius.elliptical(x, y)); + + T circular(double radius) => _builder(Radius.circular(radius)); +} + +/// Utility for setting `TextDecorationStyle` values. +/// +/// Useful for defining TextDecorationStyle values for widgets +/// +/// Example: +/// +/// ```dart +/// final textDecorationStyle = TextDecorationStyleUtility(builder); +/// final solid = textDecorationStyle.solid(); +/// final double = textDecorationStyle.double(); +/// final dotted = textDecorationStyle.dotted(); +/// final dashed = textDecorationStyle.dashed(); +/// final wavy = textDecorationStyle.wavy(); +/// ``` +/// +/// See [TextDecorationStyle] for more information. +class TextDecorationStyleUtility + extends ScalarUtility { + const TextDecorationStyleUtility(super.builder); + + T solid() => _builder(TextDecorationStyle.solid); + T double() => _builder(TextDecorationStyle.double); + T dotted() => _builder(TextDecorationStyle.dotted); + T dashed() => _builder(TextDecorationStyle.dashed); + T wavy() => _builder(TextDecorationStyle.wavy); +} + +/// Utility for setting `TextBaseline` values. +/// +/// Useful for defining TextBaseline values for widgets +/// +/// Example: +/// +/// ```dart +/// final textBaseline = TextBaselineUtility(builder); +/// final alphabetic = textBaseline.alphabetic(); +/// final ideographic = textBaseline.ideographic(); +/// ``` +/// +/// See [TextBaseline] for more information. +class TextBaselineUtility + extends ScalarUtility { + const TextBaselineUtility(super.builder); + + T alphabetic() => _builder(TextBaseline.alphabetic); + T ideographic() => _builder(TextBaseline.ideographic); +} + +/// Utility for setting `TextOverflow` values. +/// +/// Useful for defining TextOverflow values for widgets +/// +/// Example: +/// +/// ```dart +/// final textOverflow = TextOverflowUtility(builder); +/// final clip = textOverflow.clip(); +/// final ellipsis = textOverflow.ellipsis(); +/// final fade = textOverflow.fade(); +/// ``` +/// +/// See [TextOverflow] for more information. +class TextOverflowUtility + extends ScalarUtility { + const TextOverflowUtility(super.builder); + T clip() => _builder(TextOverflow.clip); + T ellipsis() => _builder(TextOverflow.ellipsis); + T fade() => _builder(TextOverflow.fade); +} + +/// Utility for setting `TextWidthBasis` values. +/// +/// Useful for defining TextWidthBasis values for widgets +/// +/// Example: +/// +/// ```dart +/// final textWidthBasis = TextWidthBasisUtility(builder); +/// final parent = textWidthBasis.parent(); +/// final longestLine = textWidthBasis.longestLine(); +/// ``` +/// +/// See [TextWidthBasis] for more information. +class TextWidthBasisUtility + extends ScalarUtility { + const TextWidthBasisUtility(super.builder); + T parent() => _builder(TextWidthBasis.parent); + T longestLine() => _builder(TextWidthBasis.longestLine); +} + +/// Utility for setting `TextAlign` values. +/// +/// Useful for defining TextAlign values for widgets +/// +/// Example: +/// +/// ```dart +/// final textAlign = TextAlignUtility(builder); +/// final left = textAlign.left(); +/// final right = textAlign.right(); +/// final center = textAlign.center(); +/// final justify = textAlign.justify(); +/// final start = textAlign.start(); +/// final end = textAlign.end(); +/// ``` +/// +/// See [TextAlign] for more information. +class TextAlignUtility + extends ScalarUtility { + const TextAlignUtility(super.builder); + T left() => _builder(TextAlign.left); + T right() => _builder(TextAlign.right); + T center() => _builder(TextAlign.center); + T justify() => _builder(TextAlign.justify); + T start() => _builder(TextAlign.start); + T end() => _builder(TextAlign.end); +} diff --git a/lib/src/attributes/scalars/scalars_attribute.dart b/lib/src/attributes/scalars/scalars_attribute.dart new file mode 100644 index 000000000..68697761f --- /dev/null +++ b/lib/src/attributes/scalars/scalars_attribute.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +import '../../core/attribute.dart'; +import '../color/color_attribute.dart'; +import '../color/color_dto.dart'; + +@immutable +abstract class ScalarAttribute, Value> + extends StyleAttribute { + final Value value; + const ScalarAttribute(this.value); + + @override + Type get type => Self; + + @override + get props => [value]; +} + +@immutable +class AxisAttribute extends ScalarAttribute { + const AxisAttribute(super.value); + + static AxisAttribute? maybeFrom(Axis? value) => + value == null ? null : AxisAttribute(value); +} + +@immutable +class TransformAttribute extends ScalarAttribute + with SingleChildRenderAttributeMixin { + const TransformAttribute(super.value); + + static TransformAttribute? maybeFrom(Matrix4? value) => + value == null ? null : TransformAttribute(value); + + @override + Transform build(mix, child) { + return Transform(transform: value, child: child); + } +} + +@immutable +class AlignmentGeometryAttribute + extends ScalarAttribute + with SingleChildRenderAttributeMixin { + const AlignmentGeometryAttribute(super.value); + + static AlignmentGeometryAttribute? maybeFrom(AlignmentGeometry? value) => + value == null ? null : AlignmentGeometryAttribute(value); + + @override + Align build(mix, child) { + return Align(alignment: value, child: child); + } +} + +@immutable +class ClipBehaviorAttribute + extends ScalarAttribute { + const ClipBehaviorAttribute(super.value); + + static ClipBehaviorAttribute? maybeFrom(Clip? value) => + value == null ? null : ClipBehaviorAttribute(value); +} + +@immutable +class BackgroundColorAttribute extends ColorAttribute + with SingleChildRenderAttributeMixin { + const BackgroundColorAttribute(super.value); + + static BackgroundColorAttribute? maybeFrom(Color? value) => + value == null ? null : BackgroundColorAttribute(ColorDto(value)); + + @override + BackgroundColorAttribute merge(BackgroundColorAttribute? other) { + return other == null + ? this + : BackgroundColorAttribute(value.merge(other.value)); + } + + @override + ColoredBox build(mix, child) { + return ColoredBox(color: resolve(mix), child: child); + } +} diff --git a/lib/src/attributes/shadow/shadow_dto.dart b/lib/src/attributes/shadow/shadow_dto.dart new file mode 100644 index 000000000..e165c057a --- /dev/null +++ b/lib/src/attributes/shadow/shadow_dto.dart @@ -0,0 +1,114 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; +import '../color/color_dto.dart'; + +@immutable +abstract class ShadowDtoImpl, + Value extends Shadow> extends Dto with Mergeable { + final ColorDto? color; + final Offset? offset; + final double? blurRadius; + + const ShadowDtoImpl({this.blurRadius, this.color, this.offset}); + + @override + Value resolve(MixData mix); + + @override + Self merge(covariant Self? other); +} + +@immutable +class ShadowDto extends ShadowDtoImpl { + const ShadowDto({super.blurRadius, super.color, super.offset}); + + static ShadowDto from(Shadow shadow) { + return ShadowDto( + blurRadius: shadow.blurRadius, + color: ColorDto.maybeFrom(shadow.color), + offset: shadow.offset, + ); + } + + static ShadowDto? maybeFrom(Shadow? shadow) { + return shadow == null ? null : from(shadow); + } + + @override + Shadow resolve(MixData mix) { + const defaultShadow = Shadow(); + + return Shadow( + color: color?.resolve(mix) ?? defaultShadow.color, + offset: offset ?? defaultShadow.offset, + blurRadius: blurRadius ?? defaultShadow.blurRadius, + ); + } + + @override + ShadowDto merge(covariant ShadowDto? other) { + if (other == null) return this; + + return ShadowDto( + blurRadius: other.blurRadius ?? blurRadius, + color: other.color ?? color, + offset: other.offset ?? offset, + ); + } + + @override + get props => [color, offset, blurRadius]; +} + +class BoxShadowDto extends ShadowDtoImpl { + final double? spreadRadius; + + const BoxShadowDto({ + super.color, + super.offset, + super.blurRadius, + this.spreadRadius, + }); + + static BoxShadowDto from(BoxShadow shadow) { + return BoxShadowDto( + color: ColorDto.maybeFrom(shadow.color), + offset: shadow.offset, + blurRadius: shadow.blurRadius, + spreadRadius: shadow.spreadRadius, + ); + } + + static BoxShadowDto? maybeFrom(BoxShadow? shadow) { + return shadow == null ? null : from(shadow); + } + + @override + BoxShadow resolve(MixData mix) { + const defaultShadow = BoxShadow(); + + return BoxShadow( + color: color?.resolve(mix) ?? defaultShadow.color, + offset: offset ?? defaultShadow.offset, + blurRadius: blurRadius ?? defaultShadow.blurRadius, + spreadRadius: spreadRadius ?? defaultShadow.spreadRadius, + ); + } + + @override + BoxShadowDto merge(BoxShadowDto? other) { + if (other == null) return this; + + return BoxShadowDto( + color: other.color ?? color, + offset: other.offset ?? offset, + blurRadius: other.blurRadius ?? blurRadius, + spreadRadius: other.spreadRadius ?? spreadRadius, + ); + } + + @override + get props => [color, offset, blurRadius, spreadRadius]; +} diff --git a/lib/src/attributes/shadow/shadow_util.dart b/lib/src/attributes/shadow/shadow_util.dart new file mode 100644 index 000000000..293d3827d --- /dev/null +++ b/lib/src/attributes/shadow/shadow_util.dart @@ -0,0 +1,244 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../core/extensions/values_ext.dart'; +import '../color/color_dto.dart'; +import '../color/color_util.dart'; +import '../scalars/scalar_util.dart'; +import 'shadow_dto.dart'; + +/// Utility class for creating and manipulating attributes with [Shadow] +/// +/// Allows setting of color, offset, and blur radius for a shadow. This class +/// provides a convenient way to configure and apply shadow effects to [T] +/// +/// Accepts a builder function that returns [T] and takes a [ShadowDto] as a parameter. +/// +/// Example usage: +/// +/// ```dart +// final shadow = ShadowUtility(builder); +// final attribute = shadow( +// color: Colors.black, +// offset: Offset(2.0, 2.0), +// blurRadius: 4.0, +// ); +/// ``` +/// +/// See also: +/// * [ShadowDto], the data transfer object for [Shadow] +/// * [MixUtility], the utility class that [ShadowUtility] extends +class ShadowUtility extends MixUtility { + const ShadowUtility(super.builder); + + // Internal method to create a shadow with optional parameters. + T _only({ColorDto? color, Offset? offset, double? blurRadius}) { + final shadow = ShadowDto( + blurRadius: blurRadius, + color: color, + offset: offset, + ); + + return builder(shadow); + } + + /// Returns a [ColorUtility] for setting the color of the shadow. + /// + /// Example usage: + /// + /// ```dart + /// final shadow = ShadowUtility(builder); + /// final attribute = shadow.color(Colors.black); + /// ``` + /// + /// Attribute now holds a [ShadowAttribute] with a [ShadowDto] that has a color value of `Colors.black`. + /// + /// See also: + /// * [ColorUtility], the utility class for manipulating [Color] + /// * [ColorDto], the data transfer object for [Color] + ColorUtility get color { + return ColorUtility((color) => _only(color: color)); + } + + /// Returns an [OffsetUtility] for setting the offset of the shadow. + /// + /// Example usage: + /// + /// ```dart + /// final shadow = ShadowUtility(builder); + /// final attribute = shadow.offset(Offset(2.0, 2.0)); + /// ``` + /// + /// Attribute now holds a [ShadowAttribute] with a [ShadowDto] that has an offset value of `Offset(2.0, 2.0)`. + /// + /// See also: + /// * [OffsetUtility], the utility class for manipulating [Offset] + /// * [Offset], the data transfer object for [Offset] + OffsetUtility get offset { + return OffsetUtility((offset) => _only(offset: offset)); + } + + /// Method to set the blur radius of the shadow. + /// + /// Example usage: + /// + /// ```dart + /// final shadow = ShadowUtility(builder); + /// final attribute = shadow.blurRadius(4.0); + /// ``` + /// + /// Attribute now holds a [ShadowAttribute] with a [ShadowDto] that has a blur radius value of `4.0`. + /// + /// See also: + /// * [DoubleUtility], the utility class for manipulating [double] + T blurRadius(double blurRadius) => _only(blurRadius: blurRadius); + + /// Method to create a shadow with provided parameters. + T call({Color? color, Offset? offset, double? blurRadius}) { + return _only( + color: color?.toDto(), + offset: offset, + blurRadius: blurRadius, + ); + } +} + +/// Utility class for creating and manipulating a list of [BoxShadow] attributes. +/// +/// Allows for the creation of a list of box shadows, useful for applying multiple shadows to a single element. +class BoxShadowListUtility + extends MixUtility> { + const BoxShadowListUtility(super.builder); + + /// Method to create a list of box shadows from a list of [BoxShadow]. + /// + /// Example usage: + /// + /// ```dart + /// final boxShadowList = BoxShadowListUtility(builder); + /// final attribute = boxShadowList([ + /// BoxShadow(color: Colors.black, blurRadius: 4.0, offset: Offset(2.0, 2.0)) + /// ]); + /// ``` + /// + /// Attribute now holds a list of [BoxShadowAttribute] with corresponding [BoxShadowDto] values. + T call(List shadows) { + return builder(shadows.map(BoxShadowDto.from).toList()); + } +} + +/// Utility class for creating and manipulating [BoxShadow] attributes. +/// +/// Allows setting of color, offset, blur radius, and spread radius for a box shadow. +/// Useful for adding depth and elevation effects to Flutter widgets. +class BoxShadowUtility + extends MixUtility { + const BoxShadowUtility(super.builder); + + /// Returns a [ColorUtility] for setting the color of the box shadow. + /// + /// Example usage: + /// + /// ```dart + /// final boxShadow = BoxShadowUtility(builder); + /// final attribute = boxShadow.color(Colors.black); + /// ``` + /// + /// Attribute now holds a [BoxShadowAttribute] with a [BoxShadowDto] that has a color value of `Colors.black`. + ColorUtility get color { + return ColorUtility((color) => call(color: color)); + } + + /// Returns an [OffsetUtility] for setting the offset of the box shadow. + /// + /// Example usage: + /// + /// ```dart + /// final boxShadow = BoxShadowUtility(builder); + /// final attribute = boxShadow.offset(Offset(2.0, 2.0)); + /// ``` + /// + /// Attribute now holds a [BoxShadowAttribute] with a [BoxShadowDto] that has an offset value of `Offset(2.0, 2.0)`. + OffsetUtility get offset { + return OffsetUtility((offset) => call(offset: offset)); + } + + /// Method to set the spread radius of the box shadow. + /// + /// Example usage: + /// + /// ```dart + /// final boxShadow = BoxShadowUtility(builder); + /// final attribute = boxShadow.spreadRadius(1.0); + /// ``` + /// + /// Attribute now holds a [BoxShadowAttribute] with a [BoxShadowDto] that has a spread radius value of `1.0`. + T spreadRadius(double spreadRadius) => call(spreadRadius: spreadRadius); + + /// Method to set the blur radius of the box shadow. + /// + /// Example usage: + /// + /// ```dart + /// final boxShadow = BoxShadowUtility(builder); + /// final attribute = boxShadow.blurRadius(4.0); + /// ``` + /// + /// Attribute now holds a [BoxShadowAttribute] with a [BoxShadowDto] that has a blur radius value of `4.0`. + T blurRadius(double blurRadius) => call(blurRadius: blurRadius); + + /// Method to create a box shadow with provided parameters. + T call({ + ColorDto? color, + Offset? offset, + double? blurRadius, + double? spreadRadius, + }) { + final shadow = BoxShadowDto( + color: color, + offset: offset, + blurRadius: blurRadius, + spreadRadius: spreadRadius, + ); + + return builder(shadow); + } +} + +/// Utility class for setting elevation using predefined shadows. +/// +/// Provides convenience methods for standard material design elevations. This class +/// simplifies the process of applying consistent elevation effects across the application. +class ElevationUtility + extends MixUtility> { + const ElevationUtility(super.builder); + + /// Method to set elevation based on material design standards. + /// + /// Example usage: + /// + /// ```dart + /// final elevation = ElevationUtility(builder); + /// final attribute = elevation.two(); + /// ``` + /// + /// Attribute now holds a list of [BoxShadowAttribute] corresponding to a material elevation of `2`. + T call(int value) { + assert(kElevationToShadow.containsKey(value), 'Invalid elevation value'); + + return builder(kElevationToShadow[value]!.toDto()); + } + + // Convenience methods for common elevation values. + T none() => call(0); + T one() => call(1); + T two() => call(2); + T three() => call(3); + T four() => call(4); + T six() => call(6); + T eight() => call(8); + T nine() => call(9); + T twelve() => call(12); + T sixteen() => call(16); + T twentyFour() => call(24); +} diff --git a/lib/src/attributes/shadow_attribute.dart b/lib/src/attributes/shadow_attribute.dart deleted file mode 100644 index 81b3624eb..000000000 --- a/lib/src/attributes/shadow_attribute.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../factory/mix_provider_data.dart'; -import 'attribute.dart'; -import 'color_attribute.dart'; - -@immutable -class ShadowAttribute extends VisualAttribute { - final ColorAttribute? color; - final Offset? offset; - final double? blurRadius; - - const ShadowAttribute({this.blurRadius, this.color, this.offset}); - - @override - Shadow resolve(MixData mix) { - const defaultShadow = Shadow(); - - return Shadow( - color: color?.resolve(mix) ?? defaultShadow.color, - offset: offset ?? defaultShadow.offset, - blurRadius: blurRadius ?? defaultShadow.blurRadius, - ); - } - - @override - ShadowAttribute merge(ShadowAttribute? other) { - return ShadowAttribute( - blurRadius: other?.blurRadius ?? blurRadius, - color: color?.merge(other?.color) ?? other?.color, - offset: other?.offset ?? offset, - ); - } - - @override - get props => [color, offset, blurRadius]; -} - -class BoxShadowAttribute extends ShadowAttribute { - final double? spreadRadius; - - const BoxShadowAttribute({ - super.color, - super.offset, - super.blurRadius, - this.spreadRadius, - }); - - @override - BoxShadow resolve(MixData mix) { - const defaultShadow = BoxShadow(); - - final shadow = super.resolve(mix); - - return BoxShadow( - color: shadow.color, - offset: shadow.offset, - blurRadius: shadow.blurRadius, - spreadRadius: spreadRadius ?? defaultShadow.spreadRadius, - ); - } - - @override - BoxShadowAttribute merge(BoxShadowAttribute? other) { - return BoxShadowAttribute( - color: color?.merge(other?.color) ?? other?.color, - offset: other?.offset ?? offset, - blurRadius: other?.blurRadius ?? blurRadius, - spreadRadius: other?.spreadRadius ?? spreadRadius, - ); - } - - @override - get props => [...super.props, spreadRadius]; -} diff --git a/lib/src/attributes/space_attribute.dart b/lib/src/attributes/space_attribute.dart deleted file mode 100644 index 88b82cd1d..000000000 --- a/lib/src/attributes/space_attribute.dart +++ /dev/null @@ -1,231 +0,0 @@ -// ignore_for_file: avoid-shadowing - -import 'package:flutter/material.dart'; - -import '../factory/mix_provider_data.dart'; -import 'attribute.dart'; - -@immutable -abstract class SpaceGeometryAttribute - extends VisualAttribute { - final double? top; - final double? bottom; - final double? left; - final double? right; - - // Directional - final double? start; - final double? end; - - const SpaceGeometryAttribute({ - this.top, - this.bottom, - this.left, - this.right, - this.start, - this.end, - }); - - SpaceGeometryAttribute create({ - double? top, - double? bottom, - double? left, - double? right, - double? start, - double? end, - }); - - @override - SpaceGeometryAttribute merge( - covariant SpaceGeometryAttribute? other, - ) { - return create( - top: other?.top ?? top, - bottom: other?.bottom ?? bottom, - left: other?.left ?? left, - right: other?.right ?? right, - start: other?.start ?? start, - end: other?.end ?? end, - ); - } - - @override - T resolve(MixData mix) { - final top = this.top ?? 0; - final bottom = this.bottom ?? 0; - - if (this is SpaceAttribute) { - final left = this.left ?? 0; - final right = this.right ?? 0; - - return EdgeInsets.only( - left: mix.resolver.spaceTokenRef(left), - top: mix.resolver.spaceTokenRef(top), - right: mix.resolver.spaceTokenRef(right), - bottom: mix.resolver.spaceTokenRef(bottom), - ) as T; - } else if (this is SpaceDirectionalAttribute) { - final start = this.start ?? 0; - final end = this.end ?? 0; - - return EdgeInsetsDirectional.only( - start: mix.resolver.spaceTokenRef(start), - top: mix.resolver.spaceTokenRef(top), - end: mix.resolver.spaceTokenRef(end), - bottom: mix.resolver.spaceTokenRef(bottom), - ) as T; - } - throw UnsupportedError( - 'SpaceGeometryAttribute must be either SpaceAttribute or SpaceDirectionalAttribute', - ); - } - - @override - get props => [top, bottom, left, right, start, end]; -} - -@immutable -abstract class SpaceAttribute extends SpaceGeometryAttribute { - const SpaceAttribute({super.top, super.bottom, super.left, super.right}); -} - -@immutable -abstract class SpaceDirectionalAttribute - extends SpaceGeometryAttribute { - const SpaceDirectionalAttribute({ - super.top, - super.bottom, - super.start, - super.end, - }); -} - -@immutable -abstract class PaddingGeometryAttribute - extends SpaceGeometryAttribute { - const PaddingGeometryAttribute({ - super.top, - super.bottom, - super.left, - super.right, - super.start, - super.end, - }); -} - -@immutable -class PaddingAttribute extends PaddingGeometryAttribute - implements SpaceAttribute { - const PaddingAttribute({super.top, super.bottom, super.left, super.right}); - - @override - PaddingAttribute create({ - double? top, - double? bottom, - double? left, - double? right, - double? start, - double? end, - }) { - return PaddingAttribute( - top: top ?? this.top, - bottom: bottom ?? this.bottom, - left: left ?? this.left, - right: right ?? this.right, - ); - } -} - -@immutable -class PaddingDirectionalAttribute - extends PaddingGeometryAttribute - implements SpaceDirectionalAttribute { - const PaddingDirectionalAttribute({ - super.top, - super.bottom, - super.start, - super.end, - }); - - @override - PaddingDirectionalAttribute create({ - double? top, - double? bottom, - double? left, - double? right, - double? start, - double? end, - }) { - return PaddingDirectionalAttribute( - top: top ?? this.top, - bottom: bottom ?? this.bottom, - start: start ?? this.start, - end: end ?? this.end, - ); - } -} - -@immutable -abstract class MarginGeometryAttribute - extends SpaceGeometryAttribute { - const MarginGeometryAttribute({ - super.top, - super.bottom, - super.left, - super.right, - super.start, - super.end, - }); -} - -@immutable -class MarginAttribute extends MarginGeometryAttribute - implements SpaceAttribute { - const MarginAttribute({super.top, super.bottom, super.left, super.right}); - - @override - MarginAttribute create({ - double? top, - double? bottom, - double? left, - double? right, - double? start, - double? end, - }) { - return MarginAttribute( - top: top ?? this.top, - bottom: bottom ?? this.bottom, - left: left ?? this.left, - right: right ?? this.right, - ); - } -} - -@immutable -class MarginDirectionalAttribute - extends MarginGeometryAttribute - implements SpaceDirectionalAttribute { - const MarginDirectionalAttribute({ - super.top, - super.bottom, - super.start, - super.end, - }); - - @override - MarginDirectionalAttribute create({ - double? top, - double? bottom, - double? left, - double? right, - double? start, - double? end, - }) { - return MarginDirectionalAttribute( - top: top ?? this.top, - bottom: bottom ?? this.bottom, - start: start ?? this.start, - end: end ?? this.end, - ); - } -} diff --git a/lib/src/attributes/spacing/edge_insets_dto.dart b/lib/src/attributes/spacing/edge_insets_dto.dart new file mode 100644 index 000000000..14f346978 --- /dev/null +++ b/lib/src/attributes/spacing/edge_insets_dto.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; + +@immutable +abstract class EdgeInsetsGeometryDto> + extends Dto with Mergeable { + final double? top; + final double? bottom; + final double? left; + final double? right; + + // Directional + final double? start; + final double? end; + + const EdgeInsetsGeometryDto({ + this.top, + this.bottom, + this.left, + this.right, + this.start, + this.end, + }); + + bool get isDirectional => start != null || end != null; + + @override + EdgeInsetsGeometry resolve(MixData mix); + + @override + get props => [top, bottom, left, right, start, end]; +} diff --git a/lib/src/attributes/spacing/spacing_attribute.dart b/lib/src/attributes/spacing/spacing_attribute.dart new file mode 100644 index 000000000..ba35471f3 --- /dev/null +++ b/lib/src/attributes/spacing/spacing_attribute.dart @@ -0,0 +1,48 @@ +// ignore_for_file: unnecessary_overrides, prefer-moving-to-variable + +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; +import 'spacing_dto.dart'; + +@immutable +abstract class SpacingAttribute> + extends DtoAttribute + with SingleChildRenderAttributeMixin { + const SpacingAttribute(super.value); + + bool get isDirectional => value.isDirectional; + + double? get top => value.top; + double? get bottom => value.bottom; + double? get left => value.left; + double? get right => value.right; + double? get start => value.start; + double? get end => value.end; + + @override + Padding build(MixData mix, Widget child) { + return Padding(padding: resolve(mix), child: child); + } +} + +@immutable +class PaddingAttribute extends SpacingAttribute { + const PaddingAttribute(super.value); + + @override + PaddingAttribute merge(PaddingAttribute? other) { + return other == null ? this : PaddingAttribute(value.merge(other.value)); + } +} + +@immutable +class MarginAttribute extends SpacingAttribute { + const MarginAttribute(super.value); + + @override + MarginAttribute merge(MarginAttribute? other) { + return other == null ? this : MarginAttribute(value.merge(other.value)); + } +} diff --git a/lib/src/attributes/spacing/spacing_dto.dart b/lib/src/attributes/spacing/spacing_dto.dart new file mode 100644 index 000000000..3dbf8bc45 --- /dev/null +++ b/lib/src/attributes/spacing/spacing_dto.dart @@ -0,0 +1,77 @@ +// ignore_for_file: prefer-moving-to-variable + +import 'package:flutter/material.dart'; + +import '../../factory/mix_provider_data.dart'; +import 'edge_insets_dto.dart'; + +@immutable +class SpacingDto extends EdgeInsetsGeometryDto { + const SpacingDto({ + super.top, + super.bottom, + super.left, + super.right, + super.start, + super.end, + }); + + static SpacingDto from(EdgeInsetsGeometry edgeInsets) { + if (edgeInsets is EdgeInsetsDirectional) { + return SpacingDto( + top: edgeInsets.top, + bottom: edgeInsets.bottom, + start: edgeInsets.start, + end: edgeInsets.end, + ); + } else if (edgeInsets is EdgeInsets) { + return SpacingDto( + top: edgeInsets.top, + bottom: edgeInsets.bottom, + left: edgeInsets.left, + right: edgeInsets.right, + ); + } + + throw ArgumentError.value( + edgeInsets, + 'edgeInsets', + 'Must be either EdgeInsets or EdgeInsetsDirectional', + ); + } + + static SpacingDto? maybeFrom(EdgeInsetsGeometry? edgeInsets) { + return edgeInsets == null ? null : from(edgeInsets); + } + + @override + SpacingDto merge(SpacingDto? other) { + if (other == null) return this; + + return SpacingDto( + top: other.top ?? top, + bottom: other.bottom ?? bottom, + left: other.left ?? left, + right: other.right ?? right, + start: other.start ?? start, + end: other.end ?? end, + ); + } + + @override + EdgeInsetsGeometry resolve(MixData mix) { + return isDirectional + ? EdgeInsetsDirectional.only( + start: mix.tokens.spaceTokenRef(start ?? 0), + top: mix.tokens.spaceTokenRef(top ?? 0), + end: mix.tokens.spaceTokenRef(end ?? 0), + bottom: mix.tokens.spaceTokenRef(bottom ?? 0), + ) + : EdgeInsets.only( + left: mix.tokens.spaceTokenRef(left ?? 0), + top: mix.tokens.spaceTokenRef(top ?? 0), + right: mix.tokens.spaceTokenRef(right ?? 0), + bottom: mix.tokens.spaceTokenRef(bottom ?? 0), + ); + } +} diff --git a/lib/src/attributes/spacing/spacing_util.dart b/lib/src/attributes/spacing/spacing_util.dart new file mode 100644 index 000000000..c452087f3 --- /dev/null +++ b/lib/src/attributes/spacing/spacing_util.dart @@ -0,0 +1,658 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../theme/tokens/space_token.dart'; +import '../scalars/scalar_util.dart'; +import 'spacing_attribute.dart'; +import 'spacing_dto.dart'; + +/// A utility class for defining spacing attributes like padding and margin in Flutter widgets. +/// +/// This class provides a convenient and intuitive way to specify various types of spacing in UI components. +/// It works with `SpacingAttribute` to create custom spacing attributes that can be applied to widgets. +/// +/// Example: +/// ```dart +/// // Instantiate SpacingUtility with a custom SpacingAttributeBuilder. +/// final spacing = SpacingUtility(SpacingAttribute.new); +/// +/// // Apply uniform spacing of 10 units on all sides. +/// final uniform = spacing.all(10); +/// +/// // Apply different spacing values for each side. +/// final custom = spacing.only(top: 10, bottom: 20, left: 30, right: 40); +/// +/// // Apply 15 units of vertical and 20 units of horizontal spacing. +/// final vertical = spacing.vertical(15); +/// final horizontal = spacing.horizontal(20); +/// ``` +@immutable +class SpacingUtility + extends DtoUtility { + /// Comment goes here + const SpacingUtility(super.builder) : super(valueToDto: SpacingDto.from); + + SpacingDirectionalUtility get directional => + SpacingDirectionalUtility(builder); + + /// Applies uniform horizontal spacing to a widget. + /// + /// This method returns a `SpacingSideUtility` that provides equal horizontal spacing on both the left and right sides of a widget. + /// It is ideal for consistent horizontal padding or margin, aiding in maintaining a harmonious alignment in the horizontal axis of the layout. + /// + /// Parameters: + /// `value` (double): The spacing value to apply equally to the left and right sides. + /// + /// Example: + /// + /// ```dart + /// // Applying 15 units of uniform horizontal spacing to the left and right sides of a widget + /// final horizontalSpacing = spacing.horizontal(15); + /// ``` + /// + /// See also: + /// * [SpacingSideUtility], the utility class for applying spacing to individual sides of a widget + SpacingSideUtility get horizontal { + return SpacingSideUtility( + (double value) => only(left: value, right: value), + ); + } + + /// Applies uniform vertical spacing to a widget. + /// + /// This method creates a `SpacingSideUtility` instance that applies equal spacing to both the top and bottom sides of a widget. + /// It's especially useful for achieving symmetrical vertical padding or margin, enhancing the visual balance of the layout. + /// + /// Parameters: + /// `value` (double): The amount of vertical spacing to apply uniformly to the top and bottom. + /// + /// Example: + /// + /// ```dart + /// // Creating a vertical spacing utility with 20 units of spacing for the top and bottom sides + /// final verticalSpacing = spacing.vertical(20); + /// ``` + /// + /// See also: + /// * [SpacingSideUtility], the utility class for applying spacing to individual sides of a widget + SpacingSideUtility get vertical { + return SpacingSideUtility( + (double value) => only(top: value, bottom: value), + ); + } + + /// Creates a spacing utility that applies uniform spacing to all sides of a widget. + /// + /// This method returns a `SpacingSideUtility` for setting equal spacing on the top, bottom, left, and right sides. + /// It is ideal for creating consistent padding or margin around a widget. + /// + /// Parameters: + /// `value` (double): The uniform spacing value to apply on all sides. + /// + /// Example: + /// + /// ```dart + /// // Applying 10 units of uniform spacing to all sides of a widget + /// final uniformSpacing = spacing.all(10); + /// ``` + /// + /// See also: + /// * [SpacingSideUtility], the utility class for applying spacing to individual sides of a widget + SpacingSideUtility get all { + return SpacingSideUtility( + (double value) => + only(top: value, bottom: value, left: value, right: value), + ); + } + + /// Applies uniform spacing to the top side of a widget. + /// + /// This method returns a `SpacingSideUtility` for setting a specified amount of spacing + /// (either padding or margin) at the top edge of a widget. It is helpful for adjusting vertical + /// alignment and spacing within layouts. + /// + /// Parameters: + /// `value` (double): The spacing value to be applied to the top side. + /// + /// Example: + /// + /// ```dart + /// // Adding 5 units of spacing to the top side of a widget + /// final topSpacing = spacing.top(5); + /// ``` + /// + /// See also: + /// * [SpacingSideUtility], the utility class for applying spacing to individual sides of a widget + SpacingSideUtility get top { + return SpacingSideUtility((value) => only(top: value)); + } + + /// Applies uniform spacing to the bottom side of a widget. + /// + /// This method returns a `SpacingSideUtility` for adding a specific amount of spacing + /// (padding or margin) to the bottom edge. It is useful for managing vertical space and alignment + /// in widget layouts. + /// + /// Parameters: + /// `value` (double): The spacing value to apply to the bottom side. + /// + /// Example: + /// + /// ```dart + /// // Setting 5 units of spacing at the bottom side of a widget + /// final bottomSpacing = spacing.bottom(5); + /// ``` + /// + /// See also: + /// * [SpacingSideUtility], the utility class for applying spacing to individual sides of a widget + SpacingSideUtility get bottom { + return SpacingSideUtility((value) => only(bottom: value)); + } + + /// Applies spacing to the left side. + /// + /// Ideal for adding padding or margin to the left edge. + /// + /// Example: + /// + /// ```dart + /// final myLeftSpacing = spacing.left(8); + /// ``` + /// + /// See also: + /// * [SpacingSideUtility], the utility class for applying spacing to individual sides of a widget + SpacingSideUtility get left { + return SpacingSideUtility((value) => only(left: value)); + } + + /// Applies spacing to the right side. + /// + /// Use this method for padding or margin on the right edge. + /// + /// Example: + /// + /// ```dart + /// final myRightSpacing = spacing.right(8); + /// ``` + /// + /// See also: + /// * [SpacingSideUtility], the utility class for applying spacing to individual sides of a widget + SpacingSideUtility get right { + return SpacingSideUtility((value) => only(right: value)); + } + + /// Applies spacing to the start side of the widget, considering the text direction. + /// + /// This method returns a `SpacingSideUtility` for adding padding or margin to the start side, + /// which is dynamically determined based on the text direction (LTR or RTL). + /// It is particularly useful in multilingual applications supporting RTL layouts. + /// + /// Parameters: + /// `value` (double): The spacing value to apply to the start side. + /// + /// Example: + /// + /// ```dart + /// // Adding 10 units of spacing to the start side of a widget + /// final startSpacing = spacing.start(10); + /// ``` + /// + /// See also: + /// * [SpacingSideUtility], the utility class for applying spacing to individual sides of a widget + SpacingSideUtility get start { + return SpacingSideUtility((value) => only(start: value)); + } + + /// Applies spacing to the end side of the widget, taking into account the text direction. + /// + /// This method returns a `SpacingSideUtility` for applying padding or margin to the end side. + /// The 'end' side is contextually determined based on the current text direction (LTR or RTL), making + /// it suitable for RTL layouts and localization needs. + /// + /// Parameters: + /// `value` (double): The spacing value to be applied to the end side. + /// + /// Example: + /// + /// ```dart + /// // Applying 10 units of spacing to the end side of a widget + /// final endSpacing = spacing.end(10); + /// ``` + /// + /// See also: + /// * [SpacingSideUtility], the utility class for applying spacing to individual sides of a widget + SpacingSideUtility get end { + return SpacingSideUtility((double value) => only(end: value)); + } + + /// Creates a custom spacing attribute with individually specified values for each side. + /// + /// This method allows for granular control of spacing by specifying different values for top, bottom, left, right, + /// start, and end sides. It is versatile for creating complex layouts with different spacing requirements on each side. + /// + /// Example: + /// + /// ```dart + /// // Creating a spacing attribute with custom values for each side + /// final customSpacing = spacing.only( + /// top: 10, + /// bottom: 20, + /// left: 30, + /// right: 40, + /// ); + /// ``` + /// See also: + /// * [SpacingDto], the data transfer object for [SpacingAttribute] + T only({ + double? top, + double? bottom, + double? left, + double? right, + double? start, + double? end, + }) { + return builder( + SpacingDto( + top: top, + bottom: bottom, + left: left, + right: right, + start: start, + end: end, + ), + ); + } + + /// Creates a spacing attribute with flexible spacing values based on the number of parameters provided. + /// + /// This callable method allows for defining space by passing up to four parameters, each representing a different side. + /// The behavior varies based on the number of parameters: + /// - 1 parameter: Applies uniform spacing on all sides. + /// - 2 parameters: Applies the first value to top and bottom, the second to left and right. + /// - 3 parameters: Applies the first value to top, the second to left and right, the third to bottom. + /// - 4 parameters: Applies each value to top, right, bottom, and left respectively. + /// + /// Example: + /// + /// ```dart + /// // Creating a spacing attribute with different values for each side + /// final mixedSpacing = spacing(10, 20, 30, 40); + /// ``` + /// See also: + /// * [SpacingDto], the data transfer object for [SpacingAttribute] + T call(double p1, [double? p2, double? p3, double? p4]) { + double top = p1; + double bottom = p1; + double left = p1; + double right = p1; + + if (p2 != null) { + left = p2; + right = p2; + } + + if (p3 != null) bottom = p3; + if (p4 != null) left = p4; + + return only(top: top, bottom: bottom, left: left, right: right); + } +} + +/// A utility class for creating directional spacing attributes in Flutter widgets. +/// +/// `SpacingDirectionalUtility` is tailored for managing spacing attributes with directional properties, +/// such as `start` and `end`, in place of traditional `left` and `right`. This is especially beneficial +/// for layouts that need to support different writing directions (e.g., RTL - Right To Left). It works +/// in conjunction with `SpaceDirectionalAttribute` to apply directional spacing in a readable and +/// maintainable manner. +/// +/// Example: +/// ```dart +/// final spacing = SpacingDirectionalUtility(SpacingAttribute.new); +/// +/// // Apply uniform spacing of 10 units on all directional sides. +/// final uniform = spacing.all(10); +/// +/// // Apply different spacing values for top, bottom, start, and end. +/// final custom = spacing.only( +/// top: 10, +/// bottom: 20, +/// start: 30, +/// end: 40, +/// ); +/// +/// // Apply 15 units of vertical and 20 units of horizontal directional spacing. +/// final vertical = spacing.vertical(15); +/// final horizontal = spacing.horizontal(20); +/// ``` +/// +/// See also: +/// * [SpacingUtility], the utility class for creating spacing attributes +/// * [MixUtility], the base utility class for creating attributes +@immutable +class SpacingDirectionalUtility + extends DtoUtility { + const SpacingDirectionalUtility(super.builder) + : super(valueToDto: SpacingDto.from); + + /// Creates a spacing utility that applies uniform spacing to all sides of a widget. + /// + /// This method returns a `SpacingSideUtility` for setting equal spacing on the top, bottom, left, and right sides. + /// It is ideal for creating consistent padding or margin around a widget. + /// + /// Parameters: + /// `value` (double): The uniform spacing value to apply on all sides. + /// + /// Example: + /// + /// ```dart + /// // Applying 10 units of uniform spacing to all sides of a widget + /// final uniformSpacing = spacing.all(10); + /// ``` + SpacingSideUtility get all { + return SpacingSideUtility( + (value) => only(top: value, bottom: value, start: value, end: value), + ); + } + + /// Applies spacing to the start side of the widget, considering the text direction. + /// + /// This method returns a `SpacingSideUtility` for adding padding or margin to the start side, + /// which is dynamically determined based on the text direction (LTR or RTL). + /// It is particularly useful in multilingual applications supporting RTL layouts. + /// + /// Parameters: + /// `value` (double): The spacing value to apply to the start side. + /// + /// Example: + /// + /// ```dart + /// // Adding 10 units of spacing to the start side of a widget + /// final startSpacing = spacing.start(10); + /// ``` + SpacingSideUtility get start { + return SpacingSideUtility((value) => only(start: value)); + } + + /// Applies spacing to the end side of the widget, taking into account the text direction. + /// + /// This method returns a `SpacingSideUtility` for applying padding or margin to the end side. + /// The 'end' side is contextually determined based on the current text direction (LTR or RTL), making + /// it suitable for RTL layouts and localization needs. + /// + /// Parameters: + /// `value` (double): The spacing value to be applied to the end side. + /// + /// Example: + /// + /// ```dart + /// // Applying 10 units of spacing to the end side of a widget + /// final endSpacing = spacing.end(10); + /// ``` + SpacingSideUtility get end { + return SpacingSideUtility((value) => only(end: value)); + } + + /// Applies uniform spacing to the top side of a widget. + /// + /// This method returns a `SpacingSideUtility` for setting a specified amount of spacing + /// (either padding or margin) at the top edge of a widget. It is helpful for adjusting vertical + /// alignment and spacing within layouts. + /// + /// Parameters: + /// `value` (double): The spacing value to be applied to the top side. + /// + /// Example: + /// + /// ```dart + /// // Adding 5 units of spacing to the top side of a widget + /// final topSpacing = spacing.top(5); + /// ``` + SpacingSideUtility get top { + return SpacingSideUtility((value) => only(top: value)); + } + + /// Applies uniform spacing to the bottom side of a widget. + /// + /// This method returns a `SpacingSideUtility` for adding a specific amount of spacing + /// (padding or margin) to the bottom edge. It is useful for managing vertical space and alignment + /// in widget layouts. + /// + /// Parameters: + /// `value` (double): The spacing value to apply to the bottom side. + /// + /// Example: + /// + /// ```dart + /// // Setting 5 units of spacing at the bottom side of a widget + /// final bottomSpacing = spacing.bottom(5); + /// ``` + SpacingSideUtility get bottom { + return SpacingSideUtility((value) => only(bottom: value)); + } + + /// Applies uniform vertical spacing to a widget. + /// + /// This method creates a `SpacingSideUtility` instance that applies equal spacing to both the top and bottom sides of a widget. + /// It's especially useful for achieving symmetrical vertical padding or margin, enhancing the visual balance of the layout. + /// + /// Parameters: + /// `value` (double): The amount of vertical spacing to apply uniformly to the top and bottom. + /// + /// Example: + /// + /// ```dart + /// // Creating a vertical spacing utility with 20 units of spacing for the top and bottom sides + /// final verticalSpacing = spacing.vertical(20); + /// ``` + + SpacingSideUtility get vertical { + return SpacingSideUtility((value) => only(top: value, bottom: value)); + } + + /// Applies uniform spacing on left and right sides. + /// + /// This method is useful for setting equal spacing on the start and end sides. + /// + /// Example: + /// + /// ```dart + /// final horizontalSpacing = spacing.horizontal(15); + /// ``` + + SpacingSideUtility get horizontal { + return SpacingSideUtility( + (double value) => only(start: value, end: value), + ); + } + + /// Creates a spacing attribute with flexible spacing values based on the number of parameters provided. + /// + /// This callable method allows for defining space by passing up to four parameters, each representing a different side. + /// The behavior varies based on the number of parameters: + /// - 1 parameter: Applies uniform spacing on all sides. + /// - 2 parameters: Applies the first value to top and bottom, the second to start and end. + /// - 3 parameters: Applies the first value to top, the second to start and end, the third to bottom. + /// - 4 parameters: Applies each value to top, end, bottom, and start respectively. + /// + /// Parameters: + /// `p1`, `p2`, `p3`, `p4` (double): Spacing values for different sides. + /// + /// Example: + /// + /// ```dart + /// // Creating a spacing attribute with different values for each side + /// final mixedSpacing = spacing(10, 20, 30, 40,); + /// ``` + /// See also: + /// * [SpacingDto], the data transfer object for [SpacingAttribute] + T call(double p1, [double? p2, double? p3, double? p4]) { + double top = p1; + double bottom = p1; + double start = p1; + double end = p1; + + if (p2 != null) { + start = p2; + end = p2; + } + + if (p3 != null) bottom = p3; + + if (p4 != null) start = p4; + + return only(top: top, bottom: bottom, start: start, end: end); + } + + /// Creates a custom directional spacing attribute with individually specified values for each side. + /// + /// This method allows for granular control of spacing by specifying different values for top, bottom, start, end, + /// start, and end sides. It is versatile for creating complex layouts with different spacing requirements on each side. + /// + /// Example: + /// + /// ```dart + /// // Creating a spacing attribute with custom values for each side + /// final customSpacing = spacing.only( + /// top: 10, + /// bottom: 20, + /// start: 30, + /// end: 40, + /// ); + /// ``` + /// See also: + /// * [SpacingDto], the data transfer object for [SpacingAttribute] + T only({double? top, double? bottom, double? start, double? end}) { + return builder( + SpacingDto(top: top, bottom: bottom, start: start, end: end), + ); + } +} + +/// A utility class for creating spacing attributes using design tokens from `MixTheme`. +/// +/// `SpacingSideUtility` enables the application of standard spacing sizes (like xsmall, small, etc.) or custom values in widget layouts. +/// The sizes used are design tokens defined in `MixTheme`. These tokens are referenced values that get converted to actual spacing during +/// the widget build process, ensuring consistent and theme-aligned spacing throughout the application. +/// +/// Example: +/// ```dart +/// // Using predefined spacing sizes from MixTheme design tokens +/// final smallSpacing = spacingSide.small(); +/// +/// // Applying a custom spacing value, to be converted in line with MixTheme during build +/// final customSpacing = spacingSide(15); +/// ``` +/// +/// See also: +/// * [SpaceToken], the design tokens for spacing sizes +/// * [SpacingUtility], the utility class for creating spacing attributes +/// * [SpacingDirectionalUtility], the utility class for creating directional spacing attributes +/// * [SpacingDto], the data transfer object for [SpacingAttribute] +@immutable +class SpacingSideUtility + extends MixUtility { + /// Creates a `SpacingSideUtility` with a builder function. + /// + /// The builder function takes a `double` as a parameter and returns `T`. + /// Now when using the utility it can inject custom methods into the builder function. + /// + /// Methods: + /// - `xsmall()`: Applies the 'extra-small' spacing size from design tokens. + /// - `small()`: Applies the 'small' spacing size from design tokens. + /// - `medium()`: Applies the 'medium' spacing size from design tokens. + /// - `large()`: Applies the 'large' spacing size from design tokens. + /// - `xlarge()`: Applies the 'extra-large' spacing size from design tokens. + /// - `xxlarge()`: Applies the 'extra-extra-large' spacing size from design tokens. + /// - `call(double value)`: Applies a custom spacing value. The `value` parameter is the spacing value to apply. + /// + /// Each method returns a `T` type, representing the spacing attribute created, which aligns with the overall theme and design system of the application. + const SpacingSideUtility(super.builder); + + /// Applies the 'extra-small' spacing size from design tokens. + /// + /// Example: + /// + /// ```dart + /// final smallSpacing = spacingSide.small(); + /// ``` + /// + /// See also: + /// * [SpaceToken], the design tokens for spacing sizes + T xsmall() => builder(SpaceToken.xsmall()); + + /// Applies the 'small' spacing size from design tokens. + /// + /// Example: + /// + /// ```dart + /// final smallSpacing = spacingSide.small(); + /// ``` + /// + /// See also: + /// * [SpaceToken], the design tokens for spacing sizes + T small() => builder(SpaceToken.small()); + + /// Applies the 'medium' spacing size from design tokens. + /// + /// Example: + /// + /// ```dart + /// final mediumSpacing = spacingSide.medium(); + /// ``` + /// + /// See also: + /// * [SpaceToken], the design tokens for spacing sizes + T medium() => builder(SpaceToken.medium()); + + /// Applies the 'large' spacing size from design tokens. + /// + /// Example: + /// + /// ```dart + /// final largeSpacing = spacingSide.large(); + /// ``` + /// + /// See also: + /// * [SpaceToken], the design tokens for spacing sizes + T large() => builder(SpaceToken.large()); + + /// Applies the 'extra-large' spacing size from design tokens. + /// + /// Example: + /// + /// ```dart + /// final xlargeSpacing = spacingSide.xlarge(); + /// ``` + /// + /// See also: + /// * [SpaceToken], the design tokens for spacing sizes + T xlarge() => builder(SpaceToken.xlarge()); + + /// Applies the 'extra-extra-large' spacing size from design tokens. + /// + /// Example: + /// + /// ```dart + /// final xxlargeSpacing = spacingSide.xxlarge(); + /// ``` + /// + /// See also: + /// * [SpaceToken], the design tokens for spacing sizes + T xxlarge() => builder(SpaceToken.xxlarge()); + + /// Applies a custom spacing value + /// + /// Parameters: + /// `value` (double): The spacing value to apply. + /// + /// Example: + /// + /// ```dart + /// final customSpacing = spacingSide(15); + /// ``` + /// + /// See also: + /// * [SpaceToken], the design tokens for spacing sizes + T call(double value) => builder(value); +} diff --git a/lib/src/attributes/strut_style/strut_style_attribute.dart b/lib/src/attributes/strut_style/strut_style_attribute.dart new file mode 100644 index 000000000..4208b2a26 --- /dev/null +++ b/lib/src/attributes/strut_style/strut_style_attribute.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import 'strut_style_dto.dart'; + +@immutable +class StrutStyleAttribute + extends DtoAttribute { + const StrutStyleAttribute(super.value); + + @override + StrutStyleAttribute merge(StrutStyleAttribute? other) { + return StrutStyleAttribute(value.merge(other?.value)); + } +} diff --git a/lib/src/attributes/strut_style_attribute.dart b/lib/src/attributes/strut_style/strut_style_dto.dart similarity index 68% rename from lib/src/attributes/strut_style_attribute.dart rename to lib/src/attributes/strut_style/strut_style_dto.dart index d92d52ecc..a463bbfed 100644 --- a/lib/src/attributes/strut_style_attribute.dart +++ b/lib/src/attributes/strut_style/strut_style_dto.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; -import '../factory/mix_provider_data.dart'; -import 'attribute.dart'; +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; @immutable -class StrutStyleAttribute extends VisualAttribute { +class StrutStyleDto extends Dto with Mergeable { final String? fontFamily; final List? fontFamilyFallback; final double? fontSize; @@ -14,7 +14,7 @@ class StrutStyleAttribute extends VisualAttribute { final double? leading; final bool? forceStrutHeight; - const StrutStyleAttribute({ + const StrutStyleDto({ this.fontFamily, this.fontFamilyFallback, this.fontSize, @@ -25,11 +25,28 @@ class StrutStyleAttribute extends VisualAttribute { this.forceStrutHeight, }); + static StrutStyleDto from(StrutStyle strutStyle) { + return StrutStyleDto( + fontFamily: strutStyle.fontFamily, + fontFamilyFallback: strutStyle.fontFamilyFallback, + fontSize: strutStyle.fontSize, + fontWeight: strutStyle.fontWeight, + fontStyle: strutStyle.fontStyle, + height: strutStyle.height, + leading: strutStyle.leading, + forceStrutHeight: strutStyle.forceStrutHeight, + ); + } + + static StrutStyleDto? maybeFrom(StrutStyle? strutStyle) { + return strutStyle == null ? null : from(strutStyle); + } + @override - StrutStyleAttribute merge(StrutStyleAttribute? other) { + StrutStyleDto merge(StrutStyleDto? other) { if (other == null) return this; - return StrutStyleAttribute( + return StrutStyleDto( fontFamily: other.fontFamily ?? fontFamily, fontFamilyFallback: other.fontFamilyFallback ?? fontFamilyFallback, fontSize: other.fontSize ?? fontSize, diff --git a/lib/src/attributes/strut_style/strut_style_util.dart b/lib/src/attributes/strut_style/strut_style_util.dart new file mode 100644 index 000000000..0960273f3 --- /dev/null +++ b/lib/src/attributes/strut_style/strut_style_util.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../scalars/scalar_util.dart'; +import 'strut_style_dto.dart'; + +@immutable +class StrutStyleUtility + extends DtoUtility { + const StrutStyleUtility(super.builder) + : super(valueToDto: StrutStyleDto.from); + + FontFamilyUtility get fontFamily { + return FontFamilyUtility((fontFamily) => call(fontFamily: fontFamily)); + } + + FontSizeUtility get fontSize { + return FontSizeUtility((fontSize) => call(fontSize: fontSize)); + } + + FontWeightUtility get fontWeight { + return FontWeightUtility((fontWeight) => call(fontWeight: fontWeight)); + } + + FontStyleUtility get fontStyle { + return FontStyleUtility((fontStyle) => call(fontStyle: fontStyle)); + } + + BoolUtility get forceStrutHeight { + return BoolUtility( + (forceStrutHeight) => call(forceStrutHeight: forceStrutHeight), + ); + } + + T height(double height) => call(height: height); + + T leading(double leading) => call(leading: leading); + + T fontFamilyFallback(List fontFamilyFallback) => + call(fontFamilyFallback: fontFamilyFallback); + + T call({ + String? fontFamily, + List? fontFamilyFallback, + double? fontSize, + FontWeight? fontWeight, + FontStyle? fontStyle, + double? height, + double? leading, + bool? forceStrutHeight, + }) { + final strutStyle = StrutStyleDto( + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontSize: fontSize, + fontWeight: fontWeight, + fontStyle: fontStyle, + height: height, + leading: leading, + forceStrutHeight: forceStrutHeight, + ); + + return builder(strutStyle); + } +} diff --git a/lib/src/attributes/style_mix_attribute.dart b/lib/src/attributes/style_mix_attribute.dart index 03fda5bdc..9b00777f1 100644 --- a/lib/src/attributes/style_mix_attribute.dart +++ b/lib/src/attributes/style_mix_attribute.dart @@ -1,19 +1,34 @@ +import 'package:flutter/material.dart'; + +import '../core/attribute.dart'; import '../factory/style_mix.dart'; -import 'attribute.dart'; -/// Allows to pass down Mixes as attributes for use with helpers. -class StyleMixAttribute extends Attribute { +@immutable +abstract class NestedStyleMixAttribute< + Self extends NestedStyleMixAttribute> extends Attribute + with Mergeable { final StyleMix value; - const StyleMixAttribute(this.value); + const NestedStyleMixAttribute(this.value); + + Self _mergeWith(StyleMix otherValue); @override - StyleMixAttribute merge(StyleMixAttribute? other) { - if (other == null) return this; + Self merge(Self? other) { + if (other == null) return this as Self; - return StyleMixAttribute(value.merge(other.value)); + return _mergeWith(other.value); } @override get props => [value]; } + +/// Allows to pass down Mixes as attributes for use with helpers. +class StyleMixAttribute extends NestedStyleMixAttribute { + const StyleMixAttribute(super.value); + + @override + StyleMixAttribute _mergeWith(StyleMix otherValue) => + StyleMixAttribute(value.merge(otherValue)); +} diff --git a/lib/src/attributes/text_directives_util.dart b/lib/src/attributes/text_directives_util.dart new file mode 100644 index 000000000..b05b6c201 --- /dev/null +++ b/lib/src/attributes/text_directives_util.dart @@ -0,0 +1,30 @@ +import '../core/attribute.dart'; +import '../core/directive.dart'; +import '../helpers/string_ext.dart'; +import '../recipes/text/text_attribute.dart'; +import 'scalars/scalar_util.dart'; + +final capitalize = _textDirective(TextModifiers.capitalize); +final uppercase = _textDirective(TextModifiers.uppercase); +final lowercase = _textDirective(TextModifiers.lowercase); +final titleCase = _textDirective(TextModifiers.titleCase); +final sentenceCase = _textDirective(TextModifiers.sentenceCase); + +TextMixAttribute Function() _textDirective(Modifier modifier) => + () => TextMixAttribute(directives: [TextDirective(modifier)]); + +class TextDirectiveUtility + extends ScalarUtility { + const TextDirectiveUtility(super.builder); +} + +// This is mostly used for testing, and easy reference of the modifier itself. +class TextModifiers { + const TextModifiers._(); + + static String capitalize(String value) => value.capitalize; + static String uppercase(String value) => value.toUpperCase(); + static String lowercase(String value) => value.toLowerCase(); + static String titleCase(String value) => value.titleCase; + static String sentenceCase(String value) => value.sentenceCase; +} diff --git a/lib/src/attributes/text_style/text_style_attribute.dart b/lib/src/attributes/text_style/text_style_attribute.dart new file mode 100644 index 000000000..c664458c4 --- /dev/null +++ b/lib/src/attributes/text_style/text_style_attribute.dart @@ -0,0 +1,17 @@ +// ignore_for_file: avoid-non-null-assertion + +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import 'text_style_dto.dart'; + +@immutable +class TextStyleAttribute + extends DtoAttribute { + const TextStyleAttribute(TextStyleDto value) : super(value); + + @override + TextStyleAttribute merge(TextStyleAttribute? other) { + return TextStyleAttribute(value.merge(other?.value)); + } +} diff --git a/lib/src/attributes/text_style/text_style_dto.dart b/lib/src/attributes/text_style/text_style_dto.dart new file mode 100644 index 000000000..6f925c16a --- /dev/null +++ b/lib/src/attributes/text_style/text_style_dto.dart @@ -0,0 +1,302 @@ +// ignore_for_file: avoid-non-null-assertion + +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../core/extensions/iterable_ext.dart'; +import '../../core/extensions/values_ext.dart'; +import '../../factory/mix_provider_data.dart'; +import '../../theme/tokens/text_style_token.dart'; +import '../color/color_dto.dart'; +import '../shadow/shadow_dto.dart'; + +@immutable +class TextStyleDataDto extends Dto with Mergeable { + final String? fontFamily; + final FontWeight? fontWeight; + final FontStyle? fontStyle; + final double? fontSize; + final double? letterSpacing; + final double? wordSpacing; + final TextBaseline? textBaseline; + final ColorDto? color; + final ColorDto? backgroundColor; + final List? shadows; + final List? fontFeatures; + final TextDecoration? decoration; + final ColorDto? decorationColor; + final TextDecorationStyle? decorationStyle; + final Locale? locale; + final String? debugLabel; + final double? height; + final Paint? foreground; + final Paint? background; + final double? decorationThickness; + final List? fontFamilyFallback; + + final TextStyleToken? token; + + const TextStyleDataDto({ + this.background, + this.backgroundColor, + this.color, + this.debugLabel, + this.decoration, + this.decorationColor, + this.decorationStyle, + this.decorationThickness, + this.fontFamily, + this.fontFamilyFallback, + this.fontFeatures, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.foreground, + this.height, + this.letterSpacing, + this.locale, + this.shadows, + this.textBaseline, + this.wordSpacing, + }) : token = null; + + const TextStyleDataDto.token(this.token) + : background = null, + backgroundColor = null, + color = null, + debugLabel = null, + decoration = null, + decorationColor = null, + decorationStyle = null, + decorationThickness = null, + fontFamily = null, + fontFamilyFallback = null, + fontFeatures = null, + fontSize = null, + fontStyle = null, + fontWeight = null, + foreground = null, + height = null, + letterSpacing = null, + locale = null, + shadows = null, + textBaseline = null, + wordSpacing = null; + + static TextStyleDataDto from(TextStyle style) { + return TextStyleDataDto( + background: style.background, + backgroundColor: style.backgroundColor?.toDto(), + color: style.color?.toDto(), + debugLabel: style.debugLabel, + decoration: style.decoration, + decorationColor: style.decorationColor?.toDto(), + decorationStyle: style.decorationStyle, + decorationThickness: style.decorationThickness, + fontFamily: style.fontFamily, + fontFamilyFallback: style.fontFamilyFallback, + fontFeatures: style.fontFeatures, + fontSize: style.fontSize, + fontStyle: style.fontStyle, + fontWeight: style.fontWeight, + foreground: style.foreground, + height: style.height, + letterSpacing: style.letterSpacing, + locale: style.locale, + shadows: style.shadows?.map((e) => e.toDto()).toList(), + textBaseline: style.textBaseline, + wordSpacing: style.wordSpacing, + ); + } + + static TextStyleDataDto? maybeFrom(TextStyle? style) { + return style == null ? null : TextStyleDataDto.from(style); + } + + bool get isTokenRef => token != null; + + @override + TextStyleDataDto merge(TextStyleDataDto? other) { + if (other == null) return this; + assert( + token == null && other.token == null, + 'Cannot merge token refs', + ); + + return TextStyleDataDto( + background: other.background ?? background, + backgroundColor: other.backgroundColor ?? backgroundColor, + color: other.color ?? color, + debugLabel: other.debugLabel ?? debugLabel, + decoration: other.decoration ?? decoration, + decorationColor: other.decorationColor ?? decorationColor, + decorationStyle: other.decorationStyle ?? decorationStyle, + decorationThickness: other.decorationThickness ?? decorationThickness, + fontFamily: other.fontFamily ?? fontFamily, + fontFamilyFallback: [ + ...?fontFamilyFallback, + ...?other.fontFamilyFallback, + ], + fontFeatures: other.fontFeatures ?? fontFeatures, + fontSize: other.fontSize ?? fontSize, + fontStyle: other.fontStyle ?? fontStyle, + fontWeight: other.fontWeight ?? fontWeight, + foreground: other.foreground ?? foreground, + height: other.height ?? height, + letterSpacing: other.letterSpacing ?? letterSpacing, + locale: other.locale ?? locale, + shadows: shadows?.merge(other.shadows) ?? other.shadows, + textBaseline: other.textBaseline ?? textBaseline, + wordSpacing: other.wordSpacing ?? wordSpacing, + ); + } + + @override + TextStyle resolve(MixData mix) { + return isTokenRef + ? mix.tokens.textStyleToken(token!) + : TextStyle( + color: color?.resolve(mix), + backgroundColor: backgroundColor?.resolve(mix), + fontSize: fontSize, + fontWeight: fontWeight, + fontStyle: fontStyle, + letterSpacing: letterSpacing, + wordSpacing: wordSpacing, + textBaseline: textBaseline, + height: height, + locale: locale, + foreground: foreground, + background: background, + shadows: shadows?.map((e) => e.resolve(mix)).toList(), + fontFeatures: fontFeatures, + decoration: decoration, + decorationColor: decorationColor?.resolve(mix), + decorationStyle: decorationStyle, + decorationThickness: decorationThickness, + debugLabel: debugLabel, + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + ); + } + + @override + get props => [ + fontFamily, + fontWeight, + fontStyle, + fontSize, + letterSpacing, + wordSpacing, + textBaseline, + color, + backgroundColor, + shadows, + fontFeatures, + decoration, + decorationColor, + decorationStyle, + debugLabel, + locale, + height, + background, + foreground, + decorationThickness, + fontFamilyFallback, + token, + ]; +} + +@immutable +class TextStyleDto extends Dto with Mergeable { + final List value; + const TextStyleDto.raw(this.value); + + factory TextStyleDto.only({ + ColorDto? color, + ColorDto? backgroundColor, + double? fontSize, + FontWeight? fontWeight, + FontStyle? fontStyle, + double? letterSpacing, + String? debugLabel, + double? wordSpacing, + TextBaseline? textBaseline, + List? shadows, + List? fontFeatures, + TextDecoration? decoration, + ColorDto? decorationColor, + TextDecorationStyle? decorationStyle, + Locale? locale, + double? height, + Paint? foreground, + Paint? background, + double? decorationThickness, + String? fontFamily, + List? fontFamilyFallback, + }) { + return TextStyleDto(TextStyleDataDto( + background: background, + backgroundColor: backgroundColor, + color: color, + debugLabel: debugLabel, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, + decorationThickness: decorationThickness, + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontFeatures: fontFeatures, + fontSize: fontSize, + fontStyle: fontStyle, + fontWeight: fontWeight, + foreground: foreground, + height: height, + letterSpacing: letterSpacing, + locale: locale, + shadows: shadows, + textBaseline: textBaseline, + wordSpacing: wordSpacing, + )); + } + + factory TextStyleDto(TextStyleDataDto value) { + return TextStyleDto.raw([value]); + } + + factory TextStyleDto.token(TextStyleToken token) { + return TextStyleDto(TextStyleDataDto.token(token)); + } + + static TextStyleDto from(TextStyle style) { + return TextStyleDto(TextStyleDataDto.from(style)); + } + + static TextStyleDto? maybeFrom(TextStyle? style) { + return style == null ? null : TextStyleDto.from(style); + } + + // This method resolves the TextStyleAttribute to a TextStyle. + // It maps over the values list and checks if each TextStyleDto is a token reference. + // If it is, it resolves the token reference and converts it to a TextStyleDto. + // If it's not a token reference, it leaves the TextStyleDto as is. + // Then it reduces the list of TextStyleDto objects to a single TextStyleDto by merging them. + // Finally, it resolves the resulting TextStyleDto to a TextStyle. + @override + TextStyle resolve(MixData mix) { + return value + .map((e) => e.isTokenRef ? TextStyleDataDto.from(e.resolve(mix)) : e) + .reduce((value, element) => value.merge(element)) + .resolve(mix); + } + + @override + TextStyleDto merge(TextStyleDto? other) { + return other == null ? this : TextStyleDto.raw([...value, ...other.value]); + } + + @override + get props => [value]; +} diff --git a/lib/src/attributes/text_style/text_style_util.dart b/lib/src/attributes/text_style/text_style_util.dart new file mode 100644 index 000000000..1aae80197 --- /dev/null +++ b/lib/src/attributes/text_style/text_style_util.dart @@ -0,0 +1,504 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../core/extensions/values_ext.dart'; +import '../../theme/tokens/text_style_token.dart'; +import '../color/color_dto.dart'; +import '../color/color_util.dart'; +import '../scalars/scalar_util.dart'; +import '../shadow/shadow_dto.dart'; +import '../shadow/shadow_util.dart'; +import 'text_style_dto.dart'; + +/// A utility class for handling `TextStyle` for `Attribute`s. +/// +/// This class is part of a larger system for styling UI components. It extends `MixUtility` to leverage the flexibility and reusability of mixed utilities in a type-safe manner, focusing specifically on `TextStyleDto`. +/// +/// The `TextStyleUtility` provides methods for setting various text style attributes like color, font weight, font style, and more. These methods return the generic type `T`, allow to be used within multiple `Attribute` classes. +/// +/// The utility also includes specialized getters (e.g., `color`, `fontWeight`) that return specific utility classes. These classes offer more focused methods for manipulating individual aspects of a text style. +/// +/// Example: +/// ```dart +/// // Creating a TextStyleUtility instance for styling. +/// final textStyle = TextStyleUtility(builder); +/// +/// // You can use the call method to set multiple values at once. +/// final styledText = textStyle(fontWeight: FontWeight.bold, fontStyle: FontStyle.italic); +/// +/// // You can also use the specialized getters to manipulate specific aspects of the text style. +/// final bold = textStyle.fontWeight.bold(); +/// final italic = textStyle.fontStyle.italic(); +/// ``` +/// +/// See also: +/// - [MixUtility] +/// - [TextStyleDto] +/// - [TextStyle] + +class TextStyleUtility + extends DtoUtility { + const TextStyleUtility(super.builder) : super(valueToDto: TextStyleDto.from); + + T _only({ + ColorDto? color, + FontWeight? fontWeight, + FontStyle? fontStyle, + TextDecoration? decoration, + double? fontSize, + double? letterSpacing, + double? wordSpacing, + ColorDto? backgroundColor, + ColorDto? decorationColor, + TextDecorationStyle? decorationStyle, + TextBaseline? textBaseline, + List? shadows, + List? fontFeatures, + Paint? foreground, + Paint? background, + double? decorationThickness, + List? fontFamilyFallback, + Locale? locale, + String? debugLabel, + double? height, + }) { + final textStyle = TextStyleDto( + TextStyleDataDto( + background: background, + backgroundColor: backgroundColor, + color: color, + debugLabel: debugLabel, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, + decorationThickness: decorationThickness, + fontFamilyFallback: fontFamilyFallback, + fontFeatures: fontFeatures, + fontSize: fontSize, + fontStyle: fontStyle, + fontWeight: fontWeight, + foreground: foreground, + height: height, + letterSpacing: letterSpacing, + locale: locale, + shadows: shadows, + textBaseline: textBaseline, + wordSpacing: wordSpacing, + ), + ); + + return builder(textStyle); + } + + /// Returns a [ColorUtility] for manipulating the color of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + ColorUtility get color { + return ColorUtility((ColorDto color) => _only(color: color)); + } + + /// Returns a [FontWeightUtility] for manipulating the font weight of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final bold = textStyle.fontWeight.bold(); + /// ``` + /// + /// See also: + /// - [FontWeightUtility] + FontWeightUtility get fontWeight { + return FontWeightUtility((weight) => _only(fontWeight: weight)); + } + + /// Returns a [FontStyleUtility] for manipulating the font style of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final italic = textStyle.fontStyle.italic(); + /// ``` + /// + /// See also: + /// - [FontStyleUtility] + FontStyleUtility get fontStyle { + return FontStyleUtility((style) => _only(fontStyle: style)); + } + + /// Returns a [TextDecorationUtility] for manipulating the text decoration of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final underline = textStyle.decoration.underline(); + /// ``` + TextDecorationUtility get decoration { + return TextDecorationUtility( + (decoration) => _only(decoration: decoration), + ); + } + + /// Returns a [FontSizeUtility] for manipulating the font size of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final customFont = textStyle.fontSize(16); + /// ``` + /// See also: + /// - [FontSizeUtility] + FontSizeUtility get fontSize { + return FontSizeUtility((size) => _only(fontSize: size)); + } + + /// Returns a [ColorUtility] for manipulating the background color of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final customBackgroundColor = textStyle.backgroundColor(Colors.red); + /// ``` + /// + /// See also: + /// - [ColorUtility] + ColorUtility get backgroundColor { + return ColorUtility((color) => _only(backgroundColor: color)); + } + + /// Returns a [ColorUtility] for manipulating the decoration color of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final decorationColor = textStyle.decorationColor(Colors.red); + /// ``` + /// + /// See also: + /// - [ColorUtility] + ColorUtility get decorationColor { + return ColorUtility((color) => _only(decorationColor: color)); + } + + /// Returns a [ShadowUtility] for manipulating the shadow of the text style. + /// + /// This utility allows you to modify the color, offset, and blur radius + /// of the shadow. It uses the [ShadowUtility] class which provides + /// an interface to build upon the [ShadowDto] object. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final shadowed = textStyle.shadow(color: Colors.red, offset: Offset(1, 1), blurRadius: 1); + /// final offset = textStyle.shadow.offset(1, 1); + /// + /// ``` + /// + /// See also: + /// - [ColorUtility] + /// - [OffsetUtility] + /// - [DoubleUtility] + ShadowUtility get shadow { + return ShadowUtility((shadow) => _only(shadows: [shadow])); + } + + /// Returns a [TextDecorationStyleUtility] for manipulating the decoration style of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final dashed = textStyle.decorationStyle.dashed(); + /// ``` + /// + /// See also: + /// - [TextDecorationStyleUtility] + /// - [TextDecorationStyle] + TextDecorationStyleUtility get decorationStyle { + return TextDecorationStyleUtility( + (style) => _only(decorationStyle: style), + ); + } + + /// Returns a [TextBaselineUtility] for manipulating the text baseline of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final alphabetic = textStyle.textBaseline.alphabetic(); + /// ``` + /// + /// See also: + /// - [TextBaselineUtility] + /// - [TextBaseline] + TextBaselineUtility get textBaseline { + return TextBaselineUtility((baseline) => _only(textBaseline: baseline)); + } + + /// Returns a [FontFamilyUtility] for manipulating the font family of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final customFontFamily = textStyle.fontFamily('Roboto'); + /// ``` + /// + /// See also: + /// - [FontFamilyUtility] + FontFamilyUtility get fontFamily { + return FontFamilyUtility((fontFamily) => call(fontFamily: fontFamily)); + } + + /// Method for setting the height of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final customHeight = textStyle.height(1.5); + /// ``` + T height(double height) => _only(height: height); + + /// Method for setting the word spacing of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final customWordSpacing = textStyle.wordSpacing(1.5); + /// ``` + T wordSpacing(double wordSpacing) { + return _only(wordSpacing: wordSpacing); + } + + /// Method for setting the letter spacing of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final customLetterSpacing = textStyle.letterSpacing(1.5); + /// ``` + /// + /// See also: + /// - [DoubleUtility] + T letterSpacing(double letterSpacing) { + return _only(letterSpacing: letterSpacing); + } + + /// Method for setting the shadows of the text style. + /// + /// Example: + /// + /// ```dart + // final textStyle = TextStyleUtility(builder); + // final customShadows = textStyle.shadows([ + // Shadow(color: Colors.red, offset: Offset(1, 1), blurRadius: 1), + // ]); + /// ``` + T shadows(List shadows) { + return _only(shadows: shadows.map((e) => e.toDto()).toList()); + } + + /// Method alias for [FontStyle.italic]. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final italic = textStyle.italic(); + /// ``` + /// + /// See also: + /// * [FontStyle.italic] + T italic() => fontStyle.italic(); + + /// Method alias for [FontStyle.normal]. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final normal = textStyle.normal(); + /// ``` + /// + /// See also: + /// * [FontStyle.normal] + T bold() => fontWeight.bold(); + + /// Method for settings the foreground of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final foreground = textStyle.foreground(Paint()..color = Colors.red); + /// ``` + /// + /// See also: + /// * [Paint] + T foreground(Paint foreground) => call(foreground: foreground); + + /// Method for settings the background of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final background = textStyle.background(Paint()..color = Colors.red); + /// ``` + T background(Paint background) => call(background: background); + + /// Method for settings font features of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final fontFeatures = textStyle.fontFeatures([FontFeature.enable('smcp')]); + /// ``` + /// + /// See also: + /// * [FontFeature] + T fontFeatures(List fontFeatures) => + call(fontFeatures: fontFeatures); + + /// Method for setting the locale of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final locale = textStyle.locale(Locale('en')); + /// ``` + /// + /// See also: + /// * [Locale] + T locale(Locale locale) => call(locale: locale); + + /// Method for setting the debug label of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final debugLabel = textStyle.debugLabel('debug label'); + /// ``` + T debugLabel(String label) => call(debugLabel: label); + + /// Method for setting the decoration thickness of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final decorationThickness = textStyle.decorationThickness(1.0); + /// ``` + T decorationThickness(double thickness) => + call(decorationThickness: thickness); + + /// Method for setting the font family fallback of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final fontFamilyFallback = textStyle.fontFamilyFallback(['Roboto']); + /// ``` + T fontFamilyFallback(List fallback) => + call(fontFamilyFallback: fallback); + + /// Method for setting the `TextStyleToken` of the text style. + /// + /// Example: + /// + /// ```dart + /// final textStyle = TextStyleUtility(builder); + /// final token = textStyle.token(token); + /// ``` + /// + /// See also: + /// * [TextStyleToken] + T token(TextStyleToken token) => builder(TextStyleDto.token(token)); + + /// Callable method for setting multiple values at once. + /// + /// Example: + /// + /// ```dart + // final textStyle = TextStyleUtility(builder); + // final attribute = textStyle( + // fontWeight: FontWeight.bold, + // fontStyle: FontStyle.italic, + // fontFamily: 'Roboto', + // fontSize: 16, + // color: Colors.red, + // backgroundColor: Colors.blue, + // letterSpacing: 1.5, + // wordSpacing: 1.5, + // textBaseline: TextBaseline.alphabetic, + // ); + /// ``` + T call({ + String? fontFamily, + FontWeight? fontWeight, + FontStyle? fontStyle, + double? fontSize, + double? letterSpacing, + double? wordSpacing, + TextBaseline? textBaseline, + List? shadows, + Color? color, + Color? backgroundColor, + List? fontFeatures, + TextDecoration? decoration, + TextDecorationStyle? decorationStyle, + Locale? locale, + String? debugLabel, + List? fontFamilyFallback, + Paint? foreground, + Paint? background, + double? decorationThickness, + Color? decorationColor, + double? height, + }) { + final textStyle = TextStyleDto.only( + color: color?.toDto(), + backgroundColor: backgroundColor?.toDto(), + fontSize: fontSize, + fontWeight: fontWeight, + fontStyle: fontStyle, + letterSpacing: letterSpacing, + debugLabel: debugLabel, + wordSpacing: wordSpacing, + textBaseline: textBaseline, + shadows: shadows?.map((e) => e.toDto()).toList(), + fontFeatures: fontFeatures, + decoration: decoration, + decorationColor: decorationColor?.toDto(), + decorationStyle: decorationStyle, + locale: locale, + height: height, + foreground: foreground, + background: background, + decorationThickness: decorationThickness, + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + ); + + return builder(textStyle); + } +} diff --git a/lib/src/attributes/text_style_attribute.dart b/lib/src/attributes/text_style_attribute.dart deleted file mode 100644 index 47d146c7c..000000000 --- a/lib/src/attributes/text_style_attribute.dart +++ /dev/null @@ -1,160 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart'; - -import '../factory/mix_provider_data.dart'; -import '../theme/tokens/text_style_token.dart'; -import 'attribute.dart'; -import 'color_attribute.dart'; -import 'shadow_attribute.dart'; - -@immutable -class TextStyleAttribute extends VisualAttribute { - final String? fontFamily; - final FontWeight? fontWeight; - final FontStyle? fontStyle; - final double? fontSize; - final double? letterSpacing; - final double? wordSpacing; - final TextBaseline? textBaseline; - final ColorAttribute? color; - final ColorAttribute? backgroundColor; - final List? shadows; - final List? fontFeatures; - final TextDecoration? decoration; - final ColorAttribute? decorationColor; - final TextDecorationStyle? decorationStyle; - final Locale? locale; - final String? debugLabel; - final double? height; - final Paint? foreground; - final Paint? background; - final double? decorationThickness; - final List? fontFamilyFallback; - - final TextStyleToken? ref; - - const TextStyleAttribute({ - this.background, - this.backgroundColor, - this.color, - this.debugLabel, - this.decoration, - this.decorationColor, - this.decorationStyle, - this.decorationThickness, - this.fontFamily, - this.fontFamilyFallback, - this.fontFeatures, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.foreground, - this.height, - this.letterSpacing, - this.locale, - this.shadows, - this.ref, - this.textBaseline, - this.wordSpacing, - }); - - bool isRef() => ref != null; - - @override - TextStyleAttribute merge(TextStyleAttribute? other) { - if (other == null) return this; - - final haveRefs = ref == null || other.ref == null; - - assert( - haveRefs, - 'Cannot merge two different refs', - ); - - return TextStyleAttribute( - background: other.background ?? background, - backgroundColor: other.backgroundColor ?? backgroundColor, - color: other.color ?? color, - debugLabel: other.debugLabel ?? debugLabel, - decoration: other.decoration ?? decoration, - decorationColor: other.decorationColor ?? decorationColor, - decorationStyle: other.decorationStyle ?? decorationStyle, - decorationThickness: other.decorationThickness ?? decorationThickness, - fontFamily: other.fontFamily ?? fontFamily, - fontFamilyFallback: [ - ...?fontFamilyFallback, - ...?other.fontFamilyFallback, - ], - fontFeatures: other.fontFeatures ?? fontFeatures, - fontSize: other.fontSize ?? fontSize, - fontStyle: other.fontStyle ?? fontStyle, - fontWeight: other.fontWeight ?? fontWeight, - foreground: other.foreground ?? foreground, - height: other.height ?? height, - letterSpacing: other.letterSpacing ?? letterSpacing, - locale: other.locale ?? locale, - shadows: mergeMergeableList(shadows, other.shadows), - ref: other.ref ?? ref, - textBaseline: other.textBaseline ?? textBaseline, - wordSpacing: other.wordSpacing ?? wordSpacing, - ); - } - - @override - TextStyle resolve(MixData mix) { - // ignore: avoid-non-null-assertion - final textStyle = ref == null ? null : mix.resolver.textStyleToken(ref!); - - return textStyle ?? - TextStyle( - color: color?.resolve(mix), - backgroundColor: backgroundColor?.resolve(mix), - fontSize: fontSize, - fontWeight: fontWeight, - fontStyle: fontStyle, - letterSpacing: letterSpacing, - wordSpacing: wordSpacing, - textBaseline: textBaseline, - height: height, - locale: locale, - foreground: foreground, - background: background, - shadows: shadows?.map((e) => e.resolve(mix)).toList(), - fontFeatures: fontFeatures, - decoration: decoration, - decorationColor: decorationColor?.resolve(mix), - decorationStyle: decorationStyle, - decorationThickness: decorationThickness, - debugLabel: debugLabel, - fontFamily: fontFamily, - fontFamilyFallback: fontFamilyFallback, - ); - } - - @override - get props => [ - fontFamily, - fontWeight, - fontStyle, - fontSize, - letterSpacing, - wordSpacing, - textBaseline, - color, - backgroundColor, - shadows, - fontFeatures, - decoration, - decorationColor, - decorationStyle, - debugLabel, - locale, - height, - background, - foreground, - decorationThickness, - fontFamilyFallback, - ref, - ]; -} diff --git a/lib/src/attributes/variant_attribute.dart b/lib/src/attributes/variant_attribute.dart index 8e10b099f..416d094f0 100644 --- a/lib/src/attributes/variant_attribute.dart +++ b/lib/src/attributes/variant_attribute.dart @@ -1,25 +1,26 @@ import 'package:flutter/material.dart'; +import '../core/attribute.dart'; import '../factory/style_mix.dart'; import '../variants/context_variant.dart'; import '../variants/multi_variant.dart'; import '../variants/variant.dart'; -import 'attribute.dart'; @immutable -class VariantAttribute extends Attribute { +class VariantAttribute extends Attribute + with Mergeable> { final T variant; final StyleMix _style; const VariantAttribute(this.variant, StyleMix style) : _style = style; + Key get mergeKey => ObjectKey(variant); + StyleMix get value => _style; @override VariantAttribute merge(covariant VariantAttribute other) { - if (other.variant != variant) { - throw throwArgumentError(other); - } + if (other.variant != variant) throw throwArgumentError(other); return VariantAttribute(variant, _style.merge(other._style)); } @@ -28,17 +29,21 @@ class VariantAttribute extends Attribute { get props => [variant, value]; } +mixin WhenVariant on VariantAttribute { + bool when(BuildContext context); +} + @immutable -class ContextVariantAttribute extends VariantAttribute { +class ContextVariantAttribute extends VariantAttribute + with WhenVariant { const ContextVariantAttribute(super.variant, super.style); + @override bool when(BuildContext context) => variant.when(context); @override ContextVariantAttribute merge(ContextVariantAttribute other) { - if (other.variant != variant) { - throw throwArgumentError(other); - } + if (other.variant != variant) throw throwArgumentError(other); return ContextVariantAttribute(variant, _style.merge(other._style)); } @@ -53,11 +58,10 @@ ArgumentError throwArgumentError(T other) { } @immutable -class MultiVariantAttribute extends VariantAttribute { +class MultiVariantAttribute extends VariantAttribute + with WhenVariant { const MultiVariantAttribute(super.variant, super.style); - bool when(BuildContext context) => variant.when(context); - // Remove all variants in given a list VariantAttribute remove(Iterable variantsToRemove) { final variant = this.variant.remove(variantsToRemove); @@ -73,11 +77,12 @@ class MultiVariantAttribute extends VariantAttribute { bool matches(Iterable otherVariants) => variant.matches(otherVariants); + @override + bool when(BuildContext context) => variant.when(context); + @override MultiVariantAttribute merge(MultiVariantAttribute other) { - if (other.variant != variant) { - throw throwArgumentError(other); - } + if (other.variant != variant) throw throwArgumentError(other); return MultiVariantAttribute(variant, _style.merge(other._style)); } diff --git a/lib/src/core/attribute.dart b/lib/src/core/attribute.dart new file mode 100644 index 000000000..5ea5206c8 --- /dev/null +++ b/lib/src/core/attribute.dart @@ -0,0 +1,135 @@ +import 'package:flutter/material.dart'; + +import '../factory/mix_provider_data.dart'; +import '../helpers/compare_mixin.dart'; + +@immutable +abstract class Attribute with Comparable { + const Attribute(); +} + +abstract class StyleAttribute extends Attribute { + const StyleAttribute(); + + // Type used for combining attributes + Type get type; +} + +abstract class Dto with Comparable, Resolvable { + const Dto(); +} + +/// A mixin used when a [Dto] or [Attribute] can be resolved. +/// +/// This mixin provides a `resolve` method that accepts a [MixData] object and returns a value of type [Value]. +/// +/// Classes that use this mixin should implement the `resolve` method to define the resolution logic. +/// +/// Example usage: +/// +/// ```dart +/// class MyClass with Resolvable { +/// @override +/// String resolve(MixData mix) { +/// // Resolution logic goes here +/// } +/// } +/// ``` +/// +mixin Resolvable { + Value resolve(MixData mix); +} + +/// A mixin that provides the ability to merge with another object of the same type. +/// +/// The `Mergeable` mixin defines a single method `merge` that takes another object +/// of the same type and returns a new object that represents the merged result. +/// +/// This mixin is typically used by [Dto] or [Attribute] that +/// need to be merged with other instances of the same type. +/// +/// Example usage: +/// ```dart +/// class MyClass with Mergeable { +/// int id; +/// String name; +/// +/// MyClass merge(covariant MyClass other) { +/// return MyClass() +/// ..id = other.id ?? id +/// ..name = other.name ?? name; +/// } +/// } +/// ``` +/// The `merge` method should be implemented by classes that mix in this mixin. +mixin Mergeable { + T merge(covariant T? other); +} + +/// An abstract class representing a resolvable attribute. +/// +/// This class extends the [StyleAttribute] class and provides a generic type [Self] and [Value]. +/// The [Self] type represents the concrete implementation of the attribute, while the [Value] type represents the resolvable value. +abstract class ResolvableAttribute extends StyleAttribute + with Resolvable, Mergeable { + const ResolvableAttribute(); + + @override + Self merge(Self? other); + + @override + Type get type => Self; +} + +abstract class DtoAttribute, Value> + extends StyleAttribute with Resolvable, Mergeable { + final D value; + const DtoAttribute(this.value); + + @override + Value resolve(MixData mix) => value.resolve(mix); + + @override + Type get type => Self; + + @override + get props => [value]; +} + +mixin SingleChildRenderAttributeMixin + on StyleAttribute { + W build(MixData mix, Widget child); +} + +mixin MultiChildRenderAttributeMixin + on StyleAttribute { + W render(MixData mix, List children); +} + +@immutable +abstract class Spec> extends ThemeExtension + with Comparable, Mergeable { + const Spec(); + + Duration lerpDuration(Duration a, Duration b, double t) { + int lerpTicks = ((1 - t) * a.inMilliseconds + t * b.inMilliseconds).round(); + + return Duration(milliseconds: lerpTicks); + } + + int lerpInt(int a, int b, double t) { + return ((1 - t) * a + t * b).round(); + } + + N? genericNumLerp(N? a, N? b, double t) { + if (a == null && b == null) return null; + if (a == null) return b; + if (b == null) return a; + + return (a * (1 - t) + b * t) as N; + } + + P lerpSnap

(P from, P to, double t) { + return t < 0.5 ? from : to; + } +} diff --git a/lib/src/core/attributes_map.dart b/lib/src/core/attributes_map.dart index 906d66a0d..5b23cd3c2 100644 --- a/lib/src/core/attributes_map.dart +++ b/lib/src/core/attributes_map.dart @@ -2,57 +2,122 @@ import 'dart:collection'; -import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; -import '../attributes/attribute.dart'; -import '../helpers/extensions/iterable_ext.dart'; -import 'equality/compare_mixin.dart'; +import '../attributes/variant_attribute.dart'; +import '../helpers/compare_mixin.dart'; +import 'attribute.dart'; -@immutable -class VisualAttributeMap with Comparable { - final LinkedHashMap? _attributeMap; +class StyleAttributeMap with Comparable { + final LinkedHashMap? _map; - VisualAttributeMap._(this._attributeMap); + const StyleAttributeMap._(this._map); - factory VisualAttributeMap(List attributes) { - final attributeMap = LinkedHashMap(); + const StyleAttributeMap.empty() : _map = null; + + factory StyleAttributeMap(Iterable attributes) { + return StyleAttributeMap._(_mergeMap(attributes)); + } + + static LinkedHashMap _mergeMap( + Iterable attributes, + ) { + final map = LinkedHashMap(); for (final attribute in attributes) { - final type = attribute.runtimeType; - if (attributeMap.containsKey(type)) { - attributeMap[type] = attributeMap[type]!.merge(attribute); + final type = attribute.type; + + // If value cannot be merged just overwrite it + if (attribute is! Mergeable) { + map[type] = attribute; + continue; + } + + // If there is no saved attribute, just add it + final savedAttribute = map[type] as Mergeable?; + if (savedAttribute == null) { + map[type] = attribute; } else { - attributeMap[type] = attribute; + // If there is a saved attribute, merge it with the new one + map[type] = savedAttribute.merge(attribute); } } - return VisualAttributeMap._(attributeMap); + return map; + } + + int get length => _map?.length ?? 0; + + bool get isEmpty => _map?.isEmpty ?? true; + + bool get isNotEmpty => _map?.isNotEmpty ?? false; + + List get values => _map?.values.toList() ?? []; + + bool contains(StyleAttribute attribute) => + _map?.containsKey(attribute.type) ?? false; + + Attr? attributeOfType() => _map?[Attr] as Attr?; + + Iterable whereType() => + _map?.values.whereType() ?? []; + + StyleAttributeMap merge(StyleAttributeMap other) { + return StyleAttributeMap([...values, ...other.values]); } - @protected - LinkedHashMap get map => - _attributeMap ?? LinkedHashMap(); + @override + List get props => [_map ?? {}]; +} - int get length => map.length; +class VariantAttributeMap with Comparable { + final LinkedHashMap? _map; - bool get isEmpty => map.isEmpty; + const VariantAttributeMap._(this._map); - bool get isNotEmpty => map.isNotEmpty; + const VariantAttributeMap.empty() : _map = null; - Iterable get values => map.values; + factory VariantAttributeMap(Iterable attributes) { + return VariantAttributeMap._(_mergeMap(attributes)); + } - T? attributeOfType() => - map.values.whereType().firstMaybeNull; + static LinkedHashMap _mergeMap( + Iterable attributes, + ) { + final map = LinkedHashMap(); + for (final attribute in attributes) { + final type = attribute.mergeKey; + + // If there is no saved attribute, just add it + final savedAttribute = map[type] as Mergeable?; + if (savedAttribute == null) { + map[type] = attribute; + } else { + // If there is a saved attribute, merge it with the new one + map[type] = savedAttribute.merge(attribute); + } + } - Iterable whereType() { - return map.values.whereType(); + return map; } - VisualAttributeMap merge(VisualAttributeMap other) { - final list = [...values, ...other.values]; + int get length => _map?.length ?? 0; + + bool get isEmpty => _map?.isEmpty ?? true; + + bool get isNotEmpty => _map?.isNotEmpty ?? false; + + List get values => _map?.values.toList() ?? []; + + Iterable whereType() => + _map?.values.whereType() ?? []; + + bool contains(VariantAttribute attribute) => + _map?.containsKey(attribute.mergeKey) ?? false; - return VisualAttributeMap(list); + VariantAttributeMap merge(VariantAttributeMap other) { + return VariantAttributeMap([...values, ...other.values]); } @override - List get props => [_attributeMap ?? {}]; + List get props => [_map ?? {}]; } diff --git a/lib/src/core/constants.dart b/lib/src/core/constants.dart deleted file mode 100644 index f705a382b..000000000 --- a/lib/src/core/constants.dart +++ /dev/null @@ -1,2 +0,0 @@ -const kShortAliasDeprecation = - 'Short aliases will be deprecated, you can create your own. Example: final p = padding;'; diff --git a/lib/src/core/directive.dart b/lib/src/core/directive.dart new file mode 100644 index 000000000..17d443ab5 --- /dev/null +++ b/lib/src/core/directive.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; + +import '../helpers/compare_mixin.dart'; + +typedef Modifier = T Function(T value); + +/// The `Directive` abstract class provides a mechanism to modify a value of type `T`. +/// It is typically used in the context of an `Attribute`. +abstract class Directive with Comparable { + final Modifier _modifier; + const Directive(this._modifier); + + // An abstract method modify that takes a covariant parameter of type T + // This method is used to modify the value of type T + // The implementation of this method will be provided by the subclasses of Directive + T call(T value) => _modifier(value); + + @override + get props => [_modifier]; +} + +// abstract class DirectiveAttribute with Comparable { +// final List _directives; +// const DirectiveAttribute(this._directives); + +// List get value => _directives; + +// @override +// get props => [_directives]; +// } + +class TextDirective extends Directive { + const TextDirective(super.modifier); +} + +class ColorDirective extends Directive { + const ColorDirective(super.modifier); +} + +// class TextDirectiveAttribute extends DirectiveAttribute { +// const TextDirectiveAttribute.raw(super.directives); + +// factory TextDirectiveAttribute(TextDirective directive) { +// return TextDirectiveAttribute.raw([directive]); +// } + +// String modify(String text) => +// value.fold(text, (value, directive) => directive(value)); + +// @override +// TextDirectiveAttribute merge(covariant TextDirectiveAttribute? other) { +// return other == null +// ? this +// : TextDirectiveAttribute.raw([..._directives, ...other._directives]); +// } +// } diff --git a/lib/src/core/extensions/iterable_ext.dart b/lib/src/core/extensions/iterable_ext.dart new file mode 100644 index 000000000..e70a91e75 --- /dev/null +++ b/lib/src/core/extensions/iterable_ext.dart @@ -0,0 +1,52 @@ +import 'dart:math'; + +import '../attribute.dart'; + +extension IterableExt on Iterable { + T? get firstMaybeNull => isEmpty ? null : first; + + T? firstWhereOrNull(bool Function(T element) test) { + for (T element in this) { + if (test(element)) { + return element; + } + } + + return null; + } + + Iterable sorted([Comparator? compare]) { + List newList = List.of(this); + newList.sort(compare); + + return newList; + } +} + +extension ListExt on List { + List merge(List? other) { + if (other == null) return this; + + if (isEmpty) return other; + + final listLength = length; + final otherLength = other.length; + final maxLength = max(listLength, otherLength); + + return List.generate(maxLength, (int index) { + if (index < listLength && index < otherLength) { + final currentValue = this[index]; + final otherValue = other[index]; + if (currentValue is Mergeable) { + return currentValue.merge(otherValue); + } + + return otherValue ?? currentValue; + } else if (index < listLength) { + return this[index]; + } + + return other[index]; + }); + } +} diff --git a/lib/src/core/extensions/values_ext.dart b/lib/src/core/extensions/values_ext.dart new file mode 100644 index 000000000..3eccb3476 --- /dev/null +++ b/lib/src/core/extensions/values_ext.dart @@ -0,0 +1,163 @@ +import 'package:flutter/material.dart'; + +import '../../attributes/border/border_attribute.dart'; +import '../../attributes/border/border_dto.dart'; +import '../../attributes/border/border_radius_attribute.dart'; +import '../../attributes/border/border_radius_dto.dart'; +import '../../attributes/color/color_dto.dart'; +import '../../attributes/constraints/constraints_attribute.dart'; +import '../../attributes/constraints/constraints_dto.dart'; +import '../../attributes/decoration/decoration_attribute.dart'; +import '../../attributes/decoration/decoration_dto.dart'; +import '../../attributes/gradient/gradient_attribute.dart'; +import '../../attributes/gradient/gradient_dto.dart'; +import '../../attributes/scalars/scalars_attribute.dart'; +import '../../attributes/shadow/shadow_dto.dart'; +import '../../attributes/spacing/spacing_dto.dart'; +import '../../attributes/strut_style/strut_style_attribute.dart'; +import '../../attributes/strut_style/strut_style_dto.dart'; +import '../../attributes/text_style/text_style_attribute.dart'; +import '../../attributes/text_style/text_style_dto.dart'; + +extension StrutStyleExt on StrutStyle { + StrutStyleDto toDto() => StrutStyleDto.from(this); + + StrutStyleAttribute toAttribute() => StrutStyleAttribute(toDto()); +} + +extension GradientExt on Gradient { + GradientAttribute toAttribute() => GradientAttribute(toDto()); + + // toDto + GradientDto toDto() { + if (this is LinearGradient) return (this as LinearGradient).toDto(); + if (this is RadialGradient) return (this as RadialGradient).toDto(); + if (this is SweepGradient) return (this as SweepGradient).toDto(); + + throw UnimplementedError(); + } +} + +extension LinearGradientExt on LinearGradient { + LinearGradientDto toDto() => LinearGradientDto.from(this); +} + +extension RadialGradientExt on RadialGradient { + RadialGradientDto toDto() => RadialGradientDto.from(this); +} + +extension SweepGradientExt on SweepGradient { + SweepGradientDto toDto() => SweepGradientDto.from(this); +} + +extension BoxBorderExt on BoxBorder { + BoxBorderDto toDto() => BoxBorderDto.from(this); + + BoxBorderAttribute toAttribute() { + return BoxBorderAttribute(toDto()); + } +} + +extension EdgeInsetsGeometryExt on EdgeInsetsGeometry { + SpacingDto toDto() => SpacingDto.from(this); +} + +extension DoubleExt on double { + Radius toRadius() => Radius.circular(this); +} + +extension ColorExt on Color { + ColorDto toDto() => ColorDto(this); +} + +// Extension for Alignment +extension AlignmentGeometryExt on AlignmentGeometry { + AlignmentGeometryAttribute toAttribute() { + return AlignmentGeometryAttribute(this); + } +} + +extension BoxConstraintsExt on BoxConstraints { + BoxConstraintsDto toDto() => BoxConstraintsDto.from(this); + + BoxConstraintsAttribute toAttribute() => BoxConstraintsAttribute(toDto()); +} + +// Extension for Axis +extension AxisExt on Axis { + AxisAttribute toAttribute() => AxisAttribute(this); +} + +extension DecorationExt on Decoration { + DecorationDto toDto() { + if (this is BoxDecoration) return (this as BoxDecoration).toDto(); + if (this is ShapeDecoration) return (this as ShapeDecoration).toDto(); + + throw UnimplementedError('$runtimeType is not implemented.'); + } + + DecorationAttribute toAttribute() => DecorationAttribute(toDto()); +} + +extension BoxDecorationExt on BoxDecoration { + BoxDecorationDto toDto() => BoxDecorationDto.from(this); +} + +extension ShapeDecorationExt on ShapeDecoration { + ShapeDecorationDto toDto() => ShapeDecorationDto.from(this); +} + +extension BorderRadiusGeometryExt on BorderRadiusGeometry { + BorderRadiusGeometryAttribute toAttribute() { + return BorderRadiusGeometryAttribute(toDto()); + } + + BorderRadiusGeometryDto toDto() { + return BorderRadiusGeometryDto.from(this); + } +} + +extension Matrix4Ext on Matrix4 { + TransformAttribute toAttribute() => TransformAttribute(this); + + /// Merge [other] into this matrix. + Matrix4 merge(Matrix4? other) { + if (other == null || other == this) return this; + + return clone()..multiply(other); + } +} + +extension ClipExt on Clip { + ClipBehaviorAttribute toAttribute() => ClipBehaviorAttribute(this); +} + +extension BorderSideExt on BorderSide { + BorderSideDto toDto() => BorderSideDto.from(this); +} + +extension ShadowExt on Shadow { + ShadowDto toDto() => ShadowDto.from(this); +} + +extension ListShadowExt on List { + List toDto() { + return map((e) => e.toDto()).toList(); + } +} + +extension BoxShadowExt on BoxShadow { + BoxShadowDto toDto() => BoxShadowDto.from(this); +} + +extension ListBoxShadowExt on List { + List toDto() { + return map((e) => e.toDto()).toList(); + } +} + +extension TextStyleExt on TextStyle { + TextStyleDto toDto() => TextStyleDto.from(this); + + TextStyleAttribute toAttribute() => TextStyleAttribute(toDto()); +} diff --git a/lib/src/decorators/clip_decorator.dart b/lib/src/decorators/clip_decorator.dart index 8483d61e1..85ed93d4f 100644 --- a/lib/src/decorators/clip_decorator.dart +++ b/lib/src/decorators/clip_decorator.dart @@ -1,214 +1,123 @@ import 'package:flutter/material.dart'; -import '../attributes/attribute.dart'; import '../factory/mix_provider_data.dart'; import 'decorator.dart'; -abstract class ClipDecorator extends ParentDecorator> { - final CustomClipper? clipper; +class ClipPathDecorator extends WrapDecorator { final Clip? clipBehavior; + final CustomClipper? clipper; - const ClipDecorator({this.clipper, this.clipBehavior}); + const ClipPathDecorator({this.clipBehavior, this.clipper, super.key}); @override - ClipDecorator merge(covariant ClipDecorator other); - - @override - ClipDecoratorData resolve(MixData mix); - - @override - get props => [clipper, clipBehavior]; - - @override - Widget build(Widget child, covariant ClipDecoratorData data); -} - -class ClipDecoratorData extends MixExtension> { - final Clip? clipBehavior; - final CustomClipper? clipper; - - const ClipDecoratorData({this.clipper, this.clipBehavior}); - - @override - ClipDecoratorData lerp(ClipDecoratorData other, double t) { - return ClipDecoratorData( - clipper: snap(clipper, other.clipper, t), - clipBehavior: snap(clipBehavior, other.clipBehavior, t), + ClipPathDecorator merge(covariant ClipPathDecorator? other) { + return ClipPathDecorator( + clipBehavior: other?.clipBehavior ?? clipBehavior, + clipper: other?.clipper ?? clipper, ); } @override - ClipDecoratorData copyWith({ - Clip? clipBehavior, - CustomClipper? clipper, - }) { - return ClipDecoratorData( - clipper: clipper ?? this.clipper, - clipBehavior: clipBehavior ?? this.clipBehavior, - ); - } + get props => [clipBehavior, clipper]; @override - get props => [clipper, clipBehavior]; + Widget build(Widget child, MixData mix) { + return ClipPath( + key: key, + clipper: clipper, + clipBehavior: clipBehavior ?? Clip.antiAlias, + child: child, + ); + } } -class ClipOvalDecorator extends ClipDecorator { - const ClipOvalDecorator({super.clipper, super.clipBehavior}); +class ClipOvalDecorator extends WrapDecorator { + final Clip? clipBehavior; + final CustomClipper? clipper; + + const ClipOvalDecorator({this.clipper, this.clipBehavior, super.key}); @override - ClipOvalDecorator merge(ClipOvalDecorator other) { + ClipOvalDecorator merge(covariant ClipOvalDecorator? other) { return ClipOvalDecorator( - clipper: other.clipper ?? clipper, - clipBehavior: other.clipBehavior ?? clipBehavior, + clipper: other?.clipper ?? clipper, + clipBehavior: other?.clipBehavior ?? clipBehavior, ); } @override - ClipDecoratorData resolve(MixData mix) { - return ClipDecoratorData(clipper: clipper, clipBehavior: clipBehavior); - } + get props => [clipBehavior, clipper]; @override - Widget build(Widget child, ClipDecoratorData data) { + Widget build(Widget child, MixData mix) { return ClipOval( - clipper: data.clipper, - clipBehavior: data.clipBehavior ?? Clip.antiAlias, - child: child, - ); - } -} - -class ClipPathDecorator extends ClipDecorator { - const ClipPathDecorator({super.clipper, super.clipBehavior}); - - @override - ClipPathDecorator merge(ClipPathDecorator other) { - return ClipPathDecorator( - clipper: other.clipper, - clipBehavior: other.clipBehavior ?? clipBehavior, - ); - } - - @override - ClipDecoratorData resolve(MixData mix) { - return ClipDecoratorData( + key: key, clipper: clipper, clipBehavior: clipBehavior ?? Clip.antiAlias, - ); - } - - @override - Widget build(Widget child, ClipDecoratorData data) { - return ClipPath( - clipper: data.clipper, - clipBehavior: data.clipBehavior ?? Clip.antiAlias, child: child, ); } } -class ClipRRectDecoratorData extends ClipDecoratorData { - final BorderRadius? borderRadius; - - const ClipRRectDecoratorData({ - required this.borderRadius, - super.clipBehavior, - super.clipper, - }); - - @override - ClipRRectDecoratorData lerp(ClipRRectDecoratorData other, double t) { - return ClipRRectDecoratorData( - borderRadius: BorderRadius.lerp(borderRadius, other.borderRadius, t), - clipBehavior: snap(clipBehavior, other.clipBehavior, t), - clipper: snap(clipper, other.clipper, t), - ); - } - - @override - ClipRRectDecoratorData copyWith({ - BorderRadius? borderRadius, - Clip? clipBehavior, - CustomClipper? clipper, - }) { - return ClipRRectDecoratorData( - borderRadius: borderRadius ?? this.borderRadius, - clipBehavior: clipBehavior ?? this.clipBehavior, - clipper: clipper ?? this.clipper, - ); - } - - @override - get props => [borderRadius, clipper, clipBehavior]; -} +class ClipRectDecorator extends WrapDecorator { + final Clip? clipBehavior; + final CustomClipper? clipper; -class ClipRectDecorator extends ClipDecorator { - const ClipRectDecorator({super.clipper, super.clipBehavior}); + const ClipRectDecorator({this.clipBehavior, this.clipper, super.key}); @override - ClipRectDecorator merge(ClipRectDecorator other) { + ClipRectDecorator merge(covariant ClipRectDecorator? other) { return ClipRectDecorator( - clipper: other.clipper, - clipBehavior: other.clipBehavior ?? clipBehavior, + clipBehavior: other?.clipBehavior ?? clipBehavior, + clipper: other?.clipper ?? clipper, ); } @override - ClipDecoratorData resolve(MixData mix) { - return ClipDecoratorData( - clipper: clipper, - clipBehavior: clipBehavior ?? Clip.hardEdge, - ); - } - - @override - get props => [clipper, clipBehavior]; + get props => [clipBehavior, clipper]; @override - Widget build(Widget child, ClipDecoratorData data) { + Widget build(Widget child, MixData mix) { return ClipRect( - clipper: data.clipper, - clipBehavior: data.clipBehavior ?? Clip.hardEdge, + key: key, + clipper: clipper, + clipBehavior: clipBehavior ?? Clip.antiAlias, child: child, ); } } -class ClipRRectDecorator extends ClipDecorator { +class ClipRRectDecorator extends WrapDecorator { + final Clip? clipBehavior; final BorderRadius? borderRadius; + final CustomClipper? clipper; + const ClipRRectDecorator({ - super.clipper, - super.clipBehavior, + this.clipBehavior, this.borderRadius, + this.clipper, + super.key, }); @override - ClipRRectDecorator merge(ClipRRectDecorator other) { + ClipRRectDecorator merge(covariant ClipRRectDecorator? other) { return ClipRRectDecorator( - clipper: other.clipper, - clipBehavior: other.clipBehavior ?? clipBehavior, - borderRadius: other.borderRadius ?? borderRadius, + clipBehavior: other?.clipBehavior ?? clipBehavior, + borderRadius: other?.borderRadius ?? borderRadius, + clipper: other?.clipper ?? clipper, ); } @override - ClipRRectDecoratorData resolve(MixData mix) { - return ClipRRectDecoratorData( - borderRadius: borderRadius ?? BorderRadius.zero, - clipBehavior: clipBehavior ?? Clip.antiAlias, - clipper: clipper, - ); - } + get props => [clipBehavior, borderRadius, clipper]; @override - get props => [clipper, clipBehavior, borderRadius]; - - @override - Widget build(Widget child, ClipRRectDecoratorData data) { + Widget build(Widget child, MixData mix) { return ClipRRect( - borderRadius: data.borderRadius ?? BorderRadius.zero, - clipper: data.clipper, - clipBehavior: data.clipBehavior ?? Clip.antiAlias, + key: key, + borderRadius: borderRadius ?? BorderRadius.zero, + clipper: clipper, + clipBehavior: clipBehavior ?? Clip.antiAlias, child: child, ); } diff --git a/lib/src/decorators/decorator.dart b/lib/src/decorators/decorator.dart index 89f7833b4..0eaf6f146 100644 --- a/lib/src/decorators/decorator.dart +++ b/lib/src/decorators/decorator.dart @@ -1,27 +1,19 @@ import 'package:flutter/material.dart'; -import '../attributes/attribute.dart'; +import '../core/attribute.dart'; import '../factory/mix_provider_data.dart'; -abstract class Decorator extends VisualAttribute { - const Decorator(); - - Widget render(Widget child, MixData mix) { - return build(child, resolve(mix)); - } - - @override - Decorator merge(covariant Decorator? other); - - Widget build(Widget child, T data); +abstract class Decorator extends StyleAttribute { + final Key? key; + const Decorator({required this.key}); } -abstract class ParentDecorator extends Decorator { - const ParentDecorator(); +abstract class WrapDecorator> extends Decorator + with Mergeable { + const WrapDecorator({super.key}); @override - ParentDecorator merge(covariant ParentDecorator? other); + Type get type => Self; - @override - Widget build(Widget child, T data); + Widget build(Widget child, MixData mix); } diff --git a/lib/src/decorators/default_decorators.dart b/lib/src/decorators/default_decorators.dart index cd2a12121..302837080 100644 --- a/lib/src/decorators/default_decorators.dart +++ b/lib/src/decorators/default_decorators.dart @@ -1,121 +1,106 @@ +// ignore_for_file: prefer-named-boolean-parameters + import 'package:flutter/material.dart'; -import '../factory/mix_provider_data.dart'; import 'decorator.dart'; -class AspectRatioDecorator extends ParentDecorator { - final double aspectRatio; - const AspectRatioDecorator(this.aspectRatio); +class AspectRatioDecorator extends WrapDecorator { + final double value; + const AspectRatioDecorator(this.value, {super.key}); @override - AspectRatioDecorator merge(AspectRatioDecorator other) { - return AspectRatioDecorator(other.aspectRatio); - } + AspectRatioDecorator merge(AspectRatioDecorator? other) => other ?? this; + + @override + get props => [value]; @override - double resolve(MixData mix) => aspectRatio; + Widget build(child, mix) => + AspectRatio(key: key, aspectRatio: value, child: child); +} + +class VisibilityDecorator extends WrapDecorator { + final bool value; + const VisibilityDecorator(this.value, {super.key}); @override - get props => [aspectRatio]; + VisibilityDecorator merge(VisibilityDecorator? other) => other ?? this; @override - Widget build(Widget child, double data) => - AspectRatio(aspectRatio: data, child: child); + get props => [value]; + + @override + Widget build(child, mix) => + Visibility(key: key, visible: value, child: child); } -class FlexibleDecorator extends ParentDecorator { +class FlexibleDecorator extends WrapDecorator { final int? flex; - final FlexFit? flexFit; - const FlexibleDecorator({this.flex, this.flexFit}); - - const FlexibleDecorator.tight([this.flex]) : flexFit = FlexFit.tight; - const FlexibleDecorator.loose([this.flex]) : flexFit = FlexFit.loose; + final FlexFit? fit; + const FlexibleDecorator({this.flex, this.fit, super.key}); @override - FlexibleDecorator merge(FlexibleDecorator other) { + FlexibleDecorator merge(FlexibleDecorator? other) { return FlexibleDecorator( - flex: other.flex ?? flex, - flexFit: other.flexFit ?? flexFit, + flex: other?.flex ?? flex, + fit: other?.fit ?? fit, ); } @override - FlexibleDecoratorSpec resolve(MixData mix) { - return FlexibleDecoratorSpec(flex: flex, flexFit: flexFit); - } - - @override - get props => [flexFit, flex]; + get props => [flex, fit]; @override - Widget build(child, data) { + Widget build(child, mix) { return Flexible( - flex: data.flex ?? 1, - fit: data.flexFit ?? FlexFit.loose, + key: key, + flex: flex ?? 1, + fit: fit ?? FlexFit.loose, child: child, ); } } -class FlexibleDecoratorSpec { - final int? flex; - final FlexFit? flexFit; - const FlexibleDecoratorSpec({required this.flex, required this.flexFit}); -} - -class OpacityDecorator extends ParentDecorator { +class OpacityDecorator extends WrapDecorator { final double value; - const OpacityDecorator(this.value); + const OpacityDecorator(this.value, {super.key}); @override - OpacityDecorator merge(OpacityDecorator? other) { - return OpacityDecorator(other?.value ?? value); - } - - @override - double resolve(MixData mix) => value; + OpacityDecorator merge(OpacityDecorator? other) => other ?? this; @override get props => [value]; @override - Widget build(child, data) => Opacity(opacity: data, child: child); + Widget build(child, mix) => Opacity(key: key, opacity: value, child: child); } -class RotateDecorator extends ParentDecorator { +class RotateDecorator extends WrapDecorator { final int value; - const RotateDecorator(this.value); - - @override - RotateDecorator merge(RotateDecorator other) { - return RotateDecorator(other.value); - } + const RotateDecorator(this.value, {super.key}); @override - int resolve(MixData mix) => value; + RotateDecorator merge(RotateDecorator? other) => other ?? this; @override get props => [value]; @override - Widget build(child, data) => RotatedBox(quarterTurns: data, child: child); + Widget build(child, mix) => + RotatedBox(key: key, quarterTurns: value, child: child); } -class ScaleDecorator extends ParentDecorator { - final double scale; - const ScaleDecorator(this.scale); - - @override - ScaleDecorator merge(ScaleDecorator? other) { - return ScaleDecorator(other?.scale ?? scale); - } +class ScaleDecorator extends WrapDecorator { + final double value; + const ScaleDecorator(this.value, {super.key}); @override - double resolve(MixData mix) => scale; + ScaleDecorator merge(ScaleDecorator? other) => other ?? this; @override - get props => [scale]; + get props => [value]; @override - Widget build(child, data) => Transform.scale(scale: data, child: child); + Widget build(child, mix) => + Transform.scale(key: key, scale: value, child: child); } diff --git a/lib/src/deprecations.dart b/lib/src/deprecations.dart index c4cdd88ba..acbce422b 100644 --- a/lib/src/deprecations.dart +++ b/lib/src/deprecations.dart @@ -2,31 +2,11 @@ import 'dart:ui'; import 'package:flutter/material.dart'; -import 'attributes/alignment_attribute.dart'; -import 'attributes/attribute.dart'; -import 'attributes/border/border_radius_attribute.dart'; -import 'attributes/scalar_attribute.dart'; -import 'attributes/style_mix_attribute.dart'; -import 'attributes/text_style_attribute.dart'; -import 'core/constants.dart'; -import 'directives/text_directive.dart'; -import 'factory/mix_provider_data.dart'; -import 'factory/style_mix.dart'; -import 'helpers/extensions/values_ext.dart'; -import 'theme/tokens/space_token.dart'; -import 'utils/alignment_util.dart'; -import 'utils/border_radius_util.dart'; -import 'utils/border_util.dart'; -import 'utils/box_constraints_util.dart'; -import 'utils/context_variant_util.dart'; -import 'utils/decoration_util.dart'; -import 'utils/decorators_util.dart'; -import 'utils/helper_util.dart'; -import 'utils/pressable_util.dart'; -import 'utils/scalar_util.dart'; -import 'utils/space_util.dart'; -import 'utils/text_util.dart'; -import 'variants/variant.dart'; +import '../mix.dart'; +import 'attributes/text_style/text_style_dto.dart'; + +const kShortAliasDeprecation = + 'Short aliases will be deprecated, you can create your own. Example: final p = padding;'; extension DeprecatedMixExtension on StyleMix { /// Adds an Attribute to a Mix. @@ -90,23 +70,26 @@ extension DeprecatedMixExtension on StyleMix { @Deprecated('Use MixData instead.') typedef MixContext = MixData; -extension WithSpaceTokensExt on UtilityWithSpaceTokens { +extension WithSpaceTokensExt + on SpacingSideUtility { @Deprecated('Use xsmall instead') - T get xs => this.xsmall; + T get xs => this.xsmall(); @Deprecated('Use small instead') - T get sm => this.small; + T get sm => this.small(); @Deprecated('Use medium instead') - T get md => this.medium; + T get md => this.medium(); @Deprecated('Use large instead') - T get lg => this.large; + T get lg => this.large(); @Deprecated('Use xlarge instead') - T get xl => xlarge; + T get xl => xlarge(); @Deprecated('Use xxlarge instead') - T get xxl => xxlarge; + T get xxl => xxlarge(); } @Deprecated('Use mainAxisAlignment instead') -const mainAxis = mainAxisAlignment; +FlexMixAttribute mainAxis(MainAxisAlignment mainAxisAlignment) { + return FlexMixAttribute(mainAxisAlignment: mainAxisAlignment); +} @Deprecated('Use onXSmall instead') final xsmall = onXSmall; @@ -151,71 +134,70 @@ final press = onPress; const not = onNot; @Deprecated('Use textStyle instead') -const font = textStyle; +final font = text.style; -@Deprecated('Use textStyle(textShadow: textShadow) instead') -TextStyleAttribute textShadow(List textShadow) { - return textStyle(shadows: textShadow); +@Deprecated( + 'Use text.style(shadows: shadows) or text.style.shadows(shadows) instead', +) +TextMixAttribute textShadow(List shadows) { + return text.style(shadows: shadows); } -@Deprecated('Use flexible or expanded') -const flex = flexible; - -@Deprecated('Use textStyle(textShadow: textShadow) instead') +@Deprecated('Use text.style(shadow: shadow) instead') const fontWeight = LegacyTextStyleUtility.fontWeight; -@Deprecated('Use textStyle(letterSpacing: letterSpacing) instead') +@Deprecated('Use text.style(letterSpacing: letterSpacing) instead') const letterSpacing = LegacyTextStyleUtility.letterSpacing; -@Deprecated('Use textStyle(debugLabel: debugLabel) instead') +@Deprecated('Use text.style(debugLabel: debugLabel) instead') const debugLabel = LegacyTextStyleUtility.debugLabel; -@Deprecated('Use textStyle(height: height) instead') +@Deprecated('Use text.style(height: height) instead') const textHeight = LegacyTextStyleUtility.textHeight; -@Deprecated('Use textStyle(wordSpacing: wordSpacing) instead') +@Deprecated('Use text.style(wordSpacing: wordSpacing) instead') const wordSpacing = LegacyTextStyleUtility.wordSpacing; -@Deprecated('Use textStyle(fontStyle: fontStyle) instead') +@Deprecated('Use text.style(fontStyle: fontStyle) instead') const fontStyle = LegacyTextStyleUtility.fontStyle; -@Deprecated('Use textStyle(fontSize: fontSize) instead') +@Deprecated('Use text.style(fontSize: fontSize) instead') const fontSize = LegacyTextStyleUtility.fontSize; -@Deprecated('Use textStyle(inherit: inherit) instead') +@Deprecated('Use text.style(inherit: inherit) instead') const inherit = LegacyTextStyleUtility.inherit; -@Deprecated('Use textStyle(color: color) instead') +@Deprecated('Use text.style(color: color) instead') const textColor = LegacyTextStyleUtility.textColor; -@Deprecated('Use textStyle(backgroundColor: backgroundColor) instead') +@Deprecated('Use text.style(backgroundColor: backgroundColor) instead') const textBgColor = LegacyTextStyleUtility.textBgColor; -@Deprecated('Use textStyle(foreground: foreground) instead') +@Deprecated('Use text.style(foreground: foreground) instead') const textForeground = LegacyTextStyleUtility.textForeground; -@Deprecated('Use textStyle(background: background) instead') +@Deprecated('Use text.style(background: background) instead') const textBackground = LegacyTextStyleUtility.textBackground; -@Deprecated('Use textStyle(shadows: shadows) instead') +@Deprecated('Use text.style(shadows: shadows) instead') const textShadows = LegacyTextStyleUtility.textShadow; -@Deprecated('Use textStyle(fontFeatures: fontFeatures) instead') +@Deprecated('Use text.style(fontFeatures: fontFeatures) instead') const fontFeatures = LegacyTextStyleUtility.fontFeatures; -@Deprecated('Use textStyle(decoration: decoration) instead') +@Deprecated('Use text.style(decoration: decoration) instead') const textDecoration = LegacyTextStyleUtility.textDecoration; -@Deprecated('Use textStyle(decorationColor: decorationColor) instead') +@Deprecated('Use text.style(decorationColor: decorationColor) instead') const textDecorationColor = LegacyTextStyleUtility.textDecorationColor; -@Deprecated('Use textStyle(decorationStyle: decorationStyle) instead') +@Deprecated('Use text.style(decorationStyle: decorationStyle) instead') const textDecorationStyle = LegacyTextStyleUtility.textDecorationStyle; -@Deprecated('Use textStyle(decorationThickness: decorationThickness) instead') +@Deprecated('Use text.style(decorationThickness: decorationThickness) instead') const textDecorationThickness = LegacyTextStyleUtility.textDecorationThickness; -@Deprecated('Use textStyle(fontFamilyFallback: fontFamilyFallback) instead') +@Deprecated('Use text.style(fontFamilyFallback: fontFamilyFallback) instead') const fontFamilyFallback = LegacyTextStyleUtility.fontFamilyFallback; class LegacyTextStyleUtility { @@ -308,7 +290,7 @@ StyleMixAttribute _apply(Iterable mixes) { } @Deprecated(kShortAliasDeprecation) -final p = padding; +const p = padding; @Deprecated(kShortAliasDeprecation) final pt = paddingTop; @@ -335,10 +317,10 @@ final px = paddingHorizontal; final py = paddingVertical; @Deprecated(kShortAliasDeprecation) -const pi = paddingFrom; +final pi = padding.as; @Deprecated(kShortAliasDeprecation) -final m = margin; +const m = margin; @Deprecated(kShortAliasDeprecation) final mt = marginTop; @Deprecated(kShortAliasDeprecation) @@ -356,7 +338,7 @@ final mx = marginHorizontal; @Deprecated(kShortAliasDeprecation) final my = marginVertical; @Deprecated(kShortAliasDeprecation) -const mi = marginFrom; +final mi = margin.as; @Deprecated(kShortAliasDeprecation) final marginX = marginHorizontal; @@ -364,20 +346,23 @@ final marginX = marginHorizontal; @Deprecated(kShortAliasDeprecation) final marginY = marginVertical; -@Deprecated(kShortAliasDeprecation) -const r = rounded; +@Deprecated('Use borderRadius instead') +const rounded = borderRadius; -@Deprecated(kShortAliasDeprecation) -const roundedH = roundedHorizontal; +@Deprecated('Use borderRadius instead') +const r = borderRadius; -@Deprecated(kShortAliasDeprecation) -const roundedV = roundedVertical; +@Deprecated('Use borderRadius.horizontal instead') +dynamic get roundedH => UnimplementedError(); + +@Deprecated('use borderRadius.vertical instead') +dynamic get roundedV => UnimplementedError(); @Deprecated(kShortAliasDeprecation) dynamic get roundedDH => UnimplementedError(); @Deprecated(kShortAliasDeprecation) -const roundedTL = roundedTopLeft; +final roundedTL = borderRadius.topLeft; @Deprecated(kShortAliasDeprecation) BorderRadiusGeometryAttribute roundedTR() { @@ -385,38 +370,35 @@ BorderRadiusGeometryAttribute roundedTR() { } @Deprecated(kShortAliasDeprecation) -BorderRadiusAttribute roundedBL() { +BorderRadiusGeometryAttribute roundedBL() { throw UnimplementedError(); } @Deprecated(kShortAliasDeprecation) -BorderRadiusAttribute roundedBR() { +BorderRadiusGeometryAttribute roundedBR() { throw UnimplementedError(); } @Deprecated(kShortAliasDeprecation) -BorderRadiusDirectionalAttribute roundedTS() { +BorderRadiusGeometryAttribute roundedTS() { throw UnimplementedError(); } @Deprecated(kShortAliasDeprecation) -BorderRadiusDirectionalAttribute roundedTE() { +BorderRadiusGeometryAttribute roundedTE() { throw UnimplementedError(); } @Deprecated(kShortAliasDeprecation) -BorderRadiusDirectionalAttribute roundedBS() { +BorderRadiusGeometryAttribute roundedBS() { throw UnimplementedError(); } @Deprecated(kShortAliasDeprecation) -BorderRadiusDirectionalAttribute roundedBE() { +BorderRadiusGeometryAttribute roundedBE() { throw UnimplementedError(); } -@Deprecated(kShortAliasDeprecation) -const bgColor = backgroundColor; - @Deprecated(kShortAliasDeprecation) const h = height; @@ -424,34 +406,34 @@ const h = height; const w = width; @Deprecated(kShortAliasDeprecation) -const maxH = maxHeight; +final maxH = maxHeight; @Deprecated(kShortAliasDeprecation) -const maxW = maxWidth; +final maxW = maxWidth; @Deprecated(kShortAliasDeprecation) -const minH = minHeight; +final minH = minHeight; @Deprecated(kShortAliasDeprecation) -const minW = minWidth; +final minW = minWidth; @Deprecated(kShortAliasDeprecation) -const bt = borderTop; +final bt = border.top; @Deprecated(kShortAliasDeprecation) -const bb = borderBottom; +final bb = border.bottom; @Deprecated(kShortAliasDeprecation) -const bl = borderLeft; +final bl = border.left; @Deprecated(kShortAliasDeprecation) -const br = borderRight; +final br = border.right; @Deprecated(kShortAliasDeprecation) -const bs = borderStart; +final bs = border.start; @Deprecated(kShortAliasDeprecation) -const be = borderEnd; +final be = border.end; @Deprecated('Use alignment instead') const align = alignment; @@ -462,13 +444,13 @@ AlignmentGeometryAttribute zAligmnent(Alignment alignment) { } @Deprecated('Use stackFit instead') -StackFitAttribute zFit(StackFit fit) { - return stackFit(fit); +StackMixAttribute zFit(StackFit fit) { + return StackMixAttribute(fit: fit); } @Deprecated('Use stack instead') -ClipAttribute zClip(Clip clip) { - return ClipAttribute(clip); +StackMixAttribute zClip(Clip clip) { + return StackMixAttribute(clipBehavior: clip); } // Create a FlexAttributes for the direction axis. @@ -479,18 +461,18 @@ AxisAttribute direction(Axis direction) { // Create a FlexAttributes for the cross axis. @Deprecated('Use crossAxisAlignment() instead') -CrossAxisAlignmentAttribute crossAxis(CrossAxisAlignment crossAxisAlignment) { - return CrossAxisAlignmentAttribute(crossAxisAlignment); +FlexMixAttribute crossAxis(CrossAxisAlignment crossAxisAlignment) { + return FlexMixAttribute(crossAxisAlignment: crossAxisAlignment); } @Deprecated('Use textDirective(directive)') -TextDirectiveAttribute directives(List directives) { - return TextDirectiveAttribute(directives); +TextMixAttribute directives(List directives) { + return TextMixAttribute(directives: directives); } @Deprecated('Use textDirective(directive)') -TextDirectiveAttribute directive(TextDirective directive) { - return TextDirectiveAttribute([directive]); +TextMixAttribute directive(TextDirective directive) { + return TextMixAttribute(directives: [directive]); } @Deprecated('Locale is now passed to StyledText widget') @@ -499,6 +481,144 @@ TextStyleAttribute locale() { } @Deprecated('Use text(overflow: overflow)') -TextOverflowAttribute overflow(TextOverflow overflow) { - return TextOverflowAttribute(overflow); +TextMixAttribute overflow(TextOverflow overflow) { + return TextMixAttribute(overflow: overflow); +} + +@Deprecated('use margin.only instead') +final marginOnly = margin.only; + +@Deprecated('use margin.only instead') +final marginDirectionalOnly = margin.only; + +@Deprecated('use margin.all instead') +final marginAll = margin.all; + +@Deprecated('use margin.top instead') +final marginTop = margin.top; + +@Deprecated('use margin.bottom instead') +final marginBottom = margin.bottom; + +@Deprecated('use margin.left instead') +final marginLeft = margin.left; + +@Deprecated('use margin.right instead') +final marginRight = margin.right; + +@Deprecated('use margin.start instead') +final marginStart = margin.start; + +@Deprecated('use margin.end instead') +final marginEnd = margin.end; + +@Deprecated('use margin.horizontal instead') +final marginHorizontal = margin.horizontal; + +@Deprecated('use margin.vertical instead') +final marginVertical = margin.vertical; + +@Deprecated('use marginFrom instead') +final marginFrom = margin.as; + +@Deprecated('use padding.only instead') +final paddingOnly = padding.only; + +@Deprecated('use padding.only instead') +final paddingDirectionalOnly = padding.only; + +@Deprecated('use padding.all instead') +final paddingAll = padding.all; + +@Deprecated('use padding.top instead') +final paddingTop = padding.top; + +@Deprecated('use padding.bottom instead') +final paddingBottom = padding.bottom; + +@Deprecated('use padding.left instead') +final paddingLeft = padding.left; + +@Deprecated('use padding.right instead') +final paddingRight = padding.right; + +@Deprecated('use padding.start instead') +final paddingStart = padding.start; + +@Deprecated('use padding.end instead') +final paddingEnd = padding.end; + +@Deprecated('use padding.horizontal instead') +final paddingHorizontal = padding.horizontal; + +@Deprecated('use padding.vertical instead') +final paddingVertical = padding.vertical; + +@Deprecated('use paddingFrom instead') +final paddingFrom = padding.as; + +@Deprecated('use border.top instead') +final borderTop = border.top; + +@Deprecated('use border.bottom instead') +final borderBottom = border.bottom; + +@Deprecated('use border.left instead') +final borderLeft = border.left; + +@Deprecated('use border.right instead') +final borderRight = border.right; + +@Deprecated('use border.start instead') +final borderStart = border.start; + +@Deprecated('use border.end instead') +final borderEnd = border.end; + +@Deprecated('use border.horizontal instead') +final borderHorizontal = border.horizontal; + +@Deprecated('use border.vertical instead') +final borderVertical = border.vertical; + +@Deprecated('use border.all instead') +final borderAll = border.all; + +@Deprecated('Use StyledText now') +typedef TextMix = StyledText; + +@Deprecated('Use text.style instead') +final textStyle = text.style; + +@Deprecated('Use text.style.shadow instead') +final shadow = text.style.shadow; + +@Deprecated('use backgroundColor instead') +const bgColor = backgroundColor; + +// do no tuse main axisaligmnet use flex.mainAxisAlignment instead +@Deprecated( + 'use flex(mainAxisAlignment:value) instead or flex.mainAxisAlignment', +) +FlexMixAttribute mainAxisAlignment(MainAxisAlignment mainAxisAlignment) { + return FlexMixAttribute(mainAxisAlignment: mainAxisAlignment); +} + +@Deprecated( + 'use flex(crossAxisAlignment:value) instead or flex.crossAxisAlignment', +) +FlexMixAttribute crossAlignment(CrossAxisAlignment crossAxisAlignment) { + return FlexMixAttribute(crossAxisAlignment: crossAxisAlignment); +} + +@Deprecated('use flex(mainAxisSize:value) instead or flex.mainAxisSize') +FlexMixAttribute mainAxisSize(MainAxisSize mainAxisSize) { + return flex(mainAxisSize: mainAxisSize); +} + +@Deprecated('use text.style.bold() instead') +TextMixAttribute bold() { + return TextMixAttribute( + style: TextStyleDto.only(fontWeight: FontWeight.bold), + ); } diff --git a/lib/src/directives/directives/controllers.dart b/lib/src/directives/controllers.dart similarity index 100% rename from lib/src/directives/directives/controllers.dart rename to lib/src/directives/controllers.dart index cb6e13e99..e6af8d4ae 100644 --- a/lib/src/directives/directives/controllers.dart +++ b/lib/src/directives/controllers.dart @@ -40,31 +40,6 @@ class TextDecodingController { // Constructor for the [TextDecodingController] class. TextDecodingController(Function(String value) fn) : _fn = fn; - /// Updates the data to be decoded in animation. - /// - /// The given string [newText] is the target result of the animation and signifies the end-state. - void setData(String newText) { - final length = max(_data.length, newText.length); - final oldText = _data.padRight(length); - newText = newText.padRight(length); - - // Clear previous queue and populate it with characters from the new string. - _queue.clear(); - for (int i = 0; i < length; i++) { - final from = oldText[i]; - final to = newText[i]; - final start = _random.nextInt(200); - final end = start + _random.nextInt(200); - _queue.add(Character(from, to, end, start)); - } - _startTicker(); - } - - /// Stops the animation and releases allocated ticker resources. - void dispose() { - _ticker?.stop(canceled: true); // Stop the ticker safely. - } - /// Initializes the ticker that drives the animation. void _startTicker() { _ticker?.stop(canceled: true); @@ -104,4 +79,29 @@ class TextDecodingController { String _randomChar() { return _chars[_random.nextInt(_chars.length)]; } + + /// Updates the data to be decoded in animation. + /// + /// The given string [newText] is the target result of the animation and signifies the end-state. + void setData(String newText) { + final length = max(_data.length, newText.length); + final oldText = _data.padRight(length); + newText = newText.padRight(length); + + // Clear previous queue and populate it with characters from the new string. + _queue.clear(); + for (int i = 0; i < length; i++) { + final from = oldText[i]; + final to = newText[i]; + final start = _random.nextInt(200); + final end = start + _random.nextInt(200); + _queue.add(Character(from, to, end, start)); + } + _startTicker(); + } + + /// Stops the animation and releases allocated ticker resources. + void dispose() { + _ticker?.stop(canceled: true); // Stop the ticker safely. + } } diff --git a/lib/src/directives/directives/counter.dart b/lib/src/directives/counter.dart similarity index 100% rename from lib/src/directives/directives/counter.dart rename to lib/src/directives/counter.dart diff --git a/lib/src/directives/directive_attribute.dart b/lib/src/directives/directive_attribute.dart deleted file mode 100644 index ba6b27582..000000000 --- a/lib/src/directives/directive_attribute.dart +++ /dev/null @@ -1,6 +0,0 @@ -import '../core/equality/compare_mixin.dart'; - -abstract class Directive with Comparable { - const Directive(); - T modify(covariant T value); -} diff --git a/lib/src/directives/directives/glitch.dart b/lib/src/directives/glitch.dart similarity index 100% rename from lib/src/directives/directives/glitch.dart rename to lib/src/directives/glitch.dart diff --git a/lib/src/directives/text_directive.dart b/lib/src/directives/text_directive.dart deleted file mode 100644 index d5156e896..000000000 --- a/lib/src/directives/text_directive.dart +++ /dev/null @@ -1,81 +0,0 @@ -import '../attributes/attribute.dart'; -import '../factory/mix_provider_data.dart'; -import '../helpers/extensions/string_ext.dart'; -import 'directive_attribute.dart'; - -class UppercaseDirective extends TextDirective { - const UppercaseDirective(); - - @override - String modify(String value) => value.toUpperCase(); -} - -class CapitalizeDirective extends TextDirective { - const CapitalizeDirective(); - - @override - String modify(String value) => value.capitalize; -} - -class LowercaseDirective extends TextDirective { - const LowercaseDirective(); - - @override - String modify(String value) => value.toLowerCase(); -} - -class SentenceCaseDirective extends TextDirective { - const SentenceCaseDirective(); - - @override - String modify(String value) => value.sentenceCase; -} - -class TitleCaseDirective extends TextDirective { - const TitleCaseDirective(); - @override - String modify(String value) => value.titleCase; -} - -abstract class TextDirective extends Directive { - const TextDirective(); - - @override - String modify(String value); - - @override - get props => [modify]; -} - -class TextDirectiveAttribute - extends DirectiveAttribute { - const TextDirectiveAttribute(super.value); - - @override - TextDirectiveAttribute create(List value) { - return TextDirectiveAttribute(value); - } -} - -abstract class DirectiveAttribute> extends VisualAttribute> { - final List value; - const DirectiveAttribute(this.value); - - // Create method - T create(List value); - - @override - List resolve(MixData mix) => value; - - @override - T merge(covariant T? other) { - if (other == null) return this as T; - final combinedList = [...value, ...other.value]; - - return create(combinedList); - } - - @override - get props => [value]; -} diff --git a/lib/src/factory/mix_provider.dart b/lib/src/factory/mix_provider.dart index 6dec7b2ba..58f4ec293 100644 --- a/lib/src/factory/mix_provider.dart +++ b/lib/src/factory/mix_provider.dart @@ -1,72 +1,58 @@ -// ignore_for_file: prefer-widget-private-members - import 'package:flutter/material.dart'; import 'mix_provider_data.dart'; import 'style_mix.dart'; -typedef MixBuilder = Widget Function(MixData mixData); +typedef MixBuilder = Widget Function(MixData mix); -/// Context data widget for Mix data. -/// -/// Inherits from [InheritedWidget] and stores [data], a value that can be -/// accessed by widgets through the static method `of()` or by ensuring it is -/// present with `ensureOf()`. -class Mix extends InheritedWidget { - /// Initializes a new instance of [Mix]. - /// - /// The `data` parameter is the [MixData] object to store. [child] - /// receives the element tree that will use this context, and [key] can be set - /// to track changes. - const Mix({required this.data, required super.child, super.key}); +/// Provides [MixData] to the widget tree. +class MixProvider extends InheritedWidget { + /// Stores [MixData] and wraps a [child] widget. + const MixProvider({required this.data, required super.child, super.key}); - /// Returns the context data from the widget tree in [context]. - /// - /// This method returns the first instance of [Mix] that it finds - /// while searching up the widget tree. Returns null if not found. + /// Retrieves the nearest [MixData] from the widget tree. Returns null if not found. static MixData? maybeOf(BuildContext context) { - return context.dependOnInheritedWidgetOfExactType()?.data; + return context.dependOnInheritedWidgetOfExactType()?.data; } - /// Ensures that [Mix] is available on the widget tree starting at - /// the given [context]. - /// - /// Throws an exception if [Mix] is not found within the given widget - /// tree containing [context]. + /// Retrieves the nearest [MixData] from the widget tree. Throws if not found. static MixData of(BuildContext context) { final mixData = maybeOf(context); - if (mixData == null) { - throw Exception('MixProvider not found in widget tree'); + throw StateError('MixProvider not found in widget tree. ' + 'Ensure that Mix is present in the widget tree and try again.'); } return mixData; } - /// Contains the context data object. final MixData? data; @override - bool updateShouldNotify(Mix oldWidget) { - // Returns true if the `mixProvider` inside the widget has changed since - // the last time a dependent overridden method was called. - return data != oldWidget.data; - } + bool updateShouldNotify(MixProvider oldWidget) => data != oldWidget.data; - static Mix build( + /// Builds a [MixProvider] widget. + /// + /// The [context] and [style] are used to create a [MixData] instance. + /// The [builder] is a function that takes the created [MixData] and returns a widget. + /// + /// If [inherit] is set to true (default is false), the method will attempt to find the nearest + /// [MixProvider] widget up the tree from the provided [context] and merge its [MixData] with the newly created one. + /// This allows the new [MixProvider] widget to inherit styles from its ancestors. + /// + /// Returns a [MixProvider] widget with the given [MixData] and child widget built by the [builder]. + /// If [inherit] is true and a [MixProvider] widget is found up the tree, the returned [MixProvider] widget's + /// [MixData] will be a merge of the ancestor's and the newly created one. + static MixProvider build( BuildContext context, { required StyleMix style, required MixBuilder builder, - bool inherit = false, }) { MixData mixData = MixData.create(context, style); - if (inherit) { - final contextMixData = Mix.maybeOf(context); - if (contextMixData != null) { - mixData = contextMixData.merge(mixData); - } - } - return Mix(data: mixData, child: builder(mixData)); + // Returns a Mix widget with the given data and child. + // If `inherit` is true, the data from the nearest Mix widget in the widget tree + // (if any) is merged with the provided data. + return MixProvider(data: mixData, child: builder(mixData)); } } diff --git a/lib/src/factory/mix_provider_data.dart b/lib/src/factory/mix_provider_data.dart index 0291b8245..fa935fbe5 100644 --- a/lib/src/factory/mix_provider_data.dart +++ b/lib/src/factory/mix_provider_data.dart @@ -1,11 +1,10 @@ -// Necessary packages are imported at the start of the file. import 'package:flutter/material.dart'; -import '../attributes/attribute.dart'; import '../attributes/variant_attribute.dart'; +import '../core/attribute.dart'; import '../core/attributes_map.dart'; -import '../core/equality/compare_mixin.dart'; import '../decorators/decorator.dart'; +import '../helpers/compare_mixin.dart'; import '../theme/mix_theme.dart'; import 'style_mix.dart'; @@ -15,98 +14,129 @@ import 'style_mix.dart'; @immutable class MixData with Comparable { // Instance variables for widget attributes, widget decorators and token resolver. - final VisualAttributeMap _attributes; + final StyleAttributeMap _attributes; - final MixTokenResolver _resolver; + final MixTokenResolver _tokenResolver; - /// A Private constructor for the [MixData] class t hat initializes its main variables. + /// A Private constructor for the [MixData] class that initializes its main variables. /// - /// It takes in [attributes], [decorators] and [resolver] as required parameters. + /// It takes in [attributes] and [resolver] as required parameters. MixData._({ required MixTokenResolver resolver, - required VisualAttributeMap attributes, + required StyleAttributeMap attributes, }) : _attributes = attributes, - _resolver = resolver; + _tokenResolver = resolver; factory MixData.create(BuildContext context, StyleMix style) { final styleMix = applyContextToVisualAttributes(context, style); + final resolver = MixTokenResolver(context, MixTheme.of(context)); + return MixData._( - resolver: MixTokenResolver(context), - attributes: VisualAttributeMap(styleMix), + resolver: resolver, + attributes: StyleAttributeMap(styleMix), ); } - /// Getter method for [MixTokenResolver]. + /// Getter for [MixTokenResolver]. /// - /// Returns current [_resolver]. - MixTokenResolver get resolver => _resolver; + /// Returns [_tokenResolver]. + MixTokenResolver get tokens => _tokenResolver; - /// A getter method for [_attributes]. + /// Getter for [_attributes]. /// - /// Returns a list of all [VisualAttribute]. + /// Returns [_attributes]. @visibleForTesting - VisualAttributeMap get attributes => _attributes; + StyleAttributeMap get attributes => _attributes; - /// A getter method for [_decorators]. + /// Getter for [_decorators]. /// - /// Returns a list of all [Decorator]. - List decoratorOfType() => - _attributes.whereType().toList(); - - /// Retrieves an instance of the specified [VisualAttribute] type from the [MixData]. - /// d - /// Accepts a type parameter [Attr] which extends [VisualAttribute]. - /// Returns the instance of type [Attr] if found, else returns null. - T? attributeOfType() { - return _attributes.attributeOfType(); + /// Returns a list of attributes of type [Decorator]. + Iterable decoratorOfType() => + _attributes.whereType(); + + /// Finds and returns an [VisualAttribute] of type [A], or null if not found. + A? attributeOf() { + final attributes = _attributes.whereType(); + if (attributes.isEmpty) return null; + + final attributeItem = _mergeAttributes(attributes) ?? attributes.last; + // + + return attributeItem; } - R? get, R>() { - return attributeOfType()?.resolve(this); + Iterable whereType() { + return _attributes.whereType(); } - /// This is similar to a merge behavior however it prioritizes the current properties - /// over the other properties, essentially using [MixData] `other` as the base for this instance. - /// - /// [other] is the other [MixData] instance that is to be merged with current instance. - /// Returns a new instance of [MixData] which is actually a merge of current and other instance. + Value resolvableOf>( + A attribute, + ) { + final attributes = _attributes.whereType(); + if (attributes.isEmpty) return attribute.resolve(this); + + return [attribute, ...attributes] + .reduce((value, element) => value.merge(element)) + .resolve(this); + } + + // /// Resolves and returns the value of the [VisualAttribute] of type [A]. + // R get, R>() { + // return attributeOfType()?.resolve(this); + // } + + // /// Merges this [MixData] with another, prioritizing this instance's properties. MixData merge(MixData other) { return MixData._( - resolver: _resolver, + resolver: _tokenResolver, attributes: _attributes.merge(other._attributes), ); } - /// Overrides the getter function of [props] from [Comparable] to specify properties necessary for distinguishing instances. - /// - /// Returns a list of properties [_attributes] & [_decorators]. + /// Used for Comparable mixin. @override get props => [_attributes]; } @visibleForTesting -List applyContextToVisualAttributes( +List applyContextToVisualAttributes( BuildContext context, StyleMix mix, ) { - StyleMix style = StyleMix.create(mix.styles); + StyleMix style = StyleMix.create(mix.styles.values); final contextVariants = mix.variants.whereType(); final multiVariants = mix.variants.whereType(); // Once there are no more context variants to apply, return the mix if (contextVariants.isEmpty) { - return mix.styles; + return mix.styles.values.toList(); } for (ContextVariantAttribute attr in contextVariants) { - if (attr.when(context)) style = style.merge(attr.value); + style = _applyVariants(context, style, attr); } for (MultiVariantAttribute attr in multiVariants) { - if (attr.when(context)) style = style.merge(attr.value); + style = _applyVariants(context, style, attr); } return applyContextToVisualAttributes(context, style); } + +StyleMix _applyVariants( + BuildContext context, + StyleMix style, + T variant, +) { + return variant.when(context) ? style.merge(variant.value) : style; +} + +M? _mergeAttributes(Iterable mergeables) { + if (mergeables.isEmpty) return null; + + return mergeables.reduce((a, b) { + return a is Mergeable ? (a as Mergeable).merge(b) : b; + }); +} diff --git a/lib/src/factory/style_mix.dart b/lib/src/factory/style_mix.dart index f2bbcba2a..cc510843d 100644 --- a/lib/src/factory/style_mix.dart +++ b/lib/src/factory/style_mix.dart @@ -2,7 +2,20 @@ import 'package:flutter/foundation.dart'; -import '../../mix.dart'; +import '../attributes/style_mix_attribute.dart'; +import '../attributes/variant_attribute.dart'; +import '../core/attribute.dart'; +import '../core/attributes_map.dart'; +import '../helpers/compare_mixin.dart'; +import '../recipes/container/container_attribute.dart'; +import '../recipes/flex/flex_attribute.dart'; +import '../recipes/image/image_attribute.dart'; +import '../recipes/stack/stack_attribute.dart'; +import '../recipes/text/text_attribute.dart'; +import '../utils/helper_util.dart'; +import '../variants/variant.dart'; + +typedef Mix = StyleMix; /// A utility class for managing a collection of styling attributes and variants. /// @@ -13,20 +26,30 @@ import '../../mix.dart'; /// /// Example: /// ```dart -/// final style = StyleMix.create([...]); +/// final style = StyleMix(attribute1, attribute2, attribute3); /// final updatedStyle = style.selectVariant(myVariant); /// ``` -class StyleMix { +class StyleMix with Comparable { /// A constant, empty mix for use with const constructor widgets. /// /// This can be used as a default or initial value where a `StyleMix` is required. - static const empty = StyleMix._(styles: [], variants: []); + static const empty = StyleMix._( + styles: StyleAttributeMap.empty(), + variants: VariantAttributeMap.empty(), + ); - /// A map of visual attributes contained in this mix. - final List styles; + /// Visual attributes contained in this mix. + final StyleAttributeMap styles; - /// A map of variant attributes contained in this mix. - final List variants; + /// The variant attributes contained in this mix. + final VariantAttributeMap variants; + + static final stack = SpreadFunctionParams(_styleType()); + static final text = SpreadFunctionParams(_styleType()); + static final image = SpreadFunctionParams(_styleType()); + static final container = + SpreadFunctionParams(_styleType()); + static final flex = SpreadFunctionParams(_styleType()); const StyleMix._({required this.styles, required this.variants}); @@ -85,22 +108,25 @@ class StyleMix { /// ``` factory StyleMix.create(Iterable attributes) { final variantList = []; - final styleList = []; + final styleList = []; for (final attribute in attributes) { - if (attribute is VisualAttribute) { + if (attribute is StyleAttribute) { styleList.add(attribute); } else if (attribute is VariantAttribute) { variantList.add(attribute); } else if (attribute is StyleMixAttribute) { - variantList.addAll(attribute.value.variants); - styleList.addAll(attribute.value.styles); + variantList.addAll(attribute.value.variants.values); + styleList.addAll(attribute.value.styles.values); } else { - assert(false, 'Unsupported attribute type: $attribute'); + throw UnsupportedError('Unsupported attribute type: $attribute'); } } - return StyleMix._(styles: styleList, variants: variantList); + return StyleMix._( + styles: StyleAttributeMap(styleList), + variants: VariantAttributeMap(variantList), + ); } /// Selects a mix based on a [condition]. @@ -159,7 +185,7 @@ class StyleMix { /// Returns a list of all attributes contained in this mix. /// /// This includes both visual and variant attributes. - Iterable get values => [...styles, ...variants]; + Iterable get values => [...styles.values, ...variants.values]; /// Returns true if this StyleMix does not contain any attributes or variants. bool get isEmpty => styles.isEmpty && variants.isEmpty; @@ -205,8 +231,8 @@ class StyleMix { /// /// If [styles] or [variants] is null, the corresponding attribute map of this mix is used. StyleMix copyWith({ - List? styles, - List? variants, + StyleAttributeMap? styles, + VariantAttributeMap? variants, }) { return StyleMix._( styles: styles ?? this.styles, @@ -220,11 +246,11 @@ class StyleMix { /// If a null value is provided for any of the parameters, it is ignored. /// /// This method combines the visual and variant attributes of this mix and the provided [mix]. - StyleMix merge([StyleMix? mix]) { + StyleMix merge(StyleMix? mix) { if (mix == null) return this; - final mergedStyles = [...styles, ...mix.styles]; - final mergedVariants = [...variants, ...mix.variants]; + final mergedStyles = styles.merge(mix.styles); + final mergedVariants = variants.merge(mix.variants); return copyWith(styles: mergedStyles, variants: mergedVariants); } @@ -272,7 +298,7 @@ class StyleMix { /// Loop over all VariantAttributes in variants only once instead of a nested loop, /// checking if each one matches with the selected variants. /// If it does, add it to the matchedVariants, else add it to remainingVariants. - for (final attr in variants) { + for (final attr in variants.values) { if (attr is MultiVariantAttribute) { if (attr.matches(selectedVariants)) { // if all variants match, add it to the matchedVariants @@ -294,7 +320,7 @@ class StyleMix { final updatedStyle = StyleMix._( styles: styles, - variants: remainingVariants, + variants: VariantAttributeMap(remainingVariants), ); /// If not a single variant was matched, return the original StyleMix. @@ -344,7 +370,7 @@ class StyleMix { // Return an empty StyleMix if the list of picked variants is empty - for (final variantAttr in variants) { + for (final variantAttr in variants.values) { if (pickedVariants.contains(variantAttr.variant)) { matchedVariants.add(variantAttr); } @@ -395,21 +421,27 @@ class StyleMix { } @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is StyleMix && - listEquals(styles, other.styles) && - listEquals(variants, other.variants); - } - - @override - int get hashCode => styles.hashCode ^ variants.hashCode; + get props => [styles, variants]; } +/// A class representing a condition-value pair. +/// +/// This class is immutable, meaning that once an instance is created, it cannot be changed. +/// This is useful in cases where you want to ensure that the condition-value pair remains constant, +/// such as when using it in a switch statement. +@immutable class SwitchCondition { final bool condition; final T value; const SwitchCondition(this.condition, this.value); } + +StyleMix Function(Iterable attributes) + _styleType>() { + return (Iterable attributes) { + final merged = attributes.reduce((value, element) => value.merge(element)); + + return StyleMix(merged); + }; +} diff --git a/lib/src/helpers/extensions/style_mix_ext.dart b/lib/src/factory/style_mix_ext.dart similarity index 91% rename from lib/src/helpers/extensions/style_mix_ext.dart rename to lib/src/factory/style_mix_ext.dart index db1cbe0fb..591801c29 100644 --- a/lib/src/helpers/extensions/style_mix_ext.dart +++ b/lib/src/factory/style_mix_ext.dart @@ -1,12 +1,12 @@ // ignore_for_file: long-parameter-list import 'package:flutter/material.dart'; -import '../../attributes/attribute.dart'; -import '../../factory/style_mix.dart'; -import '../../widgets/container_widget.dart'; -import '../../widgets/flex_widget.dart'; -import '../../widgets/icon_widget.dart'; -import '../../widgets/text_widget.dart'; +import '../core/attribute.dart'; +import '../recipes/container/container_widget.dart'; +import '../recipes/flex/flex_widget.dart'; +import '../recipes/icon/icon_widget.dart'; +import '../recipes/text/text_widget.dart'; +import 'style_mix.dart'; extension StyleMixExt on StyleMix { StyledContainer container({ diff --git a/lib/src/helpers/extensions/build_context_ext.dart b/lib/src/helpers/build_context_ext.dart similarity index 86% rename from lib/src/helpers/extensions/build_context_ext.dart rename to lib/src/helpers/build_context_ext.dart index efd41777d..ecbf35772 100644 --- a/lib/src/helpers/extensions/build_context_ext.dart +++ b/lib/src/helpers/build_context_ext.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; -import '../../factory/mix_provider.dart'; -import '../../factory/mix_provider_data.dart'; -import '../../theme/mix_theme.dart'; +import '../factory/mix_provider.dart'; +import '../factory/mix_provider_data.dart'; +import '../theme/mix_theme.dart'; extension BuildContextExt on BuildContext { - MixData? get mix => Mix.maybeOf(this); + MixData? get mix => MixProvider.maybeOf(this); /// MEDIA QUERY EXTENSION METHODS diff --git a/lib/src/core/equality/compare_mixin.dart b/lib/src/helpers/compare_mixin.dart similarity index 61% rename from lib/src/core/equality/compare_mixin.dart rename to lib/src/helpers/compare_mixin.dart index b85189f2e..dfd4a1d27 100644 --- a/lib/src/core/equality/compare_mixin.dart +++ b/lib/src/helpers/compare_mixin.dart @@ -1,44 +1,64 @@ -import '../../helpers/extensions/iterable_ext.dart'; +// ignore_for_file: avoid-mutating-parameters + +import '../core/extensions/iterable_ext.dart'; import 'deep_collection_equality.dart'; /// Returns a `hashCode` for [props]. +/// This function uses the Jenkins hash function to generate a hash code for the given properties. int _mapPropsToHashCode(Iterable? props) => _finish([...?props].fold(0, _combine)); +// Instance of DeepCollectionEquality used for deep comparison of collections. const _equality = DeepCollectionEquality(); /// Determines whether [list1] and [list2] are equal. +/// This function checks if two lists are identical or equal in terms of their elements and their order. bool _equals(List? list1, List? list2) { + // If the lists are identical (same instance), they are equal. if (identical(list1, list2)) return true; + // If either list is null, they are not equal. if (list1 == null || list2 == null) return false; + // If the lengths of the lists are not equal, they are not equal. final length = list1.length; if (length != list2.length) return false; + // Compare each pair of elements in the lists. for (int i = 0; i < length; i++) { final unit1 = list1[i]; final unit2 = list2[i]; + // If both elements are Comparable and not equal, the lists are not equal. if (_isEquatable(unit1) && _isEquatable(unit2)) { if (unit1 != unit2) return false; - } else if (unit1 is Iterable || unit1 is Map) { + } + // If the element is a collection, use deep equality to compare. + else if (unit1 is Iterable || unit1 is Map) { if (!_equality.equals(unit1, unit2)) return false; - } else if (unit1?.runtimeType != unit2?.runtimeType) { + } + // If the runtime types of the elements are not equal, the lists are not equal. + else if (unit1?.runtimeType != unit2?.runtimeType) { return false; - } else if (unit1 != unit2) { + } + // If the elements are not equal, the lists are not equal. + else if (unit1 != unit2) { return false; } } + // If all elements are equal, the lists are equal. return true; } +// Checks if the given object is Comparable. bool _isEquatable(dynamic object) { return object is Comparable; } /// Jenkins Hash Functions /// https://en.wikipedia.org/wiki/Jenkins_hash_function +/// This function implements the Jenkins hash function to generate a hash code for the given object. int _combine(int hash, dynamic object) { + // If the object is a Map, sort the keys and combine their hash codes. if (object is Map) { object.keys .sorted((dynamic a, dynamic b) => a.hashCode - b.hashCode) @@ -48,9 +68,11 @@ int _combine(int hash, dynamic object) { return hash; } + // If the object is a Set, sort the elements and combine their hash codes. if (object is Set) { object = object.sorted(((dynamic a, dynamic b) => a.hashCode - b.hashCode)); } + // If the object is an Iterable, combine the hash codes of all elements. if (object is Iterable) { for (final value in object) { hash = hash ^ _combine(hash, value); @@ -59,12 +81,14 @@ int _combine(int hash, dynamic object) { return hash ^ object.length; } + // Combine the current hash code with the hash code of the object. hash = 0x1fffffff & (hash + object.hashCode); hash = 0x1fffffff & (hash + ((hash & 0x0007ffff) << 10)); return hash ^ (hash >> 6); } +// Finalizes the hash code computation by performing additional bitwise operations. int _finish(int hash) { hash = 0x1fffffff & (hash + ((hash & 0x03ffffff) << 3)); hash = hash ^ (hash >> 11); @@ -74,6 +98,7 @@ int _finish(int hash) { /// Returns a string representation of [props] with property names and values. /// Only includes properties that are not null. +/// This function is used for debugging and logging purposes. String mapPropsToString(Type runtimeType, List props) { final buffer = StringBuffer()..write('$runtimeType('); @@ -93,12 +118,15 @@ String mapPropsToString(Type runtimeType, List props) { /// A mixin that helps implement equality /// without needing to explicitly override [operator ==] and [hashCode]. - +/// This mixin provides a standard implementation of equality and hash code based on the properties of the object. mixin Comparable { + // The properties based on which equality and hash code are computed. List get props; + // Whether to use a detailed string representation of the object. bool get stringify => true; + // Overrides the equality operator to compare based on the properties. @override bool operator ==(Object other) { return identical(this, other) || @@ -107,13 +135,15 @@ mixin Comparable { _equals(props, other.props); } + // Overrides the hash code getter to compute the hash code based on the properties. @override int get hashCode => runtimeType.hashCode ^ _mapPropsToHashCode(props); + // Returns a list of properties that differ between this object and another. List getDiff(Object other) { final diff = []; - // Return if there are no diferences. + // Return if there are no differences. if (this == other) return diff; if (other is Comparable) { @@ -141,6 +171,7 @@ mixin Comparable { return diff; } + // Overrides the string representation of the object for debugging and logging. @override String toString() { return stringify ? mapPropsToString(runtimeType, props) : '$runtimeType'; diff --git a/lib/src/core/equality/deep_collection_equality.dart b/lib/src/helpers/deep_collection_equality.dart similarity index 100% rename from lib/src/core/equality/deep_collection_equality.dart rename to lib/src/helpers/deep_collection_equality.dart index b87508129..ee0b06566 100644 --- a/lib/src/core/equality/deep_collection_equality.dart +++ b/lib/src/helpers/deep_collection_equality.dart @@ -1,38 +1,5 @@ class DeepCollectionEquality { const DeepCollectionEquality(); - bool equals(Object? obj1, Object? obj2) { - if (identical(obj1, obj2)) return true; - if (obj1.runtimeType != obj2.runtimeType) return false; - - if (obj1 is Map) { - return _mapsEqual(obj1, obj2 as Map); - } else if (obj1 is Set) { - return _setsEqual(obj1, obj2 as Set); - } else if (obj1 is Iterable) { - return _iterablesEqual(obj1, obj2 as Iterable); - } - - return obj1 == obj2; - } - - int hash(Object? obj) { - if (obj is Map) { - return Object.hashAllUnordered(obj.keys) ^ - Object.hashAllUnordered(obj.values); - } else if (obj is Set) { - return Object.hashAllUnordered(obj); - } else if (obj is Iterable) { - int hashCode = 0; - for (var value in obj) { - hashCode ^= hash(value); - } - - return hashCode; - } - - return obj.hashCode; - } - bool _mapsEqual(Map map1, Map map2) { if (map1.length != map2.length) return false; for (var key in map1.keys) { @@ -71,4 +38,37 @@ class DeepCollectionEquality { return true; } + + bool equals(Object? obj1, Object? obj2) { + if (identical(obj1, obj2)) return true; + if (obj1.runtimeType != obj2.runtimeType) return false; + + if (obj1 is Map) { + return _mapsEqual(obj1, obj2 as Map); + } else if (obj1 is Set) { + return _setsEqual(obj1, obj2 as Set); + } else if (obj1 is Iterable) { + return _iterablesEqual(obj1, obj2 as Iterable); + } + + return obj1 == obj2; + } + + int hash(Object? obj) { + if (obj is Map) { + return Object.hashAllUnordered(obj.keys) ^ + Object.hashAllUnordered(obj.values); + } else if (obj is Set) { + return Object.hashAllUnordered(obj); + } else if (obj is Iterable) { + int hashCode = 0; + for (var value in obj) { + hashCode ^= hash(value); + } + + return hashCode; + } + + return obj.hashCode; + } } diff --git a/lib/src/helpers/extensions/iterable_ext.dart b/lib/src/helpers/extensions/iterable_ext.dart deleted file mode 100644 index b3df3726a..000000000 --- a/lib/src/helpers/extensions/iterable_ext.dart +++ /dev/null @@ -1,20 +0,0 @@ -extension IterableExt on Iterable { - T? get firstMaybeNull => isEmpty ? null : first; - - T? firstWhereOrNull(bool Function(T element) test) { - for (T element in this) { - if (test(element)) { - return element; - } - } - - return null; - } - - Iterable sorted([Comparator? compare]) { - List newList = List.of(this); - newList.sort(compare); - - return newList; - } -} diff --git a/lib/src/helpers/extensions/values_ext.dart b/lib/src/helpers/extensions/values_ext.dart deleted file mode 100644 index 6335aaf0a..000000000 --- a/lib/src/helpers/extensions/values_ext.dart +++ /dev/null @@ -1,406 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../attributes/alignment_attribute.dart'; -import '../../attributes/border/border_attribute.dart'; -import '../../attributes/border/border_radius_attribute.dart'; -import '../../attributes/color_attribute.dart'; -import '../../attributes/constraints_attribute.dart'; -import '../../attributes/decoration_attribute.dart'; -import '../../attributes/edge_insets_attribute.dart'; -import '../../attributes/scalar_attribute.dart'; -import '../../attributes/shadow_attribute.dart'; -import '../../attributes/space_attribute.dart'; -import '../../attributes/strut_style_attribute.dart'; -import '../../attributes/text_style_attribute.dart'; - -extension StrutStyleExt on StrutStyle { - StrutStyleAttribute toAttribute() { - return StrutStyleAttribute( - fontFamily: fontFamily, - fontFamilyFallback: fontFamilyFallback, - fontSize: fontSize, - fontWeight: fontWeight, - fontStyle: fontStyle, - height: height, - leading: leading, - forceStrutHeight: forceStrutHeight, - ); - } - - StrutStyle merge(StrutStyle? other) { - return StrutStyle( - fontFamily: other?.fontFamily ?? fontFamily, - fontFamilyFallback: other?.fontFamilyFallback ?? fontFamilyFallback, - fontSize: other?.fontSize ?? fontSize, - height: other?.height ?? height, - leadingDistribution: other?.leadingDistribution ?? leadingDistribution, - leading: other?.leading ?? leading, - fontWeight: other?.fontWeight ?? fontWeight, - fontStyle: other?.fontStyle ?? fontStyle, - forceStrutHeight: other?.forceStrutHeight ?? forceStrutHeight, - debugLabel: other?.debugLabel ?? debugLabel, - ); - } -} - -// Extension for TextAlign -extension TextAlignExt on TextAlign { - TextAlignAttribute toAttribute() => TextAlignAttribute(this); -} - -extension GradientExt on Gradient { - GradientAttribute toAttribute() => GradientAttribute(this); -} - -extension EdgeInsetsGeometryExt on EdgeInsetsGeometry { - EdgeInsetsGeometryAttribute toAttribute() { - if (this is EdgeInsets) return (this as EdgeInsets).toAttribute(); - if (this is EdgeInsetsDirectional) { - return (this as EdgeInsetsDirectional).toAttribute(); - } - - throw UnimplementedError(); - } - - PaddingGeometryAttribute toPadding() { - if (this is EdgeInsets) return (this as EdgeInsets).toPadding(); - if (this is EdgeInsetsDirectional) { - return (this as EdgeInsetsDirectional).toPadding(); - } - - throw UnimplementedError(); - } - - MarginGeometryAttribute toMargin() { - if (this is EdgeInsets) return (this as EdgeInsets).toMargin(); - if (this is EdgeInsetsDirectional) { - return (this as EdgeInsetsDirectional).toMargin(); - } - - throw UnimplementedError(); - } -} - -extension EdgeInsetsExt on EdgeInsets { - EdgeInsetsAttribute toAttribute() => EdgeInsetsAttribute( - top: top, - bottom: bottom, - left: left, - right: right, - ); - - PaddingAttribute toPadding() => PaddingAttribute( - top: top, - bottom: bottom, - left: left, - right: right, - ); - - MarginAttribute toMargin() => MarginAttribute( - top: top, - bottom: bottom, - left: left, - right: right, - ); -} - -extension EdgeInsetsDirectionalExt on EdgeInsetsDirectional { - EdgeInsetsDirectionalAttribute toAttribute() => - EdgeInsetsDirectionalAttribute( - top: top, - bottom: bottom, - start: start, - end: end, - ); - - PaddingDirectionalAttribute toPadding() => PaddingDirectionalAttribute( - top: top, - bottom: bottom, - start: start, - end: end, - ); - - MarginDirectionalAttribute toMargin() => MarginDirectionalAttribute( - top: top, - bottom: bottom, - start: start, - end: end, - ); -} - -extension ColorExt on Color { - Color darken([double amount = 0.1]) { - assert(amount >= 0 && amount <= 1); - - final hsl = HSLColor.fromColor(this); - final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0)); - - return hslDark.toColor(); - } - - Color lighten([double amount = 0.1]) { - assert(amount >= 0 && amount <= 1); - - final hsl = HSLColor.fromColor(this); - final hslLight = - hsl.withLightness((hsl.lightness + amount).clamp(0.0, 1.0)); - - return hslLight.toColor(); - } - - /// String is in the format "aabbcc" or "ffaabbcc" with an optional leading "#". - static Color fromHex(String hexString) { - String updatedHexString = hexString.replaceFirst('#', ''); - if (updatedHexString.length == 6) updatedHexString = 'ff$updatedHexString'; - - return Color(int.parse(updatedHexString, radix: 16)); - } - - /// Prefixes a hash sign if [leadingHashSign] is set to `true` (default is `true`). - String toHex({bool leadingHashSign = true}) => '${leadingHashSign ? '#' : ''}' - '${alpha.toRadixString(16).padLeft(2, '0')}' - '${red.toRadixString(16).padLeft(2, '0')}' - '${green.toRadixString(16).padLeft(2, '0')}' - '${blue.toRadixString(16).padLeft(2, '0')}'; - - ColorAttribute toAttribute() => ColorAttribute(this); -} - -// Extension for Alignment -extension AlignmentGeometryExt on AlignmentGeometry { - AlignmentGeometryAttribute toAttribute() { - if (this is Alignment) return (this as Alignment).toAttribute(); - if (this is AlignmentDirectional) { - return (this as AlignmentDirectional).toAttribute(); - } - throw UnimplementedError(); - } -} - -extension ShapeDecorationExt on ShapeDecoration { - ShapeDecorationAttribute toAttribute() => ShapeDecorationAttribute( - color: color?.toAttribute(), - shape: shape, - gradient: gradient?.toAttribute(), - boxShadow: shadows?.map((e) => e.toAttribute()).toList(), - ); -} - -extension AlignmentExt on Alignment { - AlignmentAttribute toAttribute() => AlignmentAttribute(x: x, y: y); -} - -extension AligmentDirectionalExt on AlignmentDirectional { - AlignmentDirectionalAttribute toAttribute() => - AlignmentDirectionalAttribute(start: start, y: y); -} - -extension BoxConstraintsExt on BoxConstraints { - BoxConstraintsAttribute toAttribute() => BoxConstraintsAttribute( - minWidth: minWidth, - maxWidth: maxWidth, - minHeight: minHeight, - maxHeight: maxHeight, - ); -} - -// Extension for MainAxisAlignment -extension MainAxisAlignmentExt on MainAxisAlignment { - MainAxisAlignmentAttribute toAttribute() => MainAxisAlignmentAttribute(this); -} - -// Extension for CrossAxisAlignment -extension CrossAxisAlignmentExt on CrossAxisAlignment { - CrossAxisAlignmentAttribute toAttribute() => - CrossAxisAlignmentAttribute(this); -} - -extension MainAxisSizeExt on MainAxisSize { - MainAxisSizeAttribute toAttribute() => MainAxisSizeAttribute(this); -} - -extension TextOverflowExt on TextOverflow { - TextOverflowAttribute toAttribute() => TextOverflowAttribute(this); -} - -extension VerticalDirectionExt on VerticalDirection { - VerticalDirectionAttribute toAttribute() => VerticalDirectionAttribute(this); -} - -extension ClipExt on Clip { - ClipAttribute toAttribute() => ClipAttribute(this); -} - -extension TextWidthBasisExt on TextWidthBasis { - TextWidthBasisAttribute toAttribute() => TextWidthBasisAttribute(this); -} - -extension TextHeightBehaviorExt on TextHeightBehavior { - TextHeightBehaviorAttribute toAttribute() => - TextHeightBehaviorAttribute(this); -} - -// Extension for TextDirection -extension TextDirectionExt on TextDirection { - TextDirectionAttribute toAttribute() => TextDirectionAttribute(this); -} - -extension ImageRepeatExt on ImageRepeat { - ImageRepeatAttribute toAttribute() => ImageRepeatAttribute(this); -} - -// Extension for Axis -extension AxisExt on Axis { - AxisAttribute toAttribute() => AxisAttribute(this); -} - -// Extension for BlendMode -extension BlendModeExt on BlendMode { - BlendModeAttribute toAttribute() => BlendModeAttribute(this); -} - -// Extension for BoxFit -extension BoxFitExt on BoxFit { - BoxFitAttribute toAttribute() => BoxFitAttribute(this); -} - -extension BoxDecorationExt on BoxDecoration { - BoxDecorationAttribute toAttribute() => BoxDecorationAttribute( - border: border?.toAttribute(), - borderRadius: borderRadius?.toAttribute(), - gradient: gradient?.toAttribute(), - boxShadow: boxShadow?.map((e) => e.toAttribute()).toList(), - color: color?.toAttribute(), - shape: shape.toAttribute(), - ); -} - -extension BoxShapeExt on BoxShape { - BoxShapeAttribute toAttribute() => BoxShapeAttribute(this); -} - -extension BorderRadiusGeometryExt on BorderRadiusGeometry { - BorderRadiusGeometryAttribute toAttribute() { - if (this is BorderRadius) return (this as BorderRadius).toAttribute(); - if (this is BorderRadiusDirectional) { - return (this as BorderRadiusDirectional).toAttribute(); - } - - throw UnimplementedError(); - } -} - -extension BorderRadiusDirectionalExrt on BorderRadiusDirectional { - BorderRadiusDirectionalAttribute toAttribute() => - BorderRadiusDirectionalAttribute( - topStart: topStart, - topEnd: topEnd, - bottomStart: bottomStart, - bottomEnd: bottomEnd, - ); -} - -// Extension for BorderRadius -extension BorderRadiusExt on BorderRadius { - BorderRadiusAttribute toAttribute() => BorderRadiusAttribute( - topLeft: topLeft, - topRight: topRight, - bottomLeft: bottomLeft, - bottomRight: bottomRight, - ); -} - -extension TextBaseLineExt on TextBaseline { - TextBaselineAttribute toAttribute() => TextBaselineAttribute(this); -} - -extension Matrix4Ext on Matrix4 { - TransformAttribute toAttribute() => TransformAttribute(this); - - /// Merge [other] into this matrix. - Matrix4 merge(Matrix4? other) { - if (other == null || other == this) return this; - - return clone()..multiply(other); - } -} - -extension BorderSideExt on BorderSide { - BorderSideAttribute toAttribute() => BorderSideAttribute( - color: color.toAttribute(), - strokeAlign: strokeAlign, - style: style, - width: width, - ); -} - -extension BoxBorderExt on BoxBorder { - BoxBorderAttribute toAttribute() { - if (this is Border) return (this as Border).toAttribute(); - if (this is BorderDirectional) { - return (this as BorderDirectional).toAttribute(); - } - - throw UnimplementedError(); - } -} - -extension ShadowExt on Shadow { - ShadowAttribute toAttribute() => ShadowAttribute( - blurRadius: blurRadius, - color: color.toAttribute(), - offset: offset, - ); -} - -extension BoxShadowExt on BoxShadow { - BoxShadowAttribute toAttribute() => BoxShadowAttribute( - color: color.toAttribute(), - offset: offset, - blurRadius: blurRadius, - spreadRadius: spreadRadius, - ); -} - -extension TextStyleExt on TextStyle { - TextStyleAttribute toAttribute() => TextStyleAttribute( - background: background, - color: color?.toAttribute(), - debugLabel: debugLabel, - decoration: decoration, - decorationColor: decorationColor?.toAttribute(), - decorationStyle: decorationStyle, - decorationThickness: decorationThickness, - fontFamily: fontFamily, - fontFamilyFallback: fontFamilyFallback, - fontFeatures: fontFeatures, - fontSize: fontSize, - fontStyle: fontStyle, - fontWeight: fontWeight, - foreground: foreground, - height: height, - letterSpacing: letterSpacing, - locale: locale, - shadows: shadows?.map((e) => e.toAttribute()).toList(), - textBaseline: textBaseline, - wordSpacing: wordSpacing, - ); -} - -extension BorderExt on Border { - BorderAttribute toAttribute() => BorderAttribute( - left: left.toAttribute(), - right: right.toAttribute(), - top: top.toAttribute(), - bottom: bottom.toAttribute(), - ); -} - -extension BorderDirectionalExt on BorderDirectional { - BorderDirectionalAttribute toAttribute() => BorderDirectionalAttribute( - start: start.toAttribute(), - end: end.toAttribute(), - top: top.toAttribute(), - bottom: bottom.toAttribute(), - ); -} diff --git a/lib/src/helpers/lerp_helpers.dart b/lib/src/helpers/lerp_helpers.dart new file mode 100644 index 000000000..ed2488788 --- /dev/null +++ b/lib/src/helpers/lerp_helpers.dart @@ -0,0 +1,35 @@ +/// Linearly interpolates between two integers. +/// +/// The [lerpInt] function takes two integers, [a] and [b], and a value [t] +/// between 0.0 and 1.0. It returns a new integer that is linearly interpolated +/// between [a] and [b] based on the value of [t]. +/// +/// Example usage: +/// ```dart +/// int a = 10; +/// int b = 20; +/// double t = 0.3; +/// int result = lerpInt(a, b, t); +/// print(result); // Output: 13 +/// ``` +int lerpInt(int a, int b, double t) { + return ((1 - t) * a + t * b).round(); +} + +/// Snaps between two values based on a threshold. +/// +/// The [lerpSnap] function takes two values, [from] and [to], and a threshold +/// value [t] between 0.0 and 1.0. If [t] is less than 0.5, it returns [from], +/// otherwise it returns [to]. +/// +/// Example usage: +/// ```dart +/// String from = 'Hello'; +/// String to = 'World'; +/// double t = 0.8; +/// String result = lerpSnap(from, to, t); +/// print(result); // Output: 'World' +/// ``` +P lerpSnap

(P from, P to, double t) { + return t < 0.5 ? from : to; +} diff --git a/lib/src/helpers/extensions/string_ext.dart b/lib/src/helpers/string_ext.dart similarity index 58% rename from lib/src/helpers/extensions/string_ext.dart rename to lib/src/helpers/string_ext.dart index 9e56a9f44..dae6467c8 100644 --- a/lib/src/helpers/extensions/string_ext.dart +++ b/lib/src/helpers/string_ext.dart @@ -1,3 +1,23 @@ +/// A collection of extension methods for the `String` class and `List` class. +/// These methods provide various string manipulation functionalities such as converting +/// between different case styles (camel case, pascal case, snake case, etc.), capitalizing +/// words, and extracting words from a string. +/// +/// The `StringExt` extension provides methods for manipulating individual strings, +/// while the `ListStringExt` extension provides methods for manipulating lists of strings. +/// +/// Example usage: +/// ```dart +/// import 'package:mix/helpers/string_ext.dart'; +/// +/// void main() { +/// String myString = 'hello_world'; +/// print(myString.camelCase); // Output: helloWorld +/// +/// List myStringList = ['hello', 'world']; +/// print(myStringList.uppercase); // Output: ['HELLO', 'WORLD'] +/// } +/// ``` const _snakeCaseSeparator = '_'; const _paramCaseSeparator = '-'; const _spaceSeparator = ' '; @@ -10,7 +30,9 @@ final _symbolSet = { _spaceSeparator, }; +/// Extension on [String] to provide various string manipulation operations. extension StringExt on String { + /// Splits the string into a list of words. List get words { final sb = StringBuffer(); final words = []; @@ -39,10 +61,13 @@ extension StringExt on String { return words; } + /// Checks if the string is in all uppercase. bool get isUpperCase => toUpperCase() == this; + /// Checks if the string is in all lowercase. bool get isLowerCase => toLowerCase() == this; + /// Converts the string to camel case. String get camelCase { final wordList = words.map((word) => word.capitalize).toList(); if (wordList.isNotEmpty) { @@ -52,8 +77,10 @@ extension StringExt on String { return wordList.join(); } + /// Converts the string to pascal case. String get pascalCase => words.map((word) => word.capitalize).join(); + /// Capitalizes the first letter of the string. String get capitalize { if (isEmpty) return this; final firstRune = runes.first; @@ -63,15 +90,20 @@ extension StringExt on String { restRunes.map((rune) => String.fromCharCode(rune).toLowerCase()).join(); } + /// Converts the string to constant case. String get constantCase => words.uppercase.join(_snakeCaseSeparator); + /// Converts the string to snake case. String get snakeCase => words.lowercase.join(_snakeCaseSeparator); + /// Converts the string to param case. String get paramCase => words.lowercase.join(_paramCaseSeparator); + /// Converts the string to title case. String get titleCase => words.map((word) => word.capitalize).join(_spaceSeparator); + /// Converts the string to sentence case. String get sentenceCase { final wordList = [...words]; if (wordList.isEmpty) return this; @@ -82,7 +114,11 @@ extension StringExt on String { } } +/// Extension on [List] to provide additional string manipulation methods. extension ListStringExt on List { + /// Converts all strings in the list to lowercase. List get lowercase => map((e) => e.toLowerCase()).toList(); + + /// Converts all strings in the list to uppercase. List get uppercase => map((e) => e.toUpperCase()).toList(); } diff --git a/lib/src/recipes/container/container_attribute.dart b/lib/src/recipes/container/container_attribute.dart new file mode 100644 index 000000000..fa35f0922 --- /dev/null +++ b/lib/src/recipes/container/container_attribute.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; + +import '../../attributes/color/color_dto.dart'; +import '../../attributes/constraints/constraints_attribute.dart'; +import '../../attributes/constraints/constraints_dto.dart'; +import '../../attributes/decoration/decoration_attribute.dart'; +import '../../attributes/decoration/decoration_dto.dart'; +import '../../attributes/scalars/scalars_attribute.dart'; +import '../../attributes/spacing/spacing_attribute.dart'; +import '../../attributes/spacing/spacing_dto.dart'; +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; +import 'container_spec.dart'; + +class ContainerSpecAttribute + extends ResolvableAttribute { + final AlignmentGeometry? alignment; + final SpacingDto? padding; + final SpacingDto? margin; + final BoxConstraintsDto? constraints; + final DecorationDto? decoration; + final Matrix4? transform; + final Clip? clipBehavior; + final ColorDto? color; + final double? width; + final double? height; + + const ContainerSpecAttribute({ + this.alignment, + this.padding, + this.margin, + this.constraints, + this.decoration, + this.transform, + this.clipBehavior, + this.color, + this.width, + this.height, + }); + + static ContainerSpecAttribute of(MixData mix) { + final attribute = mix.attributeOf(); + + return ContainerSpecAttribute( + alignment: mix.attributeOf()?.value, + padding: mix.attributeOf()?.value, + margin: mix.attributeOf()?.value, + constraints: mix.attributeOf()?.value, + decoration: mix.attributeOf()?.value, + transform: mix.attributeOf()?.value, + clipBehavior: mix.attributeOf()?.value, + color: mix.attributeOf()?.value, + width: mix.attributeOf()?.value, + height: mix.attributeOf()?.value, + ).merge(attribute); + } + + @override + ContainerSpec resolve(MixData mix) { + return ContainerSpec( + alignment: alignment, + padding: padding?.resolve(mix), + margin: margin?.resolve(mix), + constraints: constraints?.resolve(mix), + decoration: decoration?.resolve(mix), + transform: transform, + clipBehavior: clipBehavior, + color: color?.resolve(mix), + width: width, + height: height, + ); + } + + @override + ContainerSpecAttribute merge(ContainerSpecAttribute? other) { + if (other == null) return this; + + return ContainerSpecAttribute( + alignment: other.alignment ?? alignment, + padding: padding?.merge(other.padding) ?? other.padding, + margin: margin?.merge(other.margin) ?? other.margin, + constraints: constraints?.merge(other.constraints) ?? other.constraints, + decoration: decoration?.merge(other.decoration) ?? other.decoration, + transform: other.transform ?? transform, + clipBehavior: other.clipBehavior ?? clipBehavior, + color: color?.merge(other.color) ?? other.color, + width: other.width ?? width, + height: other.height ?? height, + ); + } + + @override + List get props => [ + alignment, + padding, + margin, + constraints, + decoration, + transform, + clipBehavior, + color, + width, + height, + ]; +} diff --git a/lib/src/specs/container_spec.dart b/lib/src/recipes/container/container_spec.dart similarity index 60% rename from lib/src/specs/container_spec.dart rename to lib/src/recipes/container/container_spec.dart index 05adf4b05..c70c53340 100644 --- a/lib/src/specs/container_spec.dart +++ b/lib/src/recipes/container/container_spec.dart @@ -1,22 +1,19 @@ +import 'dart:ui'; + import 'package:flutter/material.dart'; -import '../attributes/alignment_attribute.dart'; -import '../attributes/attribute.dart'; -import '../attributes/constraints_attribute.dart'; -import '../attributes/decoration_attribute.dart'; -import '../attributes/scalar_attribute.dart'; -import '../attributes/space_attribute.dart'; -import '../factory/mix_provider_data.dart'; +import '../../core/attribute.dart'; -class ContainerSpec extends MixExtension { +class ContainerSpec extends Spec { final AlignmentGeometry? alignment; final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry? margin; final BoxConstraints? constraints; final Decoration? decoration; - final Matrix4? transform; final Clip? clipBehavior; + final Color? color; + final double? width, height; const ContainerSpec({ required this.alignment, @@ -26,17 +23,38 @@ class ContainerSpec extends MixExtension { required this.decoration, required this.transform, required this.clipBehavior, + required this.color, + required this.width, + required this.height, }); - static ContainerSpec resolve(MixData mix) { - return ContainerSpec( - alignment: mix.get(), - padding: mix.get(), - margin: mix.get(), - constraints: mix.get(), - decoration: mix.get(), - transform: mix.get(), - clipBehavior: mix.get(), + const ContainerSpec.empty() + : alignment = null, + padding = null, + margin = null, + constraints = null, + decoration = null, + transform = null, + color = null, + width = null, + height = null, + clipBehavior = null; + + @override + ContainerSpec merge(ContainerSpec? other) { + if (other == null) return this; + + return copyWith( + alignment: other.alignment, + padding: other.padding, + margin: other.margin, + constraints: other.constraints, + decoration: other.decoration, + width: other.width, + height: other.height, + transform: other.transform, + clipBehavior: other.clipBehavior, + color: other.color, ); } @@ -51,6 +69,7 @@ class ContainerSpec extends MixExtension { double? height, Matrix4? transform, Clip? clipBehavior, + Color? color, }) { return ContainerSpec( alignment: alignment ?? this.alignment, @@ -60,6 +79,9 @@ class ContainerSpec extends MixExtension { decoration: decoration ?? this.decoration, transform: transform ?? this.transform, clipBehavior: clipBehavior ?? this.clipBehavior, + color: color ?? this.color, + width: width ?? this.width, + height: height ?? this.height, ); } @@ -73,17 +95,23 @@ class ContainerSpec extends MixExtension { decoration: Decoration.lerp(decoration, other.decoration, t), transform: Matrix4Tween(begin: transform, end: other.transform).lerp(t), clipBehavior: t < 0.5 ? clipBehavior : other.clipBehavior, + color: Color.lerp(color, other.color, t), + width: lerpDouble(width, other.width, t), + height: lerpDouble(height, other.height, t), ); } @override get props => [ alignment, + width, + height, padding, margin, constraints, decoration, transform, clipBehavior, + color, ]; } diff --git a/lib/src/recipes/container/container_util.dart b/lib/src/recipes/container/container_util.dart new file mode 100644 index 000000000..040946f61 --- /dev/null +++ b/lib/src/recipes/container/container_util.dart @@ -0,0 +1,327 @@ +import 'package:flutter/material.dart'; + +import '../../../exports.dart'; +import '../../attributes/color/color_util.dart'; +import '../../attributes/constraints/constraints_dto.dart'; +import '../../attributes/decoration/decoration_dto.dart'; +import '../../attributes/spacing/spacing_dto.dart'; + +const container = ContainerUtility.selfBuilder; +const box = ContainerUtility.selfBuilder; + +const border = BoxBorderUtility(BoxBorderAttribute.new); + +/// Utility of `ClipUtility` that returns a `ClipBehaviorAttribute` instance +/// +/// Useful for defining the clipping behavior of widgets. +/// Includes predefined values of `Clip` such as `none`, `hardEdge`, and `antiAlias`. +/// Example: +/// ```dart +/// final noneClip = clipBehavior.none(); +/// final hardEdge = clipBehavior.hardEdge(); +/// final antiAlias = clipBehavior.antiAlias(); +/// ``` +/// See also: +/// - [ClipBehaviorAttribute] +/// - [Clip] +/// - [ClipUtility] +const clipBehavior = ClipUtility(ClipBehaviorAttribute.new); + +/// `padding` - Provides a comprehensive utility for defining padding attribute +/// +/// `padding` is an instance of `SpacingUtility`, offering a range of methods to apply custom padding +/// to various sides of a widget. Each method returns an object that can be used to construct a widget with +/// the specified padding attributes. +/// +/// Examples: +/// +/// Applying uniform padding: +/// ```dart +/// final uniform = padding.all(10); // Applies 10 units of padding on all sides. +/// ``` +/// +/// Applying horizontal padding: +/// ```dart +/// final horizontal = padding.horizontal(15); // Applies 15 units of padding on the left and right. +/// ``` +/// +/// Applying vertical padding: +/// ```dart +/// final vertical = padding.vertical(20); // Applies 20 units of padding on the top and bottom. +/// ``` +/// +/// Applying different padding values for each side: +/// ```dart +/// final custom = padding.only(top: 10, bottom: 20, left: 30, right: 40); +/// // Applies 10 units on top, 20 units on bottom, 30 units on left, and 40 units on right. +/// ``` +/// +/// Applying padding to a specific side: +/// ```dart +/// final top = padding.top(5); // Applies 5 units of padding at the top. +/// final bottom = padding.bottom(5); // Applies 5 units of padding at the bottom. +/// final left = padding.left(8); // Applies 8 units of padding on the left side. +/// final right = padding.right(8); // Applies 8 units of padding on the right side. +/// ``` +/// +/// Applying directional padding in RTL (Right-to-Left) layouts: +/// ```dart +/// final start = padding.start(10); // Applies 10 units of padding at the start side (considering text direction). +/// final end = padding.end(10); // Applies 10 units of padding at the end side (considering text direction). +/// ``` +/// +/// Using `SpacingUtility` callable method for flexible padding: +/// - 1 parameter: uniform spacing on all sides. +/// - 2 parameters: first for top and bottom, second for left and right. +/// - 3 parameters: first for top, second for left and right, third for bottom. +/// - 4 parameters: applied to top, right, bottom, and left respectively. +/// +/// Examples: +/// - `padding(10)`: uniform padding of 10 units on all sides. +/// - `padding(10, 20)`: top and bottom padding of 10 units, left and right padding of 20 units. +/// - `padding(10, 20, 30)`: top padding of 10 units, left and right padding of 20 units, bottom padding of 30 units. +/// - `padding(10, 20, 30, 40)`: top padding of 10 units, right padding of 20 units, bottom padding of 30 units, left padding of 40 units. +/// +/// `padding` effectively leverages `SpacingUtility` to create a versatile and readable way of applying padding, +/// making it easier to manage spacing in Flutter's layout system. +const padding = SpacingUtility(PaddingAttribute.new); + +/// `margin` - Provides a comprehensive utility for defining margin attribute +/// +/// `margin` is an instance of `SpacingUtility`, offering a range of methods to apply custom margin +/// to various sides of a widget. Each method returns an object that can be used to construct a widget with +/// the specified margin attributes. +/// +/// Examples: +/// +/// Applying uniform margin: +/// ```dart +/// final uniform = margin.all(10); // Applies 10 units of margin on all sides. +/// ``` +/// +/// Applying horizontal margin: +/// ```dart +/// final horizontal = margin.horizontal(15); // Applies 15 units of margin on the left and right. +/// ``` +/// +/// Applying vertical margin: +/// ```dart +/// final vertical = margin.vertical(20); // Applies 20 units of margin on the top and bottom. +/// ``` +/// +/// Applying different margin values for each side: +/// ```dart +/// final custom = margin.only(top: 10, bottom: 20, left: 30, right: 40); +/// // Applies 10 units on top, 20 units on bottom, 30 units on left, and 40 units on right. +/// ``` +/// +/// Applying margin to a specific side: +/// ```dart +/// final top = margin.top(5); // Applies 5 units of margin at the top. +/// final bottom = margin.bottom(5); // Applies 5 units of margin at the bottom. +/// final left = margin.left(8); // Applies 8 units of margin on the left side. +/// final right = margin.right(8); // Applies 8 units of margin on the right side. +/// ``` +/// +/// Applying directional margin in RTL (Right-to-Left) layouts: +/// ```dart +/// final start = margin.start(10); // Applies 10 units of margin at the start side (considering text direction). +/// final end = margin.end(10); // Applies 10 units of margin at the end side (considering text direction). +/// ``` +/// +/// Using `SpacingUtility` callable method for flexible margin: +/// - 1 parameter: uniform spacing on all sides. +/// - 2 parameters: first for top and bottom, second for left and right. +/// - 3 parameters: first for top, second for left and right, third for bottom. +/// - 4 parameters: applied to top, right, bottom, and left respectively. +/// +/// Examples: +/// - `margin(10)`: uniform margin of 10 units on all sides. +/// - `margin(10, 20)`: top and bottom margin of 10 units, left and right margin of 20 units. +/// - `margin(10, 20, 30)`: top margin of 10 units, left and right margin of 20 units, bottom margin of 30 units. +/// - `margin(10, 20, 30, 40)`: top margin of 10 units, right margin of 20 units, bottom margin of 30 units, left margin of 40 units. +/// +/// `margin` effectively leverages `SpacingUtility` to create a versatile and readable way of applying margin, +/// making it easier to manage spacing in Flutter's layout system. +const margin = SpacingUtility(MarginAttribute.new); + +// Provides an utility for creating a uniform BorderRadiusAttribute for all corners. +const borderRadius = + BorderRadiusGeometryUtility(BorderRadiusGeometryAttribute.new); +const alignment = AlignmentUtility(AlignmentGeometryAttribute.new); + +const backgroundColor = ColorUtility(BackgroundColorAttribute.new); + +const _decoration = DecorationUtility(DecorationAttribute.new); + +const _constraints = BoxConstraintsUtility(BoxConstraintsAttribute.new); + +final elevation = _decoration.box.elevation; +final boxShadow = _decoration.box.boxShadow; + +/// Defines a [BoxConstraintsAttribute] with a minimum width [minWidth]. +/// +/// This function sets a lower bound on the width of the box constraints but +/// doesn't set a fixed width. It allows flexibility above the specified [minWidth]. +/// +/// Equivalent to BoxConstraints(minWidth: minWidth). +/// +/// See also: +/// - [BoxConstraintsAttribute] +/// - [BoxConstraints] +/// - [BoxConstraintsUtility] +final minWidth = _constraints.minWidth; + +/// Defines a [BoxConstraintsAttribute] with a maximum width [maxWidth]. +/// +/// This function sets an upper limit on the width of the box constraints. The width +/// can be any value up to [maxWidth], offering flexibility in sizing. +/// +/// Equivalent to BoxConstraints(maxWidth: maxWidth). +/// +/// See also: +/// - [BoxConstraintsAttribute] +/// - [BoxConstraints] +/// - [BoxConstraintsUtility] +final maxWidth = _constraints.maxWidth; + +/// Creates a [BoxConstraintsAttribute] with a minimum height [minHeight]. +/// +/// This function sets a lower limit on the height of the box constraints, allowing +/// any height above the specified [minHeight], thereby providing vertical sizing flexibility. +/// +/// Equivalent to BoxConstraints(minHeight: minHeight). +/// +/// See also: +/// - [BoxConstraintsAttribute] +/// - [BoxConstraints] +/// - [BoxConstraintsUtility] +final minHeight = _constraints.minHeight; + +/// Creates a [BoxConstraintsAttribute] with a maximum height [maxHeight]. +/// +/// This function sets an upper bound on the height of the box constraints. The height +/// can be any value up to [maxHeight], enabling flexibility in vertical sizing. +/// +/// Equivalent to BoxConstraints(maxHeight: maxHeight). +/// +/// See also: +/// - [BoxConstraintsAttribute] +/// - [BoxConstraints] +/// - [BoxConstraintsUtility] +final maxHeight = _constraints.maxHeight; + +/// Creates a [WidthAttribute] with a specific [width]. +/// +/// The returned [WidthAttribute] instance imposes the given [width] as +/// a fixed size, ignoring any minimum or maximum width constraints. +/// +/// [width]: The fixed width value for the constraints. +const width = WidthAttribute.new; + +/// Creates a [HeightAttriubte] with a specific [height]. +/// +/// The returned [HeightAttribute] instance imposes the given [height] as +/// a fixed size, ignoring any minimum or maximum height constraints. +/// [height]: The fixed height value for the constraints. +const height = HeightAttribute.new; + +class ContainerUtility + extends MixUtility { + static const selfBuilder = ContainerUtility(MixUtility.selfBuilder); + const ContainerUtility(super.builder); + + T _only({ + AlignmentGeometry? alignment, + SpacingDto? padding, + SpacingDto? margin, + DecorationDto? decoration, + BoxConstraintsDto? constraints, + double? width, + double? height, + Matrix4? transform, + Clip? clipBehavior, + ColorDto? color, + }) { + return builder( + ContainerSpecAttribute( + alignment: alignment, + padding: padding, + margin: margin, + constraints: constraints, + decoration: decoration, + transform: transform, + clipBehavior: clipBehavior, + color: color, + width: width, + height: height, + ), + ); + } + + BoxDecorationUtility get decoration { + return BoxDecorationUtility((decoration) => _only(decoration: decoration)); + } + + AlignmentUtility get alignment { + return AlignmentUtility((alignment) => call(alignment: alignment)); + } + + SpacingUtility get padding { + return SpacingUtility((padding) => _only(padding: padding)); + } + + SpacingUtility get margin { + return SpacingUtility((margin) => _only(margin: margin)); + } + + ColorUtility get color { + return ColorUtility((color) => _only(color: color)); + } + + BoxConstraintsUtility get constraints { + return BoxConstraintsUtility( + (constraints) => _only(constraints: constraints), + ); + } + + Matrix4Utility get transform { + return Matrix4Utility((transform) => call(transform: transform)); + } + + ClipUtility get clipBehavior { + return ClipUtility((clipBehavior) => call(clipBehavior: clipBehavior)); + } + + T height(double height) => _only(height: height); + + T width(double width) => _only(width: width); + + T call({ + AlignmentGeometry? alignment, + EdgeInsets? padding, + EdgeInsets? margin, + BoxConstraints? constraints, + double? width, + double? height, + BoxDecoration? decoration, + Matrix4? transform, + Clip? clipBehavior, + Color? color, + }) { + final attribute = ContainerSpecAttribute( + alignment: alignment, + padding: SpacingDto.maybeFrom(padding), + margin: SpacingDto.maybeFrom(margin), + constraints: constraints?.toDto(), + decoration: decoration?.toDto(), + transform: transform, + clipBehavior: clipBehavior, + color: color?.toDto(), + width: width, + height: height, + ); + + return builder(attribute); + } +} diff --git a/lib/src/recipes/container/container_widget.dart b/lib/src/recipes/container/container_widget.dart new file mode 100644 index 000000000..e13507a62 --- /dev/null +++ b/lib/src/recipes/container/container_widget.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; + +import '../../helpers/build_context_ext.dart'; +import '../../widgets/styled_widget.dart'; +import 'container_attribute.dart'; +import 'container_spec.dart'; + +typedef Box = StyledContainer; + +class StyledContainer extends StyledWidget { + const StyledContainer({super.style, super.key, super.inherit, this.child}); + + final Widget? child; + + @override + Widget build(BuildContext context) { + final contextMix = context.mix; + final inheritedAttribute = inherit && contextMix != null + ? ContainerSpecAttribute.of(contextMix) + : const ContainerSpecAttribute(); + + return withMix(context, (mix) { + final attribute = ContainerSpecAttribute.of(mix); + final merged = inheritedAttribute.merge(attribute); + + final mixture = merged.resolve(mix); + + return MixedContainer(mixture, child: child); + }); + } +} + +class MixedContainer extends StatelessWidget { + const MixedContainer(this.mixture, {super.key, this.child}); + + final Widget? child; + final ContainerSpec mixture; + + @override + Widget build(BuildContext context) { + return Container( + alignment: mixture.alignment, + padding: mixture.padding, + color: mixture.color, + decoration: mixture.decoration, + width: mixture.width, + height: mixture.height, + constraints: mixture.constraints, + margin: mixture.margin, + transform: mixture.transform, + clipBehavior: mixture.clipBehavior ?? Clip.none, + child: child, + ); + } +} diff --git a/lib/src/recipes/flex/flex_attribute.dart b/lib/src/recipes/flex/flex_attribute.dart new file mode 100644 index 000000000..10f29c7aa --- /dev/null +++ b/lib/src/recipes/flex/flex_attribute.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; +import 'flex_spec.dart'; + +class FlexMixAttribute extends ResolvableAttribute { + final Axis? direction; + final MainAxisAlignment? mainAxisAlignment; + final CrossAxisAlignment? crossAxisAlignment; + final MainAxisSize? mainAxisSize; + final VerticalDirection? verticalDirection; + final TextDirection? textDirection; + final TextBaseline? textBaseline; + final Clip? clipBehavior; + final double? gap; + + const FlexMixAttribute({ + this.direction, + this.mainAxisAlignment, + this.crossAxisAlignment, + this.mainAxisSize, + this.verticalDirection, + this.textDirection, + this.textBaseline, + this.clipBehavior, + this.gap, + }); + + static FlexMixAttribute of(MixData mix) { + return mix.attributeOf() ?? const FlexMixAttribute(); + } + + @override + FlexSpec resolve(MixData mix) { + return FlexSpec( + crossAxisAlignment: crossAxisAlignment, + mainAxisAlignment: mainAxisAlignment, + mainAxisSize: mainAxisSize, + verticalDirection: verticalDirection, + direction: direction, + textDirection: textDirection, + textBaseline: textBaseline, + clipBehavior: clipBehavior, + gap: gap, + ); + } + + @override + FlexMixAttribute merge(covariant FlexMixAttribute? other) { + if (other == null) return this; + + return FlexMixAttribute( + direction: other.direction ?? direction, + mainAxisAlignment: other.mainAxisAlignment ?? mainAxisAlignment, + crossAxisAlignment: other.crossAxisAlignment ?? crossAxisAlignment, + mainAxisSize: other.mainAxisSize ?? mainAxisSize, + verticalDirection: other.verticalDirection ?? verticalDirection, + textDirection: other.textDirection ?? textDirection, + textBaseline: other.textBaseline ?? textBaseline, + clipBehavior: other.clipBehavior ?? clipBehavior, + gap: other.gap ?? gap, + ); + } + + @override + List get props => [ + direction, + mainAxisAlignment, + crossAxisAlignment, + mainAxisSize, + verticalDirection, + textDirection, + textBaseline, + clipBehavior, + gap, + ]; +} diff --git a/lib/src/specs/flex_spec.dart b/lib/src/recipes/flex/flex_spec.dart similarity index 52% rename from lib/src/specs/flex_spec.dart rename to lib/src/recipes/flex/flex_spec.dart index c994b34c3..f21998777 100644 --- a/lib/src/specs/flex_spec.dart +++ b/lib/src/recipes/flex/flex_spec.dart @@ -1,10 +1,12 @@ +import 'dart:ui'; + import 'package:flutter/material.dart'; -import '../attributes/attribute.dart'; -import '../attributes/scalar_attribute.dart'; -import '../factory/mix_provider_data.dart'; +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; +import 'flex_attribute.dart'; -class FlexSpec extends MixExtension { +class FlexSpec extends Spec { final Axis? direction; final MainAxisAlignment? mainAxisAlignment; final CrossAxisAlignment? crossAxisAlignment; @@ -13,6 +15,7 @@ class FlexSpec extends MixExtension { final TextDirection? textDirection; final TextBaseline? textBaseline; final Clip? clipBehavior; + final double? gap; const FlexSpec({ required this.crossAxisAlignment, @@ -23,35 +26,56 @@ class FlexSpec extends MixExtension { required this.textDirection, required this.textBaseline, required this.clipBehavior, + required this.gap, }); + const FlexSpec.empty() + : crossAxisAlignment = null, + mainAxisAlignment = null, + mainAxisSize = null, + verticalDirection = null, + direction = null, + textDirection = null, + textBaseline = null, + clipBehavior = null, + gap = null; + static FlexSpec resolve(MixData mix) { + final recipe = mix.attributeOf()?.resolve(mix); + + return recipe ?? const FlexMixAttribute().resolve(mix); + } + + @override + FlexSpec lerp(FlexSpec other, double t) { return FlexSpec( crossAxisAlignment: - mix.get(), + lerpSnap(crossAxisAlignment, other.crossAxisAlignment, t), mainAxisAlignment: - mix.get(), - mainAxisSize: mix.get(), + lerpSnap(mainAxisAlignment, other.mainAxisAlignment, t), + mainAxisSize: lerpSnap(mainAxisSize, other.mainAxisSize, t), verticalDirection: - mix.get(), - direction: mix.get(), - textDirection: mix.get(), - textBaseline: mix.get(), - clipBehavior: mix.get(), + lerpSnap(verticalDirection, other.verticalDirection, t), + direction: lerpSnap(direction, other.direction, t), + textDirection: lerpSnap(textDirection, other.textDirection, t), + textBaseline: lerpSnap(textBaseline, other.textBaseline, t), + clipBehavior: lerpSnap(clipBehavior, other.clipBehavior, t), + gap: lerpDouble(gap, other.gap, t), ); } @override - FlexSpec lerp(FlexSpec other, double t) { - return FlexSpec( - crossAxisAlignment: snap(crossAxisAlignment, other.crossAxisAlignment, t), - mainAxisAlignment: snap(mainAxisAlignment, other.mainAxisAlignment, t), - mainAxisSize: snap(mainAxisSize, other.mainAxisSize, t), - verticalDirection: snap(verticalDirection, other.verticalDirection, t), - direction: snap(direction, other.direction, t), - textDirection: snap(textDirection, other.textDirection, t), - textBaseline: snap(textBaseline, other.textBaseline, t), - clipBehavior: snap(clipBehavior, other.clipBehavior, t), + FlexSpec merge(FlexSpec? other) { + return copyWith( + direction: other?.direction, + mainAxisAlignment: other?.mainAxisAlignment, + crossAxisAlignment: other?.crossAxisAlignment, + mainAxisSize: other?.mainAxisSize, + verticalDirection: other?.verticalDirection, + textDirection: other?.textDirection, + textBaseline: other?.textBaseline, + clipBehavior: other?.clipBehavior, + gap: other?.gap, ); } @@ -65,6 +89,7 @@ class FlexSpec extends MixExtension { TextDirection? textDirection, TextBaseline? textBaseline, Clip? clipBehavior, + double? gap, }) { return FlexSpec( crossAxisAlignment: crossAxisAlignment ?? this.crossAxisAlignment, @@ -75,6 +100,7 @@ class FlexSpec extends MixExtension { textDirection: textDirection ?? this.textDirection, textBaseline: textBaseline ?? this.textBaseline, clipBehavior: clipBehavior ?? this.clipBehavior, + gap: gap ?? this.gap, ); } @@ -88,5 +114,6 @@ class FlexSpec extends MixExtension { textDirection, textBaseline, clipBehavior, + gap, ]; } diff --git a/lib/src/recipes/flex/flex_util.dart b/lib/src/recipes/flex/flex_util.dart new file mode 100644 index 000000000..86ab05b79 --- /dev/null +++ b/lib/src/recipes/flex/flex_util.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; + +import '../../../mix.dart'; + +/// A utility class for building [FlexMixAttribute]s. +final flex = FlexSpecUtility.selfBuilder; + +class FlexSpecUtility + extends MixUtility { + static final selfBuilder = FlexSpecUtility((value) => value); + + const FlexSpecUtility(super.builder); + + AxisUtility get direction { + return AxisUtility((direction) => call(direction: direction)); + } + + MainAxisAlignmentUtility get mainAxisAlignment { + return MainAxisAlignmentUtility( + (mainAxisAlignment) => call(mainAxisAlignment: mainAxisAlignment), + ); + } + + CrossAxisAlignmentUtility get crossAxisAlignment { + return CrossAxisAlignmentUtility( + (crossAxisAlignment) => call(crossAxisAlignment: crossAxisAlignment), + ); + } + + MainAxisSizeUtility get mainAxisSize { + return MainAxisSizeUtility( + (mainAxisSize) => call(mainAxisSize: mainAxisSize), + ); + } + + VerticalDirectionUtility get verticalDirection { + return VerticalDirectionUtility( + (verticalDirection) => call(verticalDirection: verticalDirection), + ); + } + + TextDirectionUtility get textDirection { + return TextDirectionUtility( + (textDirection) => call(textDirection: textDirection), + ); + } + + TextBaselineUtility get textBaseline { + return TextBaselineUtility( + (textBaseline) => call(textBaseline: textBaseline), + ); + } + + ClipUtility get clipBehavior { + return ClipUtility((clipBehavior) => call(clipBehavior: clipBehavior)); + } + + SpacingSideUtility get gap { + return SpacingSideUtility((gap) => call(gap: gap)); + } + + T row() => direction.horizontal(); + T column() => direction.vertical(); + + T call({ + Axis? direction, + MainAxisAlignment? mainAxisAlignment, + CrossAxisAlignment? crossAxisAlignment, + MainAxisSize? mainAxisSize, + VerticalDirection? verticalDirection, + TextDirection? textDirection, + TextBaseline? textBaseline, + Clip? clipBehavior, + double? gap, + }) { + final attriubte = FlexMixAttribute( + direction: direction, + mainAxisAlignment: mainAxisAlignment, + crossAxisAlignment: crossAxisAlignment, + mainAxisSize: mainAxisSize, + verticalDirection: verticalDirection, + textDirection: textDirection, + textBaseline: textBaseline, + clipBehavior: clipBehavior, + gap: gap, + ); + + return builder(attriubte); + } +} diff --git a/lib/src/recipes/flex/flex_widget.dart b/lib/src/recipes/flex/flex_widget.dart new file mode 100644 index 000000000..c91c4ad31 --- /dev/null +++ b/lib/src/recipes/flex/flex_widget.dart @@ -0,0 +1,249 @@ +import 'package:flutter/widgets.dart'; + +import '../../helpers/build_context_ext.dart'; +import '../../widgets/gap_widget.dart'; +import '../../widgets/styled_widget.dart'; +import '../container/container_attribute.dart'; +import '../container/container_widget.dart'; +import 'flex_attribute.dart'; +import 'flex_spec.dart'; + +/// A flexible layout widget enhanced with `StyleMix` for simplified styling. +/// +/// `StyledFlex` extends the capabilities of the Flutter `Flex` widget by integrating +/// `StyleMix`, allowing for more streamlined styling. This widget is ideal for creating +/// flexible layouts (either rows or columns) with enhanced styling capabilities. +/// +/// The `direction` parameter determines the layout orientation, while the `children` +/// parameter takes a list of widgets. The `StyleMix` integration allows for applying +/// consistent styles across all child widgets easily. +/// +/// Example Usage: +/// ```dart +/// StyledFlex( +/// direction: Axis.horizontal, +/// style: yourStyleMix, +/// children: [Widget1(), Widget2(), Widget3()], +/// ); +/// ``` +class StyledFlex extends StyledWidget { + const StyledFlex({ + super.style, + super.key, + super.inherit, + required this.direction, + this.children = const [], + }); + + final List children; + final Axis direction; + + @override + Widget build(BuildContext context) { + final contextMix = context.mix; + final inheritedAttribute = inherit && contextMix != null + ? FlexMixAttribute.of(contextMix) + : const FlexMixAttribute(); + + return withMix(context, (mix) { + final attribute = FlexMixAttribute.of(mix); + final merged = inheritedAttribute.merge(attribute); + + final mixture = merged.resolve(mix); + + List renderSpacedChildren() { + final gap = mixture.gap; + + return gap == null + ? children + : List.generate( + children.length * 2 - 1, + (index) => index % 2 == 0 ? children[index ~/ 2] : Gap(gap), + ); + } + + return MixedFlex( + mixture, + direction: direction, + children: renderSpacedChildren(), + ); + }); + } +} + +class MixedFlex extends StatelessWidget { + const MixedFlex( + this.mixture, { + super.key, + required this.children, + required this.direction, + }); + + final List children; + final Axis direction; + final FlexSpec mixture; + + @override + Widget build(BuildContext context) { + return Flex( + direction: direction, + mainAxisAlignment: + mixture.mainAxisAlignment ?? _defaultFlex.mainAxisAlignment, + mainAxisSize: mixture.mainAxisSize ?? _defaultFlex.mainAxisSize, + crossAxisAlignment: + mixture.crossAxisAlignment ?? _defaultFlex.crossAxisAlignment, + verticalDirection: + mixture.verticalDirection ?? _defaultFlex.verticalDirection, + children: children, + ); + } +} + +/// A horizontal layout widget leveraging `StyleMix` for advanced styling. +/// +/// `StyledRow` is a specialized form of `StyledFlex` that defaults to a horizontal +/// direction (i.e., `Axis.horizontal`). It benefits from `StyleMix` integration, +/// enabling more efficient and consistent styling across its children. +/// +/// Inherits all the styling and layout properties of `StyledFlex`, with a simplified +/// interface for horizontal layouts. +/// +/// Example Usage: +/// ```dart +/// StyledRow( +/// style: yourStyleMix, +/// children: [Widget1(), Widget2()], +/// ); +/// ``` +class StyledRow extends StyledFlex { + const StyledRow({ + super.style, + super.key, + super.inherit, + required super.children, + }) : super(direction: Axis.horizontal); +} + +/// A vertical layout widget enhanced with `StyleMix` for easy styling. +/// +/// `StyledColumn` is a vertical variant of `StyledFlex`, employing `StyleMix` for +/// an improved styling experience. It's designed for vertical arrangements of widgets, +/// providing a consistent and easy-to-manage styling approach. +/// +/// Inherits the comprehensive styling capabilities of `StyledFlex`, tailored for +/// vertical layouts. +/// +/// Example Usage: +/// ```dart +/// StyledColumn( +/// style: yourStyleMix, +/// children: [Widget1(), Widget2()], +/// ); +/// ``` +class StyledColumn extends StyledFlex { + const StyledColumn({ + super.style, + super.key, + super.inherit, + super.children, + }) : super(direction: Axis.vertical); +} + +/// A flex container widget with integrated `StyleMix` for enhanced styling. +/// +/// `FlexBox` combines the features of `StyledContainer` and `StyledFlex`, offering +/// a powerful tool for creating flexible layouts with advanced styling capabilities +/// through `StyleMix`. It's perfect for designing complex layouts that require both +/// container and flex properties with uniform styling. +/// +/// The `direction` parameter sets the layout's orientation, while the `StyleMix` +/// integration simplifies the process of applying consistent styles to all elements. +/// +/// Example Usage: +/// ```dart +/// FlexBox( +/// direction: Axis.horizontal, +/// style: yourStyleMix, +/// children: [Widget1(), Widget2()], +/// ); +/// ``` +class FlexBox extends StyledWidget { + const FlexBox({ + super.style, + super.key, + super.inherit, + required this.direction, + required this.children, + }); + + final List children; + final Axis direction; + + @override + Widget build(BuildContext context) { + return withMix(context, (mix) { + final containerStyle = ContainerSpecAttribute.of(mix).resolve(mix); + final flexStyle = FlexMixAttribute.of(mix).resolve(mix); + + return MixedContainer( + containerStyle, + child: MixedFlex( + flexStyle, + direction: direction, + children: children, + ), + ); + }); + } +} + +/// A horizontal flex container with `StyleMix` for easy and consistent styling. +/// +/// `HBox` is a specialized `FlexBox` designed for horizontal layouts, simplifying +/// the process of applying horizontal alignment with advanced styling via `StyleMix`. +/// It's an efficient way to achieve consistent styling in horizontal arrangements. +/// +/// Inherits all functionalities of `FlexBox`, optimized for horizontal layouts. +/// +/// Example Usage: +/// ```dart +/// HBox( +/// style: yourStyleMix, +/// children: [Widget1(), Widget2()], +/// ); +/// ``` +class HBox extends FlexBox { + const HBox({ + super.style, + super.key, + super.inherit, + super.children = const [], + }) : super(direction: Axis.horizontal); +} + +/// A vertical flex container that uses `StyleMix` for streamlined styling. +/// +/// `VBox` is a vertical counterpart to `HBox`, utilizing `StyleMix` for efficient +/// and consistent styling in vertical layouts. It offers an easy way to manage +/// vertical alignment and styling in a cohesive manner. +/// +/// Inherits the comprehensive styling and layout capabilities of `FlexBox`, tailored +/// for vertical orientations. +/// +/// Example Usage: +/// ```dart +/// VBox( +/// style: yourStyleMix, +/// children: [Widget1(), Widget2()], +/// ); +/// ``` +class VBox extends FlexBox { + const VBox({ + super.style, + super.key, + super.inherit, + super.children = const [], + }) : super(direction: Axis.vertical); +} + +const _defaultFlex = Flex(direction: Axis.horizontal, children: []); diff --git a/lib/src/recipes/icon/icon_attribute.dart b/lib/src/recipes/icon/icon_attribute.dart new file mode 100644 index 000000000..5900a9bb2 --- /dev/null +++ b/lib/src/recipes/icon/icon_attribute.dart @@ -0,0 +1,33 @@ +import '../../attributes/color/color_dto.dart'; +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; +import 'icon_spec.dart'; + +class IconMixAttribute extends ResolvableAttribute { + final double? size; + final ColorDto? color; + + const IconMixAttribute({this.size, this.color}); + + static IconMixAttribute of(MixData mix) { + return mix.attributeOf() ?? const IconMixAttribute(); + } + + @override + IconSpec resolve(MixData mix) { + return IconSpec(color: color?.resolve(mix), size: size); + } + + @override + IconMixAttribute merge(covariant IconMixAttribute? other) { + if (other == null) return this; + + return IconMixAttribute( + size: size ?? other.size, + color: color ?? other.color, + ); + } + + @override + get props => [size, color]; +} diff --git a/lib/src/recipes/icon/icon_spec.dart b/lib/src/recipes/icon/icon_spec.dart new file mode 100644 index 000000000..658c44e5e --- /dev/null +++ b/lib/src/recipes/icon/icon_spec.dart @@ -0,0 +1,47 @@ +import 'dart:ui'; + +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; +import 'icon_attribute.dart'; + +class IconSpec extends Spec { + final Color? color; + final double? size; + + const IconSpec({required this.color, required this.size}); + + const IconSpec.empty() + : color = null, + size = null; + + static IconSpec resolve(MixData mix) { + final recipe = mix.attributeOf()?.resolve(mix); + + return recipe ?? const IconMixAttribute().resolve(mix); + } + + @override + IconSpec lerp(IconSpec other, double t) { + return IconSpec( + color: Color.lerp(color, other.color, t), + size: lerpDouble(size, other.size, t), + ); + } + + @override + IconSpec merge(IconSpec? other) { + return copyWith(color: other?.color, size: other?.size); + } + + @override + IconSpec copyWith({ + Color? color, + double? size, + TextDirection? textDirection, + }) { + return IconSpec(color: color ?? this.color, size: size ?? this.size); + } + + @override + get props => [color, size]; +} diff --git a/lib/src/recipes/icon/icon_util.dart b/lib/src/recipes/icon/icon_util.dart new file mode 100644 index 000000000..05f8ef55c --- /dev/null +++ b/lib/src/recipes/icon/icon_util.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + +import '../../attributes/color/color_util.dart'; +import '../../attributes/scalars/scalar_util.dart'; +import '../../core/attribute.dart'; +import '../../core/extensions/values_ext.dart'; +import 'icon_attribute.dart'; + +const icon = IconUtility.selfBuilder; + +class IconUtility + extends MixUtility { + static const selfBuilder = IconUtility(MixUtility.selfBuilder); + const IconUtility(super.builder); + + ColorUtility get color { + return ColorUtility((color) => builder(IconMixAttribute(color: color))); + } + + T size(double size) { + return builder(IconMixAttribute(size: size)); + } + + IconMixAttribute call({double? size, Color? color}) { + return IconMixAttribute(size: size, color: color?.toDto()); + } +} diff --git a/lib/src/recipes/icon/icon_widget.dart b/lib/src/recipes/icon/icon_widget.dart new file mode 100644 index 000000000..0a66410ee --- /dev/null +++ b/lib/src/recipes/icon/icon_widget.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; + +import '../../helpers/build_context_ext.dart'; +import '../../widgets/styled_widget.dart'; +import 'icon_attribute.dart'; + +class StyledIcon extends StyledWidget { + const StyledIcon( + this.icon, { + this.semanticLabel, + super.style, + super.key, + super.inherit, + this.textDirection, + }); + + final IconData? icon; + final String? semanticLabel; + final TextDirection? textDirection; + + @override + Widget build(BuildContext context) { + final contextMix = context.mix; + final inheritedAttribute = inherit && contextMix != null + ? IconMixAttribute.of(contextMix) + : const IconMixAttribute(); + + return withMix(context, (mix) { + final attribute = IconMixAttribute.of(mix); + final merged = inheritedAttribute.merge(attribute); + + final mixture = merged.resolve(mix); + + return Icon( + icon, + size: mixture.size, + color: mixture.color, + semanticLabel: semanticLabel, + textDirection: textDirection, + ); + }); + } +} + +class AnimatedStyledIcon extends StyledWidget { + const AnimatedStyledIcon( + this.icon, { + this.semanticLabel, + super.style, + super.key, + required this.progress, + super.inherit, + this.textDirection, + }); + + final AnimatedIconData icon; + final String? semanticLabel; + final Animation progress; + final TextDirection? textDirection; + + @override + Widget build(BuildContext context) { + final inheritedAttribute = inherit && context.mix != null + // ignore: avoid-non-null-assertion + ? IconMixAttribute.of(context.mix!) + : const IconMixAttribute(); + + return withMix(context, (mix) { + final attribute = IconMixAttribute.of(mix); + final merged = inheritedAttribute.merge(attribute); + + final mixture = merged.resolve(mix); + + return AnimatedIcon( + icon: icon, + progress: progress, + color: mixture.color, + size: mixture.size, + semanticLabel: semanticLabel, + textDirection: textDirection, + ); + }); + } +} diff --git a/lib/src/recipes/image/image_attribute.dart b/lib/src/recipes/image/image_attribute.dart new file mode 100644 index 000000000..ff1506ca8 --- /dev/null +++ b/lib/src/recipes/image/image_attribute.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; + +import '../../attributes/color/color_dto.dart'; +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; +import 'image_spec.dart'; + +class ImageMixAttribute + extends ResolvableAttribute { + final double? width; + final double? height; + final ColorDto? color; + final ImageRepeat? repeat; + final BoxFit? fit; + + const ImageMixAttribute({ + this.width, + this.height, + this.color, + this.repeat, + this.fit, + }); + + @override + ImageSpec resolve(MixData mix) { + return ImageSpec( + width: width, + height: height, + color: color?.resolve(mix), + repeat: repeat, + fit: fit, + ); + } + + @override + ImageMixAttribute merge(covariant ImageMixAttribute? other) { + if (other == null) return this; + + return ImageMixAttribute( + width: width ?? other.width, + height: height ?? other.height, + color: color ?? other.color, + repeat: repeat ?? other.repeat, + fit: fit ?? other.fit, + ); + } + + @override + get props => [width, height, color, repeat, fit]; +} diff --git a/lib/src/specs/image_spec.dart b/lib/src/recipes/image/image_spec.dart similarity index 65% rename from lib/src/specs/image_spec.dart rename to lib/src/recipes/image/image_spec.dart index 1e9544320..9d8051417 100644 --- a/lib/src/specs/image_spec.dart +++ b/lib/src/recipes/image/image_spec.dart @@ -3,13 +3,12 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import '../attributes/attribute.dart'; -import '../attributes/color_attribute.dart'; -import '../attributes/scalar_attribute.dart'; -import '../factory/mix_provider_data.dart'; +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; +import 'image_attribute.dart'; @immutable -class ImageSpec extends MixExtension { +class ImageSpec extends Spec { final double? width, height; final Color? color; final ImageRepeat? repeat; @@ -23,13 +22,27 @@ class ImageSpec extends MixExtension { required this.fit, }); + const ImageSpec.empty() + : width = null, + height = null, + color = null, + repeat = null, + fit = null; + static ImageSpec resolve(MixData mix) { - return ImageSpec( - width: mix.get(), - height: mix.get(), - color: mix.get(), - repeat: mix.get(), - fit: mix.get(), + final recipe = mix.attributeOf()?.resolve(mix); + + return recipe ?? const ImageMixAttribute().resolve(mix); + } + + @override + ImageSpec merge(ImageSpec? other) { + return copyWith( + width: other?.width, + height: other?.height, + color: other?.color, + repeat: other?.repeat, + fit: other?.fit, ); } diff --git a/lib/src/recipes/image/image_util.dart b/lib/src/recipes/image/image_util.dart new file mode 100644 index 000000000..bc7e7d736 --- /dev/null +++ b/lib/src/recipes/image/image_util.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; + +import '../../attributes/color/color_util.dart'; +import '../../attributes/scalars/scalar_util.dart'; +import '../../core/attribute.dart'; +import '../../core/extensions/values_ext.dart'; +import 'image_attribute.dart'; + +const image = ImageUtility.selfBuilder; + +class ImageUtility + extends MixUtility { + static const selfBuilder = ImageUtility(MixUtility.selfBuilder); + const ImageUtility(super.builder); + + ColorUtility get color { + return ColorUtility((color) => builder(ImageMixAttribute(color: color))); + } + + ImageRepeatUtility get repeat { + return ImageRepeatUtility( + (repeat) => builder(ImageMixAttribute(repeat: repeat)), + ); + } + + BoxFitUtility get fit { + return BoxFitUtility((fit) => builder(ImageMixAttribute(fit: fit))); + } + + ImageMixAttribute call({ + double? width, + double? height, + Color? color, + ImageRepeat? repeat, + BoxFit? fit, + }) { + return ImageMixAttribute( + width: width, + height: height, + color: color?.toDto(), + repeat: repeat, + fit: fit, + ); + } +} diff --git a/lib/src/recipes/stack/stack_attribute.dart b/lib/src/recipes/stack/stack_attribute.dart new file mode 100644 index 000000000..abaf356b0 --- /dev/null +++ b/lib/src/recipes/stack/stack_attribute.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; + +import '../../core/attribute.dart'; +import '../../factory/mix_provider_data.dart'; +import 'stack_spec.dart'; + +class StackMixAttribute + extends ResolvableAttribute { + final Clip? _clipBehavior; + final TextDirection? _textDirection; + final StackFit? _fit; + final AlignmentGeometry? _alignment; + const StackMixAttribute({ + AlignmentGeometry? alignment, + StackFit? fit, + TextDirection? textDirection, + Clip? clipBehavior, + }) : _clipBehavior = clipBehavior, + _textDirection = textDirection, + _fit = fit, + _alignment = alignment; + + static StackMixAttribute of(MixData mix) { + return mix.attributeOf() ?? const StackMixAttribute(); + } + + @override + StackSpec resolve(MixData mix) { + return StackSpec( + alignment: _alignment, + fit: _fit, + textDirection: _textDirection, + clipBehavior: _clipBehavior, + ); + } + + @override + StackMixAttribute merge(covariant StackMixAttribute? other) { + if (other == null) return this; + + return StackMixAttribute( + alignment: other._alignment ?? _alignment, + fit: other._fit ?? _fit, + textDirection: other._textDirection ?? _textDirection, + clipBehavior: other._clipBehavior ?? _clipBehavior, + ); + } + + @override + List get props => [_alignment, _fit, _textDirection, _clipBehavior]; +} diff --git a/lib/src/specs/stack_spec.dart b/lib/src/recipes/stack/stack_spec.dart similarity index 66% rename from lib/src/specs/stack_spec.dart rename to lib/src/recipes/stack/stack_spec.dart index ee7f8af5d..93d1d272c 100644 --- a/lib/src/specs/stack_spec.dart +++ b/lib/src/recipes/stack/stack_spec.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; -import '../attributes/alignment_attribute.dart'; -import '../attributes/attribute.dart'; -import '../attributes/scalar_attribute.dart'; -import '../factory/mix_provider_data.dart'; +import '../../core/attribute.dart'; -class StackSpec extends MixExtension { +class StackSpec extends Spec { final AlignmentGeometry? alignment; final StackFit? fit; final TextDirection? textDirection; @@ -18,15 +15,6 @@ class StackSpec extends MixExtension { this.clipBehavior, }); - static StackSpec resolve(MixData mix) { - return StackSpec( - alignment: mix.get(), - fit: mix.get(), - textDirection: mix.get(), - clipBehavior: mix.get(), - ); - } - @override StackSpec lerp(StackSpec other, double t) { return StackSpec( @@ -37,6 +25,16 @@ class StackSpec extends MixExtension { ); } + @override + StackSpec merge(StackSpec? other) { + return copyWith( + alignment: other?.alignment, + fit: other?.fit, + textDirection: other?.textDirection, + clipBehavior: other?.clipBehavior, + ); + } + @override StackSpec copyWith({ AlignmentGeometry? alignment, diff --git a/lib/src/recipes/stack/stack_util.dart b/lib/src/recipes/stack/stack_util.dart new file mode 100644 index 000000000..96838e61a --- /dev/null +++ b/lib/src/recipes/stack/stack_util.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; + +import '../../attributes/scalars/scalar_util.dart'; +import '../../core/attribute.dart'; +import 'stack_attribute.dart'; + +const stack = StackSpecUtility.selfBuilder; + +class StackSpecUtility + extends MixUtility { + static const selfBuilder = StackSpecUtility(MixUtility.selfBuilder); + + const StackSpecUtility(super.builder); + + T _only({ + AlignmentGeometry? alignment, + StackFit? fit, + TextDirection? textDirection, + Clip? clipBehavior, + }) { + final stack = StackMixAttribute( + alignment: alignment, + fit: fit, + textDirection: textDirection, + clipBehavior: clipBehavior, + ); + + return builder(stack); + } + + AlignmentUtility get alignment { + return AlignmentUtility((alignment) => _only(alignment: alignment)); + } + + StackFitUtility get fit { + return StackFitUtility((fit) => _only(fit: fit)); + } + + TextDirectionUtility get textDirection { + return TextDirectionUtility( + (textDirection) => _only(textDirection: textDirection), + ); + } + + ClipUtility get clipBehavior { + return ClipUtility((clipBehavior) => _only(clipBehavior: clipBehavior)); + } + + T call({ + AlignmentGeometry? alignment, + StackFit? fit, + TextDirection? textDirection, + Clip? clipBehavior, + }) { + return _only( + alignment: alignment, + fit: fit, + textDirection: textDirection, + clipBehavior: clipBehavior, + ); + } +} diff --git a/lib/src/recipes/stack/stack_widget.dart b/lib/src/recipes/stack/stack_widget.dart new file mode 100644 index 000000000..b40805c04 --- /dev/null +++ b/lib/src/recipes/stack/stack_widget.dart @@ -0,0 +1,79 @@ +import 'package:flutter/widgets.dart'; + +import '../../helpers/build_context_ext.dart'; +import '../../widgets/styled_widget.dart'; +import '../container/container_attribute.dart'; +import '../container/container_widget.dart'; +import 'stack_attribute.dart'; +import 'stack_spec.dart'; + +class StyledStack extends StyledWidget { + const StyledStack({ + this.children = const [], + super.inherit, + super.key, + super.style, + }); + + final List children; + @override + Widget build(BuildContext context) { + final contextMix = context.mix; + final inheritedAttribute = inherit && contextMix != null + ? StackMixAttribute.of(contextMix) + : const StackMixAttribute(); + + return withMix(context, (mix) { + final attribute = StackMixAttribute.of(mix); + final merged = inheritedAttribute.merge(attribute); + + final mixture = merged.resolve(mix); + + return MixedStack(mixture, children: children); + }); + } +} + +class MixedStack extends StatelessWidget { + const MixedStack(this.mixture, {super.key, this.children}); + + final List? children; + final StackSpec mixture; + + @override + Widget build(BuildContext context) { + return Stack( + alignment: mixture.alignment ?? _defaultStack.alignment, + textDirection: mixture.textDirection, + fit: mixture.fit ?? _defaultStack.fit, + clipBehavior: mixture.clipBehavior ?? _defaultStack.clipBehavior, + children: children ?? const [], + ); + } +} + +class ZBox extends StyledWidget { + const ZBox({ + this.children = const [], + super.inherit, + super.key, + super.style, + }); + + final List children; + + @override + Widget build(BuildContext context) { + return withMix(context, (mix) { + final containerMix = ContainerSpecAttribute.of(mix).resolve(mix); + final stackMix = StackMixAttribute.of(mix).resolve(mix); + + return MixedContainer( + containerMix, + child: MixedStack(stackMix, children: children), + ); + }); + } +} + +const _defaultStack = Stack(); diff --git a/lib/src/recipes/text/text_attribute.dart b/lib/src/recipes/text/text_attribute.dart new file mode 100644 index 000000000..a79071218 --- /dev/null +++ b/lib/src/recipes/text/text_attribute.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.dart'; + +import '../../attributes/strut_style/strut_style_attribute.dart'; +import '../../attributes/strut_style/strut_style_dto.dart'; +import '../../attributes/text_style/text_style_attribute.dart'; +import '../../attributes/text_style/text_style_dto.dart'; +import '../../core/attribute.dart'; +import '../../core/directive.dart'; +import '../../factory/mix_provider_data.dart'; +import 'text_spec.dart'; + +class TextMixAttribute extends ResolvableAttribute { + final TextOverflow? overflow; + final StrutStyleDto? strutStyle; + final TextAlign? textAlign; + final double? textScaleFactor; + final int? maxLines; + final TextStyleDto? style; + final TextWidthBasis? textWidthBasis; + final TextHeightBehavior? textHeightBehavior; + final TextDirection? textDirection; + final bool? softWrap; + final List? directives; + + const TextMixAttribute({ + this.overflow, + this.strutStyle, + this.textAlign, + this.textScaleFactor, + this.maxLines, + this.style, + this.textWidthBasis, + this.textHeightBehavior, + this.textDirection, + this.softWrap, + this.directives, + }); + + static TextMixAttribute of(MixData mix) { + final attribute = mix.attributeOf(); + + return TextMixAttribute( + strutStyle: mix.attributeOf()?.value, + style: mix.attributeOf()?.value, + ).merge(attribute); + } + + @override + TextSpec resolve(MixData mix) { + return TextSpec( + overflow: overflow, + strutStyle: strutStyle?.resolve(mix), + textAlign: textAlign, + textScaleFactor: textScaleFactor, + maxLines: maxLines, + style: style?.resolve(mix), + textWidthBasis: textWidthBasis, + textHeightBehavior: textHeightBehavior, + textDirection: textDirection, + softWrap: softWrap, + directives: directives ?? [], + ); + } + + @override + TextMixAttribute merge(covariant TextMixAttribute? other) { + if (other == null) return this; + + return TextMixAttribute( + overflow: other.overflow ?? overflow, + strutStyle: strutStyle?.merge(other.strutStyle) ?? other.strutStyle, + textAlign: other.textAlign ?? textAlign, + textScaleFactor: other.textScaleFactor ?? textScaleFactor, + maxLines: other.maxLines ?? maxLines, + style: style?.merge(other.style) ?? other.style, + textWidthBasis: other.textWidthBasis ?? textWidthBasis, + textHeightBehavior: other.textHeightBehavior ?? textHeightBehavior, + textDirection: other.textDirection ?? textDirection, + softWrap: other.softWrap ?? softWrap, + directives: [...(directives ?? []), ...(other.directives ?? [])], + ); + } + + @override + List get props => [ + overflow, + strutStyle, + textAlign, + textScaleFactor, + maxLines, + style, + textWidthBasis, + textHeightBehavior, + textDirection, + softWrap, + directives, + ]; +} diff --git a/lib/src/specs/text_spec.dart b/lib/src/recipes/text/text_spec.dart similarity index 55% rename from lib/src/specs/text_spec.dart rename to lib/src/recipes/text/text_spec.dart index f810cc012..600fb05d3 100644 --- a/lib/src/specs/text_spec.dart +++ b/lib/src/recipes/text/text_spec.dart @@ -1,13 +1,9 @@ import 'package:flutter/material.dart'; -import '../attributes/attribute.dart'; -import '../attributes/scalar_attribute.dart'; -import '../attributes/strut_style_attribute.dart'; -import '../attributes/text_style_attribute.dart'; -import '../directives/text_directive.dart'; -import '../factory/mix_provider_data.dart'; +import '../../core/attribute.dart'; +import '../../core/directive.dart'; -class TextSpec extends MixExtension { +class TextSpec extends Spec { final TextOverflow? overflow; final StrutStyle? strutStyle; final TextAlign? textAlign; @@ -34,32 +30,53 @@ class TextSpec extends MixExtension { this.directives = const [], }); - static TextSpec resolve(MixData mix) { - return TextSpec( - overflow: mix.get(), - strutStyle: mix.get(), - textAlign: mix.get(), - textScaleFactor: mix.get(), - maxLines: mix.get(), - style: mix.get(), - textWidthBasis: mix.get(), - textHeightBehavior: - mix.get(), - textDirection: mix.get(), - softWrap: mix.get(), - directives: mix.get>() ?? [], - ); - } + // empty + const TextSpec.empty() + : overflow = null, + strutStyle = null, + textAlign = null, + textScaleFactor = null, + maxLines = null, + style = null, + textWidthBasis = null, + textHeightBehavior = null, + textDirection = null, + softWrap = null, + directives = const []; + + // static ContainerSpec? maybeOf(BuildContext context) { + // final mix = MixProvider.maybeOf(context); + + // return mix?.resolvableOf(const ContainerSpecAttribute()); + // } + + // static ContainerSpec of(BuildContext context) { + // return maybeOf(context) ?? const ContainerSpec.empty(); + // } String applyTextDirectives(String? text) { if (text == null) return ''; - String modifiedText = text; - for (final directive in directives) { - modifiedText = directive.modify(modifiedText); - } + return directives.fold(text, (text, directive) => directive(text)); + } + + @override + TextSpec merge(TextSpec? other) { + if (other == null) return this; - return modifiedText; + return copyWith( + softWrap: other.softWrap, + overflow: other.overflow, + strutStyle: other.strutStyle, + textAlign: other.textAlign, + textScaleFactor: other.textScaleFactor, + maxLines: other.maxLines, + style: other.style, + textWidthBasis: other.textWidthBasis, + textHeightBehavior: other.textHeightBehavior, + directives: other.directives, + textDirection: other.textDirection, + ); } @override @@ -67,18 +84,19 @@ class TextSpec extends MixExtension { // Define a helper method for snapping return TextSpec( - overflow: snap(overflow, other.overflow, t), - strutStyle: snap(strutStyle, other.strutStyle, t), - textAlign: snap(textAlign, other.textAlign, t), + overflow: lerpSnap(overflow, other.overflow, t), + strutStyle: lerpSnap(strutStyle, other.strutStyle, t), + textAlign: lerpSnap(textAlign, other.textAlign, t), textScaleFactor: genericNumLerp(textScaleFactor, other.textScaleFactor, t), - maxLines: snap(maxLines, other.maxLines, t), + maxLines: lerpSnap(maxLines, other.maxLines, t), style: TextStyle.lerp(style, other.style, t), - textWidthBasis: snap(textWidthBasis, other.textWidthBasis, t), - textHeightBehavior: snap(textHeightBehavior, other.textHeightBehavior, t), - textDirection: snap(textDirection, other.textDirection, t), - softWrap: snap(softWrap, other.softWrap, t), - directives: snap(directives, other.directives, t), + textWidthBasis: lerpSnap(textWidthBasis, other.textWidthBasis, t), + textHeightBehavior: + lerpSnap(textHeightBehavior, other.textHeightBehavior, t), + textDirection: lerpSnap(textDirection, other.textDirection, t), + softWrap: lerpSnap(softWrap, other.softWrap, t), + directives: lerpSnap(directives, other.directives, t), ); } diff --git a/lib/src/recipes/text/text_util.dart b/lib/src/recipes/text/text_util.dart new file mode 100644 index 000000000..e696ee61c --- /dev/null +++ b/lib/src/recipes/text/text_util.dart @@ -0,0 +1,130 @@ +import 'package:flutter/material.dart'; + +import '../../attributes/scalars/scalar_util.dart'; +import '../../attributes/strut_style/strut_style_dto.dart'; +import '../../attributes/strut_style/strut_style_util.dart'; +import '../../attributes/text_style/text_style_dto.dart'; +import '../../attributes/text_style/text_style_util.dart'; +import '../../core/attribute.dart'; +import '../../core/directive.dart'; +import '../../core/extensions/values_ext.dart'; +import 'text_attribute.dart'; + +const text = TextUtility.selfBuilder; + +class TextUtility + extends MixUtility { + static const selfBuilder = TextUtility(MixUtility.selfBuilder); + + const TextUtility(super.builder); + + TextOverflowUtility get overflow { + return TextOverflowUtility((overflow) => only(overflow: overflow)); + } + + StrutStyleUtility get strutStyle { + return StrutStyleUtility((strutStyle) => only(strutStyle: strutStyle)); + } + + TextAlignUtility get textAlign { + return TextAlignUtility((textAlign) => only(textAlign: textAlign)); + } + + IntUtility get maxLines { + return IntUtility((maxLines) => only(maxLines: maxLines)); + } + + TextStyleUtility get style { + return TextStyleUtility((style) => only(style: style)); + } + + TextWidthBasisUtility get textWidthBasis { + return TextWidthBasisUtility( + (textWidthBasis) => only(textWidthBasis: textWidthBasis), + ); + } + + TextHeightBehaviorUtility get textHeightBehavior { + return TextHeightBehaviorUtility( + (textHeightBehavior) => only(textHeightBehavior: textHeightBehavior), + ); + } + + TextDirectionUtility get textDirection { + return TextDirectionUtility( + (textDirection) => only(textDirection: textDirection), + ); + } + + BoolUtility get softWrap { + return BoolUtility((softWrap) => only(softWrap: softWrap)); + } + + T textScaleFactor(double textScaleFactor) { + return only(textScaleFactor: textScaleFactor); + } + + T directive(TextDirective directive) { + return only(directives: [directive]); + } + + T only({ + TextOverflow? overflow, + StrutStyleDto? strutStyle, + TextAlign? textAlign, + double? textScaleFactor, + int? maxLines, + TextStyleDto? style, + TextWidthBasis? textWidthBasis, + TextHeightBehavior? textHeightBehavior, + TextDirection? textDirection, + bool? softWrap, + List? directives, + }) { + final text = TextMixAttribute( + overflow: overflow, + strutStyle: strutStyle, + textAlign: textAlign, + textScaleFactor: textScaleFactor, + maxLines: maxLines, + style: style, + textWidthBasis: textWidthBasis, + textHeightBehavior: textHeightBehavior, + textDirection: textDirection, + softWrap: softWrap, + directives: directives, + ); + + return builder(text); + } + + T call({ + TextOverflow? overflow, + StrutStyle? strutStyle, + TextAlign? textAlign, + double? textScaleFactor, + int? maxLines, + TextStyle? style, + TextWidthBasis? textWidthBasis, + TextHeightBehavior? textHeightBehavior, + TextDirection? textDirection, + bool? softWrap, + List? directives, + }) { + final text = TextMixAttribute( + overflow: overflow, + strutStyle: strutStyle?.toDto(), + textAlign: textAlign, + textScaleFactor: textScaleFactor, + maxLines: maxLines, + style: style?.toDto(), + textWidthBasis: textWidthBasis, + textHeightBehavior: textHeightBehavior, + textDirection: textDirection, + softWrap: softWrap, + directives: directives, + ); + + return builder(text); + } +} diff --git a/lib/src/recipes/text/text_widget.dart b/lib/src/recipes/text/text_widget.dart new file mode 100644 index 000000000..5f2d46df8 --- /dev/null +++ b/lib/src/recipes/text/text_widget.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; + +import '../../helpers/build_context_ext.dart'; +import '../../widgets/styled_widget.dart'; +import 'text_attribute.dart'; + +class StyledText extends StyledWidget { + const StyledText( + this.text, { + this.semanticsLabel, + super.style, + super.key, + super.inherit, + this.locale, + }); + + final String text; + final String? semanticsLabel; + final Locale? locale; + + @override + Widget build(BuildContext context) { + final inheritedAttribute = inherit && context.mix != null + // ignore: avoid-non-null-assertion + ? TextMixAttribute.of(context.mix!) + : const TextMixAttribute(); + + return withMix(context, (mix) { + final attribute = TextMixAttribute.of(mix); + final merged = inheritedAttribute.merge(attribute); + + final mixture = merged.resolve(mix); + + return Text( + mixture.applyTextDirectives(text), + style: mixture.style, + strutStyle: mixture.strutStyle, + textAlign: mixture.textAlign, + textDirection: mixture.textDirection ?? TextDirection.ltr, + locale: locale, + softWrap: mixture.softWrap, + overflow: mixture.overflow, + textScaleFactor: mixture.textScaleFactor, + maxLines: mixture.maxLines, + semanticsLabel: semanticsLabel, + textWidthBasis: mixture.textWidthBasis, + textHeightBehavior: mixture.textHeightBehavior, + ); + }); + } +} diff --git a/lib/src/specs/icon_spec.dart b/lib/src/specs/icon_spec.dart deleted file mode 100644 index 889851048..000000000 --- a/lib/src/specs/icon_spec.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'dart:ui'; - -import '../attributes/attribute.dart'; -import '../attributes/color_attribute.dart'; -import '../attributes/scalar_attribute.dart'; -import '../factory/mix_provider_data.dart'; - -class IconSpec extends MixExtension { - final Color? color; - final double? size; - - final TextDirection? textDirection; - - const IconSpec({ - required this.color, - required this.size, - required this.textDirection, - }); - - static IconSpec resolve(MixData mix) { - return IconSpec( - color: mix.get(), - size: mix.get(), - textDirection: mix.get(), - ); - } - - @override - IconSpec lerp(IconSpec other, double t) { - return IconSpec( - color: Color.lerp(color, other.color, t), - size: lerpDouble(size, other.size, t), - textDirection: snap(textDirection, other.textDirection, t), - ); - } - - @override - IconSpec copyWith({ - Color? color, - double? size, - TextDirection? textDirection, - }) { - return IconSpec( - color: color ?? this.color, - size: size ?? this.size, - textDirection: textDirection ?? this.textDirection, - ); - } - - @override - get props => [color, size, textDirection]; -} diff --git a/lib/src/theme/mix_theme.dart b/lib/src/theme/mix_theme.dart index 8adc7b3fb..299d459a1 100644 --- a/lib/src/theme/mix_theme.dart +++ b/lib/src/theme/mix_theme.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; -import '../core/equality/compare_mixin.dart'; -import '../helpers/extensions/iterable_ext.dart'; +import '../helpers/compare_mixin.dart'; import 'tokens/breakpoints.dart'; import 'tokens/color_token.dart'; import 'tokens/mix_token.dart'; @@ -10,8 +9,7 @@ import 'tokens/space_token.dart'; import 'tokens/text_style_token.dart'; class MixTheme extends InheritedWidget { - const MixTheme({required Widget child, required this.data, Key? key}) - : super(key: key, child: child); + const MixTheme({required super.child, required this.data, super.key}); static MixThemeData of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType()?.data ?? @@ -30,157 +28,145 @@ class MixTheme extends InheritedWidget { @immutable class MixThemeData with Comparable { - final DesignTokenMap space; - - final DesignTokenMap breakpoints; - final DesignTokenMap colors; - - final DesignTokenMap textStyles; - final DesignTokenMap radii; + final StyledTokens radii; + final StyledTokens colors; + final StyledTokens textStyles; + final StyledTokens breakpoints; + final StyledTokens space; const MixThemeData.raw({ required this.breakpoints, required this.colors, - required this.space, required this.textStyles, required this.radii, + required this.space, }); const MixThemeData.empty() : this.raw( - breakpoints: const {}, - colors: const {}, - space: const {}, - textStyles: const {}, - radii: const {}, + breakpoints: const StyledTokens.empty(), + colors: const StyledTokens.empty(), + textStyles: const StyledTokens.empty(), + radii: const StyledTokens.empty(), + space: const StyledTokens.empty(), ); factory MixThemeData({ - DesignTokenMap? breakpoints, - DesignTokenMap? colors, - DesignTokenMap? space, - DesignTokenMap? textStyles, - DesignTokenMap? radii, + StyledTokens? breakpoints, + StyledTokens? colors, + StyledTokens? space, + StyledTokens? textStyles, + StyledTokens? radii, + }) { + return MixThemeData.raw( + breakpoints: breakpoints ?? const StyledTokens.empty(), + colors: colors ?? const StyledTokens.empty(), + textStyles: textStyles ?? const StyledTokens.empty(), + radii: radii ?? const StyledTokens.empty(), + space: space ?? const StyledTokens.empty(), + ); + } + + factory MixThemeData.tokenMap({ + TokenMap? breakpoints, + TokenMap? colors, + TokenMap? space, + TokenMap? textStyles, + TokenMap? radii, }) { return MixThemeData.raw( - breakpoints: {..._defaultBreakpoints, ...?breakpoints}, - colors: {...?colors}, - space: {..._defaultSpace, ...?space}, - textStyles: {...?textStyles}, - radii: {...?radii}, + breakpoints: StyledTokens(breakpoints ?? const {}), + colors: StyledTokens(colors ?? const {}), + textStyles: StyledTokens(textStyles ?? const {}), + radii: StyledTokens(radii ?? const {}), + space: StyledTokens(space ?? const {}), ); } MixThemeData copyWith({ - DesignTokenMap? breakpoints, - DesignTokenMap? colors, - DesignTokenMap? space, - DesignTokenMap? textStyles, - DesignTokenMap? radii, + StyledTokens? breakpoints, + StyledTokens? colors, + StyledTokens? space, + StyledTokens? textStyles, + StyledTokens? radii, }) { return MixThemeData.raw( breakpoints: breakpoints ?? this.breakpoints, colors: colors ?? this.colors, - space: space ?? this.space, textStyles: textStyles ?? this.textStyles, radii: radii ?? this.radii, + space: space ?? this.space, ); } - @override - get props => [space, breakpoints, colors, textStyles, radii]; -} + Color colorToken(BuildContext context, ColorToken token) { + return colors(token, context); + } -final DesignTokenMap _defaultSpace = { - SpaceToken.xsmall: (context) => 4.0, - SpaceToken.small: (context) => 8.0, - SpaceToken.medium: (context) => 16.0, - SpaceToken.large: (context) => 24.0, - SpaceToken.xlarge: (context) => 36.0, - SpaceToken.xxlarge: (context) => 72.0, -}; - -final DesignTokenMap - _defaultBreakpoints = { - BreakpointToken.xsmall: (context) => - const BreakpointConstraint(maxWidth: 599), - BreakpointToken.small: (context) => - const BreakpointConstraint(minWidth: 600, maxWidth: 1023), - BreakpointToken.medium: (context) => - const BreakpointConstraint(minWidth: 1024, maxWidth: 1439), - BreakpointToken.large: (context) => - const BreakpointConstraint(minWidth: 1440, maxWidth: double.infinity), -}; + Color colorRef(BuildContext context, ColorRef ref) => ref.resolve(context); -class MixTokenResolver { - final BuildContext context; + Radius radiiToken(BuildContext context, RadiusToken token) { + return radii(token, context); + } - const MixTokenResolver(this.context); + Radius radiiRef(BuildContext context, RadiusRef ref) => ref.resolve(context); - MixThemeData get _theme => MixTheme.of(context); + TextStyle textStyleToken(BuildContext context, TextStyleToken token) { + return textStyles(token, context); + } - Color colorToken(ColorToken token) { - final color = _theme.colors[token]?.call(context); + TextStyle textStyleRef(BuildContext context, TextStyleRef ref) => + ref.resolve(context); - if (color != null) return color; + double spaceTokenRef(BuildContext context, SpaceRef value) { + if (value >= 0) return value; + final token = space.findByValue(value); - if (token is ColorTokenResolver) { - return token.resolve(context); - } + return token == null ? 0.0 : spaceToken(context, token); + } - assert(color != null, 'Color token $token is not defined in Mix Theme'); + double spaceToken(BuildContext context, SpaceToken token) { + final value = space(token, context); - return color ?? Colors.transparent; + return value >= 0 ? value : token.value; } - Radius radiiToken(RadiusToken token) { - final radius = _theme.radii[token]?.call(context); - - if (radius != null) return radius; + @override + get props => [space, breakpoints, colors, textStyles, radii]; +} - if (token is RadiusTokenResolver) { - return token.resolve(context); - } +class MixTokenResolver { + final BuildContext _context; + final MixThemeData _theme; - assert( - radius != null, - 'Radii token $token is not defined in Mix Themem or it needs to have a token resolver', - ); + const MixTokenResolver(this._context, this._theme); - return radius ?? Radius.zero; + Color colorToken(ColorToken token) { + return _theme.colors(token, _context); } - TextStyle textStyleToken(TextStyleToken token) { - final style = _theme.textStyles[token]?.call(context); - if (style != null) return style; + Color colorRef(ColorRef ref) => ref.resolve(_context); - if (token is TextStyleTokenResolver) { - return token.resolve(context); - } + Radius radiiToken(RadiusToken token) { + return _theme.radii(token, _context); + } - assert(style != null, 'TextStyle token $token is not defined in Mix Theme'); + Radius radiiRef(RadiusRef ref) => ref.resolve(_context); - return style ?? const TextStyle(); + TextStyle textStyleToken(TextStyleToken token) { + return _theme.textStyles(token, _context); } - double spaceToken(SpaceToken token) { - final space = _theme.space[token]?.call(context); + TextStyle textStyleRef(TextStyleRef ref) => ref.resolve(_context); - if (space != null) return space; + double spaceTokenRef(SpaceRef value) { + if (value >= 0) return value; + final token = _theme.space.findByValue(value); - assert(space != null, 'SpaceToken: $token is not defined in Mix Theme'); - - return space ?? 0; + return token == null ? 0.0 : spaceToken(_context, token); } - double spaceTokenRef(double value) { - if (value == 0 || value > 0) return value; - final token = _theme.space.keys.firstWhereOrNull( - (key) => key() == value, - ); - - assert(token != null, 'SpaceToken: $value is not defined in Mix Theme'); - - return token == null ? value : spaceToken(token); + double spaceToken(BuildContext context, SpaceToken token) { + return _theme.space(token, context); } } diff --git a/lib/src/theme/tokens/breakpoints.dart b/lib/src/theme/tokens/breakpoints.dart index 02123d5e8..9b5b420ce 100644 --- a/lib/src/theme/tokens/breakpoints.dart +++ b/lib/src/theme/tokens/breakpoints.dart @@ -11,31 +11,37 @@ enum BreakpointOrientation { class BreakpointConstraint { final double minWidth; final double maxWidth; - final BreakpointOrientation orientation; const BreakpointConstraint({ this.minWidth = 0, this.maxWidth = double.infinity, - this.orientation = BreakpointOrientation.all, }); bool matches(Size size) { - final matchesWidth = size.width >= minWidth && size.width <= maxWidth; - final matchesOrientation = orientation == BreakpointOrientation.all || - (orientation == BreakpointOrientation.portrait && - size.height > size.width) || - (orientation == BreakpointOrientation.landscape && - size.width > size.height); - - return matchesWidth && matchesOrientation; + return size.width >= minWidth && size.width <= maxWidth; } } class BreakpointToken extends MixToken { - static const xsmall = BreakpointToken('--mix-breakpoint-xsmall'); - static const small = BreakpointToken('--mix-breakpoint-small'); - static const medium = BreakpointToken('--mix-breakpoint-medium'); - static const large = BreakpointToken('--mix-breakpoint-large'); - - const BreakpointToken(super.name); + static const xsmall = BreakpointToken( + 'mix.breakpoint.xsmall', + BreakpointConstraint(maxWidth: 599), + ); + static const small = BreakpointToken( + 'mix.breakpoint.small', + BreakpointConstraint(minWidth: 600, maxWidth: 1023), + ); + static const medium = BreakpointToken( + 'mix.breakpoint.medium', + BreakpointConstraint(minWidth: 1024, maxWidth: 1439), + ); + static const large = BreakpointToken( + 'mix.breakpoint.large', + BreakpointConstraint(minWidth: 1440, maxWidth: double.infinity), + ); + + const BreakpointToken(super.name, super.value); + + @override + BreakpointConstraint call() => value; } diff --git a/lib/src/theme/tokens/color_token.dart b/lib/src/theme/tokens/color_token.dart index 946d88b68..f69b55799 100644 --- a/lib/src/theme/tokens/color_token.dart +++ b/lib/src/theme/tokens/color_token.dart @@ -1,28 +1,33 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'mix_token.dart'; -class ColorToken extends Color implements MixToken { +class ColorToken extends MixToken { + const ColorToken(super.name, super.value); + + const ColorToken.name(String name) : this(name, Colors.transparent); + + factory ColorToken.resolvable(String name, TokenResolver resolver) { + return ColorToken(name, ColorRef(name, resolver)); + } +} + +class ColorRef extends Color with ValueRef { @override - final String name; + final String tokenName; - const ColorToken(this.name) : super(0); + @override + final TokenResolver resolve; + + const ColorRef(this.tokenName, this.resolve) : super(0); @override operator ==(Object other) { if (identical(this, other)) return true; - return other is ColorToken && other.name == name; + return other is ColorRef && other.tokenName == tokenName; } @override - int get hashCode => name.hashCode; -} - -class ColorTokenResolver extends ColorToken with TokenResolver { - @override - final Color Function(BuildContext context) tokenResolver; - - const ColorTokenResolver(super.name, this.tokenResolver); + int get hashCode => tokenName.hashCode; } diff --git a/lib/src/theme/tokens/material_tokens.dart b/lib/src/theme/tokens/material_tokens.dart index d18844c33..69aae7ee3 100644 --- a/lib/src/theme/tokens/material_tokens.dart +++ b/lib/src/theme/tokens/material_tokens.dart @@ -6,194 +6,248 @@ import 'color_token.dart'; import 'text_style_token.dart'; @immutable -class _MaterialDesignColors { - final primary = ColorTokenResolver( - '--md-color-primary', - (context) => Theme.of(context).colorScheme.primary, +class _MaterialColorTokens { + final primary = ColorToken.resolvable( + 'md.color.primary', + (context) => context.color.primary, ); - final secondary = ColorTokenResolver( - '--md-color-secondary', - (context) => Theme.of(context).colorScheme.secondary, + final secondary = ColorToken.resolvable( + 'md.color.secondary', + (context) => context.color.secondary, ); - final tertiary = ColorTokenResolver( - '--md-color-tertiary', - (context) => Theme.of(context).colorScheme.tertiary, + final tertiary = ColorToken.resolvable( + 'md.color.tertiary', + (context) => context.color.tertiary, ); - final surface = ColorTokenResolver( - '--md-color-surface', - (context) => Theme.of(context).colorScheme.surface, + final surface = ColorToken.resolvable( + 'md.color.surface', + (context) => context.color.surface, ); - final background = ColorTokenResolver( - '--md-color-background', - (context) => Theme.of(context).colorScheme.background, + final background = ColorToken.resolvable( + 'md.color.background', + (context) => context.color.background, ); - final error = ColorTokenResolver( - '--md-color-error', - (context) => Theme.of(context).colorScheme.error, + final error = ColorToken.resolvable( + 'md.color.error', + (context) => context.color.error, ); - final onPrimary = ColorTokenResolver( - '--md-color-on-primary', - (context) => Theme.of(context).colorScheme.onPrimary, + final onPrimary = ColorToken.resolvable( + 'md.color.on.primary', + (context) => context.color.onPrimary, ); - final onSecondary = ColorTokenResolver( - '--md-color-on-secondary', - (context) => Theme.of(context).colorScheme.onSecondary, + final onSecondary = ColorToken.resolvable( + 'md.color.on.secondary', + (context) => context.color.onSecondary, ); - final onTertiary = ColorTokenResolver( - '--md-color-on-tertiary', - (context) => Theme.of(context).colorScheme.onTertiary, + final onTertiary = ColorToken.resolvable( + 'md.color.on.tertiary', + (context) => context.color.onTertiary, ); - final onSurface = ColorTokenResolver( - '--md-color-on-surface', - (context) => Theme.of(context).colorScheme.onSurface, + final onSurface = ColorToken.resolvable( + 'md.color.on.surface', + (context) => context.color.onSurface, ); - final onBackground = ColorTokenResolver( - '--md-color-on-background', - (context) => Theme.of(context).colorScheme.onBackground, + final onBackground = ColorToken.resolvable( + 'md.color.on.background', + (context) => context.color.onBackground, ); - final onError = ColorTokenResolver( - '--md-color-on-error', - (context) => Theme.of(context).colorScheme.onError, + final onError = ColorToken.resolvable( + 'md.color.on.error', + (context) => context.color.onError, ); - _MaterialDesignColors(); + _MaterialColorTokens(); } @immutable // Material 3 TextTheme Tokens. class _MaterialTextStyles { // Material 3 text styles - final displayLarge = TextStyleTokenResolver( - '--md3-display-large', - (context) => Theme.of(context).textTheme.displayLarge!, + final displayLarge = TextStyleToken.resolvable( + 'md3.text.theme.display.large', + (context) => context.text.displayLarge!, ); - final displayMedium = TextStyleTokenResolver( - '--md3-display-medium', - (context) => Theme.of(context).textTheme.displayMedium!, + final displayMedium = TextStyleToken.resolvable( + 'md3.text.theme.display.medium', + (context) => context.text.displayMedium!, ); - final displaySmall = TextStyleTokenResolver( - '--md3-display-small', - (context) => Theme.of(context).textTheme.displaySmall!, + final displaySmall = TextStyleToken.resolvable( + 'md3.text.theme.display.small', + (context) => context.text.displaySmall!, ); - final headlineLarge = TextStyleTokenResolver( - '--md3-headline-large', - (context) => Theme.of(context).textTheme.headlineLarge!, + final headlineLarge = TextStyleToken.resolvable( + 'md3.text.theme.headline.large', + (context) => context.text.headlineLarge!, ); - final headlineMedium = TextStyleTokenResolver( - '--md3-headline-medium', - (context) => Theme.of(context).textTheme.headlineMedium!, + final headlineMedium = TextStyleToken.resolvable( + 'md3.text.theme.headline.medium', + (context) => context.text.headlineMedium!, ); - final headlineSmall = TextStyleTokenResolver( - '--md3-headline-small', - (context) => Theme.of(context).textTheme.headlineSmall!, + final headlineSmall = TextStyleToken.resolvable( + 'md3.text.theme.headline.small', + (context) => context.text.headlineSmall!, ); - final titleLarge = TextStyleTokenResolver( - '--md3-title-large', - (context) => Theme.of(context).textTheme.titleLarge!, + final titleLarge = TextStyleToken.resolvable( + 'md3.text.theme.title.large', + (context) => context.text.titleLarge!, ); - final titleMedium = TextStyleTokenResolver( - '--md3-title-medium', - (context) => Theme.of(context).textTheme.titleMedium!, + final titleMedium = TextStyleToken.resolvable( + 'md3.text.theme.title.medium', + (context) => context.text.titleMedium!, ); - final titleSmall = TextStyleTokenResolver( - '--md3-title-small', - (context) => Theme.of(context).textTheme.titleSmall!, + final titleSmall = TextStyleToken.resolvable( + 'md3.text.theme.title.small', + (context) => context.text.titleSmall!, ); - final bodyLarge = TextStyleTokenResolver( - '--md3-body-large', - (context) => Theme.of(context).textTheme.bodyLarge!, + final bodyLarge = TextStyleToken.resolvable( + 'md3.text.theme.body.large', + (context) => context.text.bodyLarge!, ); - final bodyMedium = TextStyleTokenResolver( - '--md3-body-medium', - (context) => Theme.of(context).textTheme.bodyMedium!, + final bodyMedium = TextStyleToken.resolvable( + 'md3.text.theme.body.medium', + (context) => context.text.bodyMedium!, ); - final bodySmall = TextStyleTokenResolver( - '--md3-body-small', - (context) => Theme.of(context).textTheme.bodySmall!, + final bodySmall = TextStyleToken.resolvable( + 'md3.text.theme.body.small', + (context) => context.text.bodySmall!, ); - final labelLarge = TextStyleTokenResolver( - '--md3-label-large', - (context) => Theme.of(context).textTheme.labelLarge!, + final labelLarge = TextStyleToken.resolvable( + 'md3.text.theme.label.large', + (context) => context.text.labelLarge!, ); - final labelMedium = TextStyleTokenResolver( - '--md3-label-medium', - (context) => Theme.of(context).textTheme.labelMedium!, + final labelMedium = TextStyleToken.resolvable( + 'md3.text.theme.label.medium', + (context) => context.text.labelMedium!, ); - final labelSmall = TextStyleTokenResolver( - '--md3-label-small', - (context) => Theme.of(context).textTheme.labelSmall!, + final labelSmall = TextStyleToken.resolvable( + 'md3.text.theme.label.small', + (context) => context.text.labelSmall!, ); // Material 2 text styles - final headline1 = TextStyleTokenResolver( - '--md2-text-style-headline1', - (context) => Theme.of(context).textTheme.headline1!, + final headline1 = TextStyleToken.resolvable( + 'md2.text.theme.headline1', + (context) => context.text.headline1!, ); - final headline2 = TextStyleTokenResolver( - '--md2-text-style-headline2', - (context) => Theme.of(context).textTheme.headline2!, + final headline2 = TextStyleToken.resolvable( + 'md2.text.theme.headline2', + (context) => context.text.headline2!, ); - final headline3 = TextStyleTokenResolver( - '--md2-text-style-headline3', - (context) => Theme.of(context).textTheme.headline3!, + final headline3 = TextStyleToken.resolvable( + 'md2.text.theme.headline3', + (context) => context.text.headline3!, ); - final headline4 = TextStyleTokenResolver( - '--md2-text-style-headline4', - (context) => Theme.of(context).textTheme.headline4!, + final headline4 = TextStyleToken.resolvable( + 'md2.text.theme.headline4', + (context) => context.text.headline4!, ); - final headline5 = TextStyleTokenResolver( - '--md2-text-style-headline5', - (context) => Theme.of(context).textTheme.headline5!, + final headline5 = TextStyleToken.resolvable( + 'md2.text.theme.headline5', + (context) => context.text.headline5!, ); - final headline6 = TextStyleTokenResolver( - '--md2-text-style-headline6', - (context) => Theme.of(context).textTheme.headline6!, + final headline6 = TextStyleToken.resolvable( + 'md2.text.theme.headline6', + (context) => context.text.headline6!, ); - final subtitle1 = TextStyleTokenResolver( - '--md2-text-style-subtitle1', - (context) => Theme.of(context).textTheme.subtitle1!, + final subtitle1 = TextStyleToken.resolvable( + 'md2.text.theme.subtitle1', + (context) => context.text.subtitle1!, ); - final subtitle2 = TextStyleTokenResolver( - '--md2-text-style-subtitle2', - (context) => Theme.of(context).textTheme.subtitle2!, + final subtitle2 = TextStyleToken.resolvable( + 'md2.text.theme.subtitle2', + (context) => context.text.subtitle2!, ); - final bodyText1 = TextStyleTokenResolver( - '--md2-text-style-bodyText1', - (context) => Theme.of(context).textTheme.bodyText1!, + final bodyText1 = TextStyleToken.resolvable( + 'md2.text.theme.bodyText1', + (context) => context.text.bodyText1!, ); - final bodyText2 = TextStyleTokenResolver( - '--md2-text-style-bodyText2', - (context) => Theme.of(context).textTheme.bodyText2!, + final bodyText2 = TextStyleToken.resolvable( + 'md2.text.theme.bodyText2', + (context) => context.text.bodyText2!, ); - final caption = TextStyleTokenResolver( - '--md2-text-style-caption', - (context) => Theme.of(context).textTheme.caption!, + final caption = TextStyleToken.resolvable( + 'md2.text.theme.caption', + (context) => context.text.caption!, ); - final button = TextStyleTokenResolver( - '--md2-text-style-button', - (context) => Theme.of(context).textTheme.button!, + final button = TextStyleToken.resolvable( + 'md2.text.theme.button', + (context) => context.text.button!, ); - final overline = TextStyleTokenResolver( - '--md2-text-style-overline', - (context) => Theme.of(context).textTheme.overline!, + final overline = TextStyleToken.resolvable( + 'md2.text.theme.overline', + (context) => context.text.overline!, ); _MaterialTextStyles(); } +final _colors = _MaterialColorTokens(); +final _textStyles = _MaterialTextStyles(); + @immutable class MaterialTokens { - final colors = _MaterialDesignColors(); - final textStyles = _MaterialTextStyles(); + final colors = _colors; + final textStyles = _textStyles; MaterialTokens(); } + +extension on BuildContext { + TextTheme get text => Theme.of(this).textTheme; + ColorScheme get color => Theme.of(this).colorScheme; +} + +mixin MaterialColorTokensMixin { + Color get primary => _colors.primary(); + Color get secondary => _colors.secondary(); + Color get tertiary => _colors.tertiary(); + Color get surface => _colors.surface(); + Color get background => _colors.background(); + Color get error => _colors.error(); + Color get onPrimary => _colors.onPrimary(); + Color get onSecondary => _colors.onSecondary(); + Color get onTertiary => _colors.onTertiary(); + Color get onSurface => _colors.onSurface(); + Color get onBackground => _colors.onBackground(); + Color get onError => _colors.onError(); +} + +mixin MaterialTextStyleTokensMixin { + TextStyle get displayLarge => _textStyles.displayLarge(); + TextStyle get displayMedium => _textStyles.displayMedium(); + TextStyle get displaySmall => _textStyles.displaySmall(); + TextStyle get headlineLarge => _textStyles.headlineLarge(); + TextStyle get headlineMedium => _textStyles.headlineMedium(); + TextStyle get headlineSmall => _textStyles.headlineSmall(); + TextStyle get titleLarge => _textStyles.titleLarge(); + TextStyle get titleMedium => _textStyles.titleMedium(); + TextStyle get titleSmall => _textStyles.titleSmall(); + TextStyle get bodyLarge => _textStyles.bodyLarge(); + TextStyle get bodyMedium => _textStyles.bodyMedium(); + TextStyle get bodySmall => _textStyles.bodySmall(); + TextStyle get labelLarge => _textStyles.labelLarge(); + TextStyle get labelMedium => _textStyles.labelMedium(); + TextStyle get labelSmall => _textStyles.labelSmall(); + TextStyle get headline1 => _textStyles.headline1(); + TextStyle get headline2 => _textStyles.headline2(); + TextStyle get headline3 => _textStyles.headline3(); + TextStyle get headline4 => _textStyles.headline4(); + TextStyle get headline5 => _textStyles.headline5(); + TextStyle get headline6 => _textStyles.headline6(); + TextStyle get subtitle1 => _textStyles.subtitle1(); + TextStyle get subtitle2 => _textStyles.subtitle2(); + TextStyle get bodyText1 => _textStyles.bodyText1(); + TextStyle get bodyText2 => _textStyles.bodyText2(); + TextStyle get caption => _textStyles.caption(); + TextStyle get button => _textStyles.button(); + TextStyle get overline => _textStyles.overline(); +} diff --git a/lib/src/theme/tokens/mix_token.dart b/lib/src/theme/tokens/mix_token.dart index 835c642c3..d8c8c485f 100644 --- a/lib/src/theme/tokens/mix_token.dart +++ b/lib/src/theme/tokens/mix_token.dart @@ -1,11 +1,32 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import '../../core/extensions/iterable_ext.dart'; +import '../../helpers/compare_mixin.dart'; +import 'color_token.dart'; +import 'radius_token.dart'; +import 'text_style_token.dart'; + @immutable abstract class MixToken { final String name; + final T value; + + const MixToken(this.name, this.value); + + static ColorToken color(String name, Color value) { + return ColorToken(name, value); + } + + static TextStyleToken textStyle(String name, TextStyle value) { + return TextStyleToken(name, value); + } + + static RadiusToken radius(String name, Radius value) { + return RadiusToken(name, value); + } - const MixToken(this.name); + T call() => value; @override operator ==(Object other) { @@ -13,21 +34,42 @@ abstract class MixToken { if (runtimeType != other.runtimeType) return false; - return other is MixToken && other.name == name; + return other is MixToken && other.name == name && other.value == value; } @override - int get hashCode => Object.hash(name, runtimeType); + int get hashCode => Object.hash(name, value, runtimeType); } -mixin TokenResolver on MixToken { - T Function(BuildContext context) get tokenResolver; - T resolve(BuildContext context) => tokenResolver(context); +mixin ValueRef { + String get tokenName; + TokenResolver get resolve; } -mixin TokenValueReference on MixToken { - T call(); -} +typedef TokenResolver = T Function(BuildContext context); + +typedef TokenMap, V> = Map>; -typedef DesignTokenMap - = Map; +class StyledTokens, V> with Comparable { + final Map> _map; + + const StyledTokens(this._map); + + // empty + const StyledTokens.empty() : this(const {}); + + V call(T token, BuildContext context) { + final value = _map[token]?.call(context) ?? token.value; + + return value is ValueRef ? value.resolve(context) : value; + } + + // Looks for the token the value set within the MixToken + // TODO: Needs to be optimized, but this is a temporary solution + T? findByValue(V value) { + return _map.keys.firstWhereOrNull((token) => token.value == value); + } + + @override + get props => [_map]; +} diff --git a/lib/src/theme/tokens/radius_token.dart b/lib/src/theme/tokens/radius_token.dart index 8f1b6ef0a..96ba2dd24 100644 --- a/lib/src/theme/tokens/radius_token.dart +++ b/lib/src/theme/tokens/radius_token.dart @@ -3,43 +3,43 @@ import 'package:flutter/widgets.dart'; import 'mix_token.dart'; -@immutable -class RadiiTokenUtil { - final small = RadiusToken.small; - final medium = RadiusToken.medium; - final large = RadiusToken.large; +const _small = RadiusToken('mix.radii.small', Radius.circular(4)); +const _medium = RadiusToken('mix.radii.medium', Radius.circular(8)); +const _large = RadiusToken('mix.radii.large', Radius.circular(16)); + +class RadiusToken extends MixToken { + static const small = _small; + static const medium = _medium; + static const large = _large; + + const RadiusToken(super.name, super.value); - const RadiiTokenUtil(); + const RadiusToken.name(String name) : this(name, Radius.zero); + + factory RadiusToken.resolvable(String name, TokenResolver resolver) { + return RadiusToken(name, RadiusRef(name, resolver)); + } } @immutable -class RadiusToken extends Radius implements MixToken { - static const small = RadiusToken('--mix-radii-small'); - static const medium = RadiusToken('--mix-radii-medium'); - static const large = RadiusToken('--mix-radii-large'); +class RadiusRef extends Radius with ValueRef { + @override + final String tokenName; @override - final String name; + final TokenResolver resolve; - const RadiusToken(this.name) : super.circular(0); + const RadiusRef(this.tokenName, this.resolve) : super.circular(0); @override operator ==(Object other) { if (identical(this, other)) return true; - return other is RadiusToken && other.name == name; + return other is RadiusRef && other.tokenName == tokenName; } @override - int get hashCode => name.hashCode; -} - -@immutable -class RadiusTokenResolver extends RadiusToken with TokenResolver { - @override - final Radius Function(BuildContext context) tokenResolver; - - const RadiusTokenResolver(super.name, this.tokenResolver); + int get hashCode => tokenName.hashCode; } // // Helper class to wrap functions that can return @@ -57,11 +57,11 @@ class UtilityWithRadiusTokens { return UtilityWithRadiusTokens((Radius value) => fn(value)); } - T get small => call(RadiusToken.small); + T small() => call(RadiusToken.small()); - T get medium => call(RadiusToken.medium); + T medium() => call(RadiusToken.medium()); - T get large => call(RadiusToken.large); + T large() => call(RadiusToken.large()); T call(Radius value) => _fn(value); } diff --git a/lib/src/theme/tokens/space_token.dart b/lib/src/theme/tokens/space_token.dart index 4fc9ca2eb..c806a06b4 100644 --- a/lib/src/theme/tokens/space_token.dart +++ b/lib/src/theme/tokens/space_token.dart @@ -2,18 +2,14 @@ import 'package:flutter/material.dart'; import 'mix_token.dart'; -@immutable -class SpaceTokenUtil { - final xsmall = SpaceToken.xsmall(); - final small = SpaceToken.small(); - final medium = SpaceToken.medium(); - final large = SpaceToken.large(); - final xlarge = SpaceToken.xlarge(); - final xxlarge = SpaceToken.xxlarge(); - SpaceTokenUtil(); -} +typedef SpaceRef = double; -typedef SpaceTokenRef = double; +const _xsmall = SpaceToken('mix.space.xsmall', 4.0); +const _small = SpaceToken('mix.space.small', 8.0); +const _medium = SpaceToken('mix.space.medium', 16.0); +const _large = SpaceToken('mix.space.large', 24.0); +const _xlarge = SpaceToken('mix.space.xlarge', 32.0); +const _xxlarge = SpaceToken('mix.space.xxlarge', 40.0); /// A class representing a space token, which extends `MixToken` class /// and uses the `SizeTokenMixin` mixin. @@ -21,43 +17,20 @@ typedef SpaceTokenRef = double; /// A space token defines a value for controlling the /// size of UI elements. @immutable -class SpaceToken extends MixToken with TokenValueReference { - static const xsmall = SpaceToken('--mix-space-xsmall'); - static const small = SpaceToken('--mix-space-small'); - static const medium = SpaceToken('--mix-space-medium'); - static const large = SpaceToken('--mix-space-large'); - static const xlarge = SpaceToken('--mix-space-xlarge'); - static const xxlarge = SpaceToken('--mix-space-xxlarge'); +class SpaceToken extends MixToken { + static const xsmall = _xsmall; + static const small = _small; + static const medium = _medium; + static const large = _large; + static const xlarge = _xlarge; + static const xxlarge = _xxlarge; /// A constant constructor that accepts a `String` argument named [name]. /// Name needs to be unique per token /// /// [name] is used to initialize the superclass `MixToken`. - const SpaceToken(super.name); + const SpaceToken(super.name, super.value); @override double call() => hashCode * -1.0; } - -// Helper class to wrap functions that can return -// Space tokens in their methods -@immutable -class UtilityWithSpaceTokens { - final T Function(double value) _fn; - - const UtilityWithSpaceTokens(T Function(double value) fn) : _fn = fn; - - T get xsmall => call(SpaceToken.xsmall()); - - T get small => call(SpaceToken.small()); - - T get medium => call(SpaceToken.medium()); - - T get large => call(SpaceToken.large()); - - T get xlarge => call(SpaceToken.xlarge()); - - T get xxlarge => call(SpaceToken.xxlarge()); - - T call(double value) => _fn(value); -} diff --git a/lib/src/theme/tokens/text_style_token.dart b/lib/src/theme/tokens/text_style_token.dart index 37612cbc3..3a86ddcd7 100644 --- a/lib/src/theme/tokens/text_style_token.dart +++ b/lib/src/theme/tokens/text_style_token.dart @@ -1,29 +1,125 @@ +import 'dart:ui'; + import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'mix_token.dart'; -class TextStyleToken extends TextStyle implements MixToken { +class TextStyleToken extends MixToken { + const TextStyleToken(super.name, super.value); + + const TextStyleToken.name(String name) : this(name, const TextStyle()); + + factory TextStyleToken.resolvable( + String name, + TokenResolver resolver, + ) { + return TextStyleToken(name, TextStyleRef(name, resolver)); + } +} + +@immutable +class TextStyleRef extends TextStyle with ValueRef { + @override + final String tokenName; + @override - final String name; + final TokenResolver resolve; - const TextStyleToken(this.name); + const TextStyleRef(this.tokenName, this.resolve); @override operator ==(Object other) { if (identical(this, other)) return true; - return other is TextStyleToken && other.name == name; + return other is TextStyleRef && other.tokenName == tokenName; } @override - int get hashCode => name.hashCode; -} + bool get inherit => throw _e(tokenName, 'inherit'); + + @override + Color get color => throw _e(tokenName, 'color'); + + @override + Color get backgroundColor => throw _e(tokenName, 'backgroundColor'); + + @override + double get fontSize => throw _e(tokenName, 'fontSize'); + + @override + FontWeight get fontWeight => throw _e(tokenName, 'fontWeight'); + + @override + FontStyle get fontStyle => throw _e(tokenName, 'fontStyle'); + + @override + double get letterSpacing => throw _e(tokenName, 'letterSpacing'); + + @override + double get wordSpacing => throw _e(tokenName, 'wordSpacing'); + + @override + TextBaseline get textBaseline => throw _e(tokenName, 'textBaseline'); + + @override + double get height => throw _e(tokenName, 'height'); + + @override + TextLeadingDistribution get leadingDistribution => + throw _e(tokenName, 'leadingDistribution'); -class TextStyleTokenResolver extends TextStyleToken - with TokenResolver { @override - final TextStyle Function(BuildContext context) tokenResolver; + Locale get locale => throw _e(tokenName, 'locale'); - const TextStyleTokenResolver(super.name, this.tokenResolver); + @override + Paint get foreground => throw _e(tokenName, 'foreground'); + + @override + Paint get background => throw _e(tokenName, 'background'); + + @override + List get shadows => throw _e(tokenName, 'shadows'); + + @override + List get fontFeatures => throw _e(tokenName, 'fontFeatures'); + + @override + List get fontVariations => + throw _e(tokenName, 'fontVariations'); + + @override + TextDecoration get decoration => throw _e(tokenName, 'decoration'); + + @override + Color get decorationColor => throw _e(tokenName, 'decorationColor'); + + @override + TextDecorationStyle get decorationStyle => + throw _e(tokenName, 'decorationStyle'); + + @override + double get decorationThickness => throw _e(tokenName, 'decorationThickness'); + + @override + String get debugLabel => throw _e(tokenName, 'debugLabel'); + + @override + int get hashCode => tokenName.hashCode; +} + +TokenFieldAccessError _e(String token, String field) { + return TokenFieldAccessError(token, field); +} + +class TokenFieldAccessError extends Error { + final String tokenName; + final String fieldName; + + TokenFieldAccessError(this.tokenName, this.fieldName); + + @override + String toString() { + return '$tokenName cannot have field $fieldName because it is outside of context'; + } } diff --git a/lib/src/theme/tokens/token_util.dart b/lib/src/theme/tokens/token_util.dart index b1d0cef92..b048946b1 100644 --- a/lib/src/theme/tokens/token_util.dart +++ b/lib/src/theme/tokens/token_util.dart @@ -1,7 +1,38 @@ +import 'package:flutter/material.dart'; + import 'material_tokens.dart'; import 'radius_token.dart'; import 'space_token.dart'; -final $md = MaterialTokens(); -const $radii = RadiiTokenUtil(); -final $space = SpaceTokenUtil(); +final $radii = RadiiTokenUtil(); +const $space = SpaceTokenUtil(); +const $colors = ColorTokenUtil(); +const $textStyles = TextStyleTokenUtil(); + +@immutable +class RadiiTokenUtil { + final small = RadiusToken.small(); + final medium = RadiusToken.medium(); + final large = RadiusToken.large(); + + RadiiTokenUtil(); +} + +@immutable +class SpaceTokenUtil { + const SpaceTokenUtil(); + double xsmall() => SpaceToken.xsmall(); + double small() => SpaceToken.small(); + double medium() => SpaceToken.medium(); + double large() => SpaceToken.large(); + double xlarge() => SpaceToken.xlarge(); + double xxlarge() => SpaceToken.xxlarge(); +} + +class ColorTokenUtil with MaterialColorTokensMixin { + const ColorTokenUtil(); +} + +class TextStyleTokenUtil with MaterialTextStyleTokensMixin { + const TextStyleTokenUtil(); +} diff --git a/lib/src/utils/alignment_util.dart b/lib/src/utils/alignment_util.dart deleted file mode 100644 index 552a6ba79..000000000 --- a/lib/src/utils/alignment_util.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:flutter/material.dart'; - -// Import custom attributes and extension methods -import '../attributes/alignment_attribute.dart'; -import '../helpers/extensions/values_ext.dart'; - -/// Converts an [x] and [y] value into an [AlignmentAttribute] object. -/// -/// The [x] and [y] values are used to create an [Alignment] object. -/// The [Alignment] object is then converted into an [AlignmentAttribute] object. -const alignment = AlignmentAttribute.pos; - -/// Convers a [start] and [y] value into an [AlignmentDirectionalAttribute] object. -const alignmentDirectional = AlignmentDirectionalAttribute.pos; - -// Convenience methods follow below, providing predefined [AlignmentGeometryAttribute]s for common alignments. -// These methods simplify the creation of [AlignmentGeometryAttribute] objects by abstracting away -// the need to directly use the [Alignment] class. - -/// Provides an [AlignmentAttribute] for top-left alignment. -AlignmentAttribute alignmentTopLeft() => Alignment.topLeft.toAttribute(); - -/// Provides an [AlignmentAttribute] for top-center alignment. -AlignmentAttribute alignmentTopCenter() => Alignment.topCenter.toAttribute(); - -/// Provides an [AlignmentAttribute] for top-right alignment. -AlignmentAttribute alignmentTopRight() => Alignment.topRight.toAttribute(); - -/// Provides an [AlignmentAttribute] for center-left alignment. -AlignmentAttribute alignmentCenterLeft() => Alignment.centerLeft.toAttribute(); - -/// Provides an [AlignmentAttribute] for center alignment. -AlignmentAttribute alignmentCenter() => Alignment.center.toAttribute(); - -/// Provides an [AlignmentAttribute] for center-right alignment. -AlignmentAttribute alignmentCenterRight() => - Alignment.centerRight.toAttribute(); - -/// Provides an [AlignmentGeometryAttribute] for bottom-left alignment. -AlignmentAttribute alignmentBottomLeft() => Alignment.bottomLeft.toAttribute(); - -/// Provides an [AlignmentAttribute] for bottom-center alignment. -AlignmentAttribute alignmentBottomCenter() => - Alignment.bottomCenter.toAttribute(); - -/// Provides an [AlignmentAttribute] for bottom-right alignment. -AlignmentAttribute alignmentBottomRight() => - Alignment.bottomRight.toAttribute(); - -/// Provides an [AlignmentGeometryAttribute] for top-start alignment considering text direction. -AlignmentDirectionalAttribute alignmentTopStart() => - AlignmentDirectional.topStart.toAttribute(); - -/// Provides an [AlignmentDirectionalAttribute] for top-end alignment considering text direction. -AlignmentDirectionalAttribute alignmentTopEnd() => - AlignmentDirectional.topEnd.toAttribute(); - -/// Provides an [AlignmentDirectionalAttribute] for center-start alignment considering text direction. -AlignmentDirectionalAttribute alignmentCenterStart() => - AlignmentDirectional.centerStart.toAttribute(); - -/// Provides an [AlignmentDirectionalAttribute] for center-end alignment considering text direction. -AlignmentDirectionalAttribute alignmentCenterEnd() => - AlignmentDirectional.centerEnd.toAttribute(); - -/// Provides an [AlignmentDirectionalAttribute] for bottom-start alignment considering text direction. -AlignmentDirectionalAttribute alignmentBottomStart() => - AlignmentDirectional.bottomStart.toAttribute(); - -/// Provides an [AlignmentDirectionalAttribute] for bottom-end alignment considering text direction. -AlignmentDirectionalAttribute alignmentBottomEnd() => - AlignmentDirectional.bottomEnd.toAttribute(); diff --git a/lib/src/utils/border_radius_util.dart b/lib/src/utils/border_radius_util.dart deleted file mode 100644 index 3bec774b6..000000000 --- a/lib/src/utils/border_radius_util.dart +++ /dev/null @@ -1,101 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../attributes/border/border_radius_attribute.dart'; - -// Provides an utility for creating a uniform BorderRadiusAttribute for all corners. -const borderRadius = BorderRadiusAttribute.positional; - -// Border Radius Directional -// Provides a utility BorderRadiusDirectionalAttribute for all corners, considering text direction. -const borderRadiusDirectional = BorderRadiusDirectionalAttribute.positional; - -// Provides an utility for creating a vertical BorderRadiusAttribute with independent radii for top and bottom sides. -const borderRadiusVertical = BorderRadiusAttribute.vertical; - -// Provides an utility for creating a horizontal BorderRadiusAttribute with independent radii for left and right sides. -const borderRadiusHorizontal = BorderRadiusAttribute.horizontal; - -// Provides an utility for creating a BorderRadiusAttribute with zero radius, resulting in square corners. -const borderRadiusOnly = BorderRadiusAttribute.new; - -const borderRadiusDirectionalOnly = BorderRadiusDirectionalAttribute.new; - -// Alias for creating a uniform circular BorderRadiusDirectionalAttribute. -BorderRadiusAttribute rounded(double p1, [double? p2, double? p3, double? p4]) { - final r1 = Radius.circular(p1); - Radius? r2 = p2 == null ? null : Radius.circular(p2); - Radius? r3 = p3 == null ? null : Radius.circular(p3); - Radius? r4 = p4 == null ? null : Radius.circular(p4); - - return BorderRadiusAttribute.positional(r1, r2, r3, r4); -} - -BorderRadiusDirectionalAttribute roundedDirectional( - double p1, [ - double? p2, - double? p3, - double? p4, -]) { - final r1 = Radius.circular(p1); - Radius? r2 = p2 == null ? null : Radius.circular(p2); - Radius? r3 = p3 == null ? null : Radius.circular(p3); - Radius? r4 = p4 == null ? null : Radius.circular(p4); - - return BorderRadiusDirectionalAttribute.positional(r1, r2, r3, r4); -} - -// Corner-specific BorderRadiusAttributes -// Creates a BorderRadiusAttribute with a circular radius applied to the top-left corner. -BorderRadiusAttribute roundedTopLeft(double radius) { - return BorderRadiusAttribute(topLeft: Radius.circular(radius)); -} - -// Creates a BorderRadiusAttribute with a circular radius applied to the top-right corner. -BorderRadiusAttribute roundedTopRight(double radius) { - return BorderRadiusAttribute(topRight: Radius.circular(radius)); -} - -// Creates a BorderRadiusAttribute with a circular radius applied to the bottom-left corner. -BorderRadiusAttribute roundedBottomLeft(double radius) { - return BorderRadiusAttribute(bottomLeft: Radius.circular(radius)); -} - -// Creates a BorderRadiusAttribute with a circular radius applied to the bottom-right corner. -BorderRadiusAttribute roundedBottomRight(double radius) { - return BorderRadiusAttribute(bottomRight: Radius.circular(radius)); -} - -// Corner-specific BorderRadiusDirectionalAttributes -// Creates a BorderRadiusDirectionalAttribute with a circular radius applied to the top-start corner, considering text direction. -BorderRadiusDirectionalAttribute roundedTopStart(double radius) { - return BorderRadiusDirectionalAttribute(topStart: Radius.circular(radius)); -} - -// Creates a BorderRadiusDirectionalAttribute with a circular radius applied to the top-end corner, considering text direction. -BorderRadiusDirectionalAttribute roundedTopEnd(double radius) { - return BorderRadiusDirectionalAttribute(topEnd: Radius.circular(radius)); -} - -BorderRadiusAttribute roundedHorizontal({double? left, double? right}) { - return BorderRadiusAttribute.horizontal( - left: left == null ? null : Radius.circular(left), - right: right == null ? null : Radius.circular(right), - ); -} - -BorderRadiusAttribute roundedVertical({double? top, double? bottom}) { - return BorderRadiusAttribute.vertical( - top: top == null ? null : Radius.circular(top), - bottom: bottom == null ? null : Radius.circular(bottom), - ); -} - -// Creates a BorderRadiusDirectionalAttribute with a circular radius applied to the bottom-start corner, considering text direction. -BorderRadiusDirectionalAttribute roundedBottomStart(double radius) { - return BorderRadiusDirectionalAttribute(bottomStart: Radius.circular(radius)); -} - -// Creates a BorderRadiusDirectionalAttribute with a circular radius applied to the bottom-end corner, considering text direction. -BorderRadiusDirectionalAttribute roundedBottomEnd(double radius) { - return BorderRadiusDirectionalAttribute(bottomEnd: Radius.circular(radius)); -} diff --git a/lib/src/utils/border_util.dart b/lib/src/utils/border_util.dart deleted file mode 100644 index fbc5687d7..000000000 --- a/lib/src/utils/border_util.dart +++ /dev/null @@ -1,199 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../attributes/border/border_attribute.dart'; -import '../helpers/extensions/values_ext.dart'; - -BorderAttribute border({ - Color? color, - double? width, - BorderStyle? style, - double? strokeAlign, -}) { - final side = borderSide( - color: color, - width: width, - style: style, - strokeAlign: strokeAlign, - ); - - return BorderAttribute(left: side, right: side, top: side, bottom: side); -} - -const borderOnly = BorderAttribute.new; - -BorderDirectionalAttribute borderDirectional({ - Color? color, - double? width, - BorderStyle? style, - double? strokeAlign, -}) { - final side = borderSide( - color: color, - width: width, - style: style, - strokeAlign: strokeAlign, - ); - - return BorderDirectionalAttribute( - start: side, - end: side, - top: side, - bottom: side, - ); -} - -const borderDirectionalOnly = BorderDirectionalAttribute.new; - -BorderAttribute borderTop({ - Color? color, - double? width, - BorderStyle? style, - double? strokeAlign, -}) { - return BorderAttribute( - top: borderSide( - color: color, - width: width, - style: style, - strokeAlign: strokeAlign, - ), - ); -} - -BorderAttribute borderBottom({ - Color? color, - double? width, - BorderStyle? style, - double? strokeAlign, -}) { - return BorderAttribute( - bottom: borderSide( - color: color, - width: width, - style: style, - strokeAlign: strokeAlign, - ), - ); -} - -BorderAttribute borderLeft({ - Color? color, - double? width, - BorderStyle? style, - double? strokeAlign, -}) { - return BorderAttribute( - left: borderSide( - color: color, - width: width, - style: style, - strokeAlign: strokeAlign, - ), - ); -} - -BorderAttribute borderRight({ - Color? color, - double? width, - BorderStyle? style, - double? strokeAlign, -}) { - return BorderAttribute( - right: borderSide( - color: color, - width: width, - style: style, - strokeAlign: strokeAlign, - ), - ); -} - -BorderDirectionalAttribute borderStart({ - Color? color, - double? width, - BorderStyle? style, - double? strokeAlign, -}) { - return BorderDirectionalAttribute( - start: borderSide( - color: color, - width: width, - style: style, - strokeAlign: strokeAlign, - ), - ); -} - -BorderDirectionalAttribute borderEnd({ - Color? color, - double? width, - BorderStyle? style, - double? strokeAlign, -}) { - return BorderDirectionalAttribute( - end: borderSide( - color: color, - width: width, - style: style, - strokeAlign: strokeAlign, - ), - ); -} - -BorderAttribute borderHorizontal({ - Color? color, - double? width, - BorderStyle? style, - double? strokeAlign, -}) { - return _borderSymetric( - horizontal: borderSide( - color: color, - width: width, - style: style, - strokeAlign: strokeAlign, - ), - ); -} - -BorderAttribute borderVertical({ - Color? color, - double? width, - BorderStyle? style, - double? strokeAlign, -}) { - return _borderSymetric( - vertical: borderSide( - color: color, - width: width, - style: style, - strokeAlign: strokeAlign, - ), - ); -} - -BorderAttribute _borderSymetric({ - BorderSideAttribute? vertical, - BorderSideAttribute? horizontal, -}) { - return BorderAttribute( - left: vertical, - right: vertical, - top: horizontal, - bottom: horizontal, - ); -} - -BorderSideAttribute borderSide({ - Color? color, - double? width, - BorderStyle? style, - double? strokeAlign, -}) { - return BorderSideAttribute( - color: color?.toAttribute(), - strokeAlign: strokeAlign, - style: style, - width: width, - ); -} diff --git a/lib/src/utils/box_constraints_util.dart b/lib/src/utils/box_constraints_util.dart deleted file mode 100644 index 61307a448..000000000 --- a/lib/src/utils/box_constraints_util.dart +++ /dev/null @@ -1,21 +0,0 @@ -import '../attributes/constraints_attribute.dart'; - -const boxConstraints = BoxConstraintsAttribute.new; - -BoxConstraintsAttribute width(double width) => - BoxConstraintsAttribute(width: width); - -BoxConstraintsAttribute height(double height) => - BoxConstraintsAttribute(height: height); - -BoxConstraintsAttribute minWidth(double width) => - BoxConstraintsAttribute(minWidth: width); - -BoxConstraintsAttribute maxWidth(double width) => - BoxConstraintsAttribute(maxWidth: width); - -BoxConstraintsAttribute minHeight(double height) => - BoxConstraintsAttribute(minHeight: height); - -BoxConstraintsAttribute maxHeight(double height) => - BoxConstraintsAttribute(maxHeight: height); diff --git a/lib/src/utils/context_variant_util.dart b/lib/src/utils/context_variant_util.dart deleted file mode 100644 index 93ab45347..000000000 --- a/lib/src/utils/context_variant_util.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../helpers/extensions/string_ext.dart'; -import '../theme/mix_theme.dart'; -import '../theme/tokens/breakpoints.dart'; -import '../variants/context_variant.dart'; - -// Breakpoint context variants -final onSmall = _screenSizeVariant(BreakpointToken.small); - -final onXSmall = _screenSizeVariant(BreakpointToken.xsmall); - -final onMedium = _screenSizeVariant(BreakpointToken.medium); - -final onLarge = _screenSizeVariant(BreakpointToken.large); - -ContextVariant onBreakpoint({ - minWidth = 0, - maxWidth = double.infinity, - orientation = BreakpointOrientation.all, -}) { - final constraints = BreakpointConstraint( - minWidth: minWidth, - maxWidth: maxWidth, - orientation: orientation, - ); - final constraintName = - 'minWidth-${constraints.minWidth}-maxWidth-${constraints.maxWidth}-orientation-${constraints.orientation}'; - - return ContextVariant( - 'on-$constraintName', - when: (BuildContext context) { - final size = MediaQuery.sizeOf(context); - - return constraints.matches(size); - }, - ); -} - -// Brighness context variants -final onDark = _brightnessVariant(Brightness.dark); - -final onLight = _brightnessVariant(Brightness.light); - -// Directionality context variants -final onRTL = _directionalityVariant(TextDirection.rtl); -final onLTR = _directionalityVariant(TextDirection.ltr); - -// Orientation context variants - -final onPortrait = _orientationVariant(Orientation.portrait); - -final onLandscape = _orientationVariant(Orientation.landscape); - -ContextVariant _directionalityVariant(TextDirection direction) { - return ContextVariant( - 'on-${direction.name.paramCase}', - when: (BuildContext context) => Directionality.of(context) == direction, - ); -} - -ContextVariant _brightnessVariant(Brightness brightness) { - return ContextVariant( - 'on-${brightness.name.paramCase}', - when: (BuildContext context) { - return Theme.of(context).brightness == brightness; - }, - ); -} - -ContextVariant _screenSizeVariant(BreakpointToken screenSize) { - return ContextVariant( - 'on-${screenSize.name.paramCase}', - when: (BuildContext context) { - final breakpoints = MixTheme.of(context).breakpoints; - - final size = MediaQuery.sizeOf(context); - - final selectedbreakpoint = breakpoints[screenSize]; - - assert( - selectedbreakpoint != null, - 'Breakpoint ${screenSize.name} is not defined in the theme', - ); - - return selectedbreakpoint?.call(context).matches(size) ?? false; - }, - ); -} - -ContextVariant onNot(ContextVariant variant) { - return ContextVariant( - 'not(${variant.name})', - when: (BuildContext context) => !variant.when(context), - ); -} - -ContextVariant _orientationVariant(Orientation orientation) { - return ContextVariant( - 'on-${orientation.name.paramCase}', - when: (BuildContext context) => - MediaQuery.orientationOf(context) == orientation, - ); -} diff --git a/lib/src/utils/context_variant_util/on_breakpoint_util.dart b/lib/src/utils/context_variant_util/on_breakpoint_util.dart new file mode 100644 index 000000000..a6e3cc2ed --- /dev/null +++ b/lib/src/utils/context_variant_util/on_breakpoint_util.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; + +import '../../helpers/string_ext.dart'; +import '../../theme/mix_theme.dart'; +import '../../theme/tokens/breakpoints.dart'; +import '../../variants/context_variant.dart'; + +/// Global breakpoint context variants based on predefined screen sizes. +/// These can be used to apply styles or layouts conditionally depending on the screen size. + +/// Variant for small screens. +final onSmall = onBreakpointToken(BreakpointToken.small); + +/// Variant for extra small screens. +final onXSmall = onBreakpointToken(BreakpointToken.xsmall); + +/// Variant for medium screens. +final onMedium = onBreakpointToken(BreakpointToken.medium); + +/// Variant for large screens. +final onLarge = onBreakpointToken(BreakpointToken.large); + +/// Creates a [ContextVariant] for custom breakpoint constraints. +/// +/// [minWidth] and [maxWidth] define the width constraints, while [orientation] specifies +/// the orientation constraint. This function returns a [ContextVariant] which will apply +/// when the screen size matches these constraints. +ContextVariant onBreakpoint({minWidth = 0, maxWidth = double.infinity}) { + final constraints = BreakpointConstraint( + minWidth: minWidth, + maxWidth: maxWidth, + ); + final constraintName = + 'minWidth-${constraints.minWidth}-maxWidth-${constraints.maxWidth}'; + + return ContextVariant( + 'on-$constraintName', + when: (BuildContext context) { + final size = MediaQuery.sizeOf(context); + + return constraints.matches(size); + }, + ); +} + +/// Creates a [ContextVariant] based on a specific [token]. +/// +/// This function uses the [token] to retrieve breakpoint settings from [MixTheme], +/// and returns a [ContextVariant] that applies when the current screen size matches +/// the specified breakpoint. +ContextVariant onBreakpointToken(BreakpointToken token) { + return ContextVariant( + 'on-${token.name.paramCase}', + when: (BuildContext context) { + final breakpoints = MixTheme.of(context).breakpoints; + + final size = MediaQuery.sizeOf(context); + + final selectedbreakpoint = breakpoints(token, context); + + return selectedbreakpoint.matches(size); + }, + ); +} diff --git a/lib/src/utils/context_variant_util/on_brightness_util.dart b/lib/src/utils/context_variant_util/on_brightness_util.dart new file mode 100644 index 000000000..b34e3cb9f --- /dev/null +++ b/lib/src/utils/context_variant_util/on_brightness_util.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + +import '../../helpers/string_ext.dart'; +import '../../variants/context_variant.dart'; + +/// Global brightness context variants. +/// These variants are used to apply styles or behaviors conditionally based on the theme's brightness setting. + +/// Variant for dark mode. +final onDark = onBrightness(Brightness.dark); + +/// Variant for light mode. +final onLight = onBrightness(Brightness.light); + +/// Creates a [ContextVariant] based on the specified [brightness]. +/// +/// This function returns a [ContextVariant] that applies when the current theme's +/// brightness matches the specified [brightness]. It is useful for defining +/// brightness-specific styles or behaviors in the application. +ContextVariant onBrightness(Brightness brightness) { + return ContextVariant( + 'on-${brightness.name.paramCase}', + when: (BuildContext context) { + return Theme.of(context).brightness == brightness; + }, + ); +} diff --git a/lib/src/utils/context_variant_util/on_directionality_util.dart b/lib/src/utils/context_variant_util/on_directionality_util.dart new file mode 100644 index 000000000..7b54ed51d --- /dev/null +++ b/lib/src/utils/context_variant_util/on_directionality_util.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +import '../../helpers/string_ext.dart'; +import '../../variants/context_variant.dart'; + +/// Variant for Right-To-Left (RTL) text direction. +final onRTL = onDirectionality(TextDirection.rtl); + +/// Variant for Left-To-Right (LTR) text direction. +final onLTR = onDirectionality(TextDirection.ltr); + +/// Creates a [ContextVariant] for a specific [direction] of text. +/// +/// This function returns a [ContextVariant] that applies when the current +/// text directionality matches the specified [direction]. It is particularly useful +/// for defining directionality-specific styles or behaviors in the application. +/// +/// [direction] - The text direction (RTL or LTR) for which the variant is to be created. +ContextVariant onDirectionality(TextDirection direction) { + return ContextVariant( + 'on-${direction.name.paramCase}', + when: (BuildContext context) => Directionality.of(context) == direction, + ); +} diff --git a/lib/src/utils/context_variant_util/on_helper_util.dart b/lib/src/utils/context_variant_util/on_helper_util.dart new file mode 100644 index 000000000..956898953 --- /dev/null +++ b/lib/src/utils/context_variant_util/on_helper_util.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +import '../../variants/context_variant.dart'; + +/// Creates a [ContextVariant] that negates the condition of another [ContextVariant]. +/// +/// This function returns a new [ContextVariant] that applies when the specified +/// [variant]'s condition does not hold true. It is useful for defining styles or +/// behaviors that should apply in the opposite condition of the provided variant. +/// +/// [variant] - The [ContextVariant] whose condition is to be negated. +ContextVariant onNot(ContextVariant variant) { + return ContextVariant( + 'not(${variant.name})', + when: (BuildContext context) => !variant.when(context), + ); +} diff --git a/lib/src/utils/context_variant_util/on_orientation_util.dart b/lib/src/utils/context_variant_util/on_orientation_util.dart new file mode 100644 index 000000000..ef0d5a628 --- /dev/null +++ b/lib/src/utils/context_variant_util/on_orientation_util.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +import '../../helpers/string_ext.dart'; +import '../../variants/context_variant.dart'; + +/// Variant for portrait orientation. +/// +/// This global variant is used to apply styles or behaviors when the device is in portrait orientation. +final onPortrait = onOrientation(Orientation.portrait); + +/// Variant for landscape orientation. +/// +/// This global variant is used to apply styles or behaviors when the device is in landscape orientation. +final onLandscape = onOrientation(Orientation.landscape); + +/// Creates a [ContextVariant] for a specific [orientation]. +/// +/// This function returns a [ContextVariant] that applies when the current +/// orientation of the device matches the specified [orientation]. It is useful +/// for defining orientation-specific styles or behaviors in the application. +/// +/// [orientation] - The device orientation (portrait or landscape) for which the variant is to be created. +ContextVariant onOrientation(Orientation orientation) { + return ContextVariant( + 'on-${orientation.name.paramCase}', + when: (BuildContext context) => + MediaQuery.orientationOf(context) == orientation, + ); +} diff --git a/lib/src/utils/decoration_util.dart b/lib/src/utils/decoration_util.dart deleted file mode 100644 index 409ede40b..000000000 --- a/lib/src/utils/decoration_util.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../attributes/decoration_attribute.dart'; -import '../helpers/extensions/values_ext.dart'; - -BoxDecorationAttribute backgroundColor(Color color) { - return BoxDecorationAttribute(color: color.toAttribute()); -} - -const boxDecoration = BoxDecorationAttribute.new; diff --git a/lib/src/utils/decorators_util.dart b/lib/src/utils/decorators_util.dart index a07ec07c5..ae364c843 100644 --- a/lib/src/utils/decorators_util.dart +++ b/lib/src/utils/decorators_util.dart @@ -1,44 +1,57 @@ import 'package:flutter/material.dart'; +import '../attributes/scalars/scalar_util.dart'; import '../decorators/clip_decorator.dart'; import '../decorators/default_decorators.dart'; const aspectRatio = AspectRatioDecorator.new; -const expanded = FlexibleDecorator.tight; -const flexible = FlexibleDecorator.loose; - const opacity = OpacityDecorator.new; +const flexible = FlexibleDecoratorUtility(); -const rotate = RotateDecorator.new; - -RotateDecorator rotate90() => rotate(1); -RotateDecorator rotate180() => rotate(2); -RotateDecorator rotate270() => rotate(3); +const rotate = RotateUtility(); const scale = ScaleDecorator.new; -const clipRounded = clipRRect; -const clipOval = ClipOvalDecorator.new; const clipPath = ClipPathDecorator.new; +const clipOval = ClipOvalDecorator.new; +const clipRect = ClipRectDecorator.new; +const clipRRect = ClipRRectDecorator.new; +const visibility = VisibilityUtility(); +final hidden = visibility.off; +final visible = visibility.on; -ClipRRectDecorator clipRRect( - double radius, { - CustomClipper? clipper, - Clip? clipBehavior, -}) { - return ClipRRectDecorator( - clipper: clipper, +ClipPathDecorator clipTriangle({Clip? clipBehavior}) { + return ClipPathDecorator( clipBehavior: clipBehavior, - borderRadius: BorderRadius.circular(radius), + clipper: const TriangleClipper(), ); } -// ClipDecorator clipOval() => const ClipDecorator(ClipDecoratorType.oval); +class FlexibleDecoratorUtility { + const FlexibleDecoratorUtility(); -ClipDecorator clipTriangle({Clip? clipBehavior}) { - return ClipPathDecorator( - clipper: const TriangleClipper(), - clipBehavior: clipBehavior, - ); + // FlexibleDecorator flexFit(FlexFit fit) => call(fit: fit); + // FlexibleDecorator flex(int flex) => call(flex: flex); + + FlexibleDecorator tight([int? flex]) => call(flex: flex, fit: FlexFit.tight); + FlexibleDecorator loose([int? flex]) => call(flex: flex, fit: FlexFit.loose); + FlexibleDecorator expanded([int? flex]) => tight(flex); + + FlexibleDecorator call({int? flex, FlexFit? fit, Key? key}) { + return FlexibleDecorator(flex: flex, fit: fit, key: key); + } +} + +class RotateUtility extends MixUtility { + const RotateUtility() : super(RotateDecorator.new); + RotateDecorator get d90 => builder(1); + RotateDecorator get d180 => builder(2); + RotateDecorator get d270 => builder(3); + + RotateDecorator call(int value) => builder(value); +} + +class VisibilityUtility extends BoolUtility { + const VisibilityUtility() : super(VisibilityDecorator.new); } diff --git a/lib/src/utils/gradient_util.dart b/lib/src/utils/gradient_util.dart deleted file mode 100644 index 662030959..000000000 --- a/lib/src/utils/gradient_util.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../attributes/scalar_attribute.dart'; - -GradientAttribute linearGradient({ - AlignmentGeometry? begin, - AlignmentGeometry? end, - required List colors, - List? stops, - TileMode? tileMode, - GradientTransform? transform, -}) { - final defaultGradient = LinearGradient(colors: colors); - - return GradientAttribute( - LinearGradient( - begin: begin ?? defaultGradient.begin, - end: end ?? defaultGradient.end, - colors: colors, - stops: stops, - tileMode: tileMode ?? defaultGradient.tileMode, - transform: transform, - ), - ); -} - -GradientAttribute radialGradient({ - AlignmentGeometry? center, - double? radius, - required List colors, - List? stops, - TileMode tileMode = TileMode.clamp, - AlignmentGeometry? focal, - double? focalRadius, - GradientTransform? transform, -}) { - final defaultGradient = RadialGradient(colors: colors); - - return GradientAttribute( - RadialGradient( - center: center ?? defaultGradient.center, - radius: radius ?? defaultGradient.radius, - colors: colors, - stops: stops, - tileMode: tileMode, - focal: focal, - focalRadius: focalRadius ?? defaultGradient.focalRadius, - transform: transform, - ), - ); -} diff --git a/lib/src/utils/pressable_util.dart b/lib/src/utils/pressable_util.dart index ff819c292..3d0732841 100644 --- a/lib/src/utils/pressable_util.dart +++ b/lib/src/utils/pressable_util.dart @@ -1,15 +1,15 @@ import 'package:flutter/material.dart'; -import '../helpers/extensions/string_ext.dart'; +import '../helpers/string_ext.dart'; import '../variants/context_variant.dart'; import '../widgets/pressable/pressable.notifier.dart'; import '../widgets/pressable/pressable_state.dart'; -import 'context_variant_util.dart'; +import 'context_variant_util/on_helper_util.dart'; -final onPress = _pressableVariant(PressableState.pressed); -final onLongPress = _pressableVariant(PressableState.longPressed); -final onHover = _pressableVariant(PressableState.hover); -final onDisabled = _pressableVariant(PressableState.disabled); +final onPress = _onState(PressableState.pressed); +final onLongPress = _onState(PressableState.longPressed); +final onHover = _onState(PressableState.hover); +final onDisabled = _onState(PressableState.disabled); final onEnabled = onNot(onDisabled); @@ -22,7 +22,7 @@ final onFocus = ContextVariant( }, ); -ContextVariant _pressableVariant(PressableState state) { +ContextVariant _onState(PressableState state) { return ContextVariant( 'on-${state.name.paramCase}', when: (BuildContext context) { diff --git a/lib/src/utils/scalar_util.dart b/lib/src/utils/scalar_util.dart deleted file mode 100644 index b3c880f22..000000000 --- a/lib/src/utils/scalar_util.dart +++ /dev/null @@ -1,55 +0,0 @@ -import '../attributes/color_attribute.dart'; -import '../attributes/scalar_attribute.dart'; - -const visible = VisibleAttribute.new; - -const stackFit = StackFitAttribute.new; - -const clip = ClipAttribute.new; - -const transform = TransformAttribute.new; - -const verticalDirection = VerticalDirectionAttribute.new; - -const textDirection = TextDirectionAttribute.new; - -const softWrap = SoftWrapAttribute.new; - -const textOverflow = TextOverflowAttribute.new; - -const textScaleFactor = TextScaleFactorAttribute.new; - -const maxLines = MaxLinesAttribute.new; - -const textWidthBasis = TextWidthBasisAttribute.new; - -const textAlign = TextAlignAttribute.new; - -const flexFit = FlexFitAttribute.new; - -const axisDirection = AxisAttribute.new; - -const mainAxisAlignment = MainAxisAlignmentAttribute.new; - -const crossAxisAlignment = CrossAxisAlignmentAttribute.new; - -const mainAxisSize = MainAxisSizeAttribute.new; - -const iconSize = IconSizeAttribute.new; -const iconColor = IconColorAttribute.new; - -const gradient = GradientAttribute.new; -const transformAlignment = TransformAlignmentAttribute.new; -const textBaseline = TextBaselineAttribute.new; -const imageAlignment = ImageAlignmentAttribute.new; -const imageScale = ImageScaleAttribute.new; -const imageFit = ImageFitAttribute.new; -const imageRepeat = ImageRepeatAttribute.new; - -const imageWidth = ImageWidthAttribute.new; -const imageHeight = ImageHeightAttribute.new; -const textHeightBehavior = TextHeightBehaviorAttribute.new; -const boxFit = BoxFitAttribute.new; -const blendMode = BlendModeAttribute.new; -const boxShape = BoxShapeAttribute.new; -const imageColor = ImageColorAttribute.new; diff --git a/lib/src/utils/space_util.dart b/lib/src/utils/space_util.dart deleted file mode 100644 index 17873e597..000000000 --- a/lib/src/utils/space_util.dart +++ /dev/null @@ -1,156 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../attributes/space_attribute.dart'; -import '../theme/tokens/space_token.dart'; - -const _paddingUtil = SpaceUtilityFactory(PaddingAttribute.new); -const _paddingDirectionalUtil = - SpaceDirectionalUtilityFactory(PaddingDirectionalAttribute.new); - -final padding = _paddingUtil.shorthand; -final paddingDirectional = _paddingDirectionalUtil.shorthand; -final paddingOnly = _paddingUtil.only; -final paddingDirectionalOnly = _paddingDirectionalUtil.only; -final paddingAll = UtilityWithSpaceTokens(_paddingUtil.all); -final paddingTop = UtilityWithSpaceTokens(_paddingUtil.top); -final paddingBottom = UtilityWithSpaceTokens(_paddingUtil.bottom); -final paddingLeft = UtilityWithSpaceTokens(_paddingUtil.left); -final paddingRight = UtilityWithSpaceTokens(_paddingUtil.right); -final paddingStart = UtilityWithSpaceTokens(_paddingDirectionalUtil.start); -final paddingEnd = UtilityWithSpaceTokens(_paddingDirectionalUtil.end); -final paddingHorizontal = UtilityWithSpaceTokens(_paddingUtil.horizontal); -final paddingVertical = UtilityWithSpaceTokens(_paddingUtil.vertical); - -PaddingGeometryAttribute paddingFrom(EdgeInsetsGeometry edgeInsets) { - if (edgeInsets is EdgeInsets) { - return _paddingUtil.from(edgeInsets); - } else if (edgeInsets is EdgeInsetsDirectional) { - return _paddingDirectionalUtil.from(edgeInsets); - } - throw UnimplementedError( - 'paddingFrom is not implemented for ${edgeInsets.runtimeType}', - ); -} - -const _marginUtil = SpaceUtilityFactory(MarginAttribute.new); -const _marginDirectionalUtil = - SpaceDirectionalUtilityFactory(MarginDirectionalAttribute.new); - -final margin = _marginUtil.shorthand; -final marginDirectional = _marginDirectionalUtil.shorthand; -final marginOnly = _marginUtil.only; -final marginDirectionalOnly = _marginDirectionalUtil.only; -final marginAll = UtilityWithSpaceTokens(_marginUtil.all); -final marginTop = UtilityWithSpaceTokens(_marginUtil.top); -final marginBottom = UtilityWithSpaceTokens(_marginUtil.bottom); -final marginLeft = UtilityWithSpaceTokens(_marginUtil.left); -final marginRight = UtilityWithSpaceTokens(_marginUtil.right); -final marginStart = UtilityWithSpaceTokens(_marginDirectionalUtil.start); -final marginEnd = UtilityWithSpaceTokens(_marginDirectionalUtil.end); -final marginHorizontal = UtilityWithSpaceTokens(_marginUtil.horizontal); -final marginVertical = UtilityWithSpaceTokens(_marginUtil.vertical); - -MarginGeometryAttribute marginFrom(EdgeInsetsGeometry edgeInsets) { - if (edgeInsets is EdgeInsets) { - return _marginUtil.from(edgeInsets); - } else if (edgeInsets is EdgeInsetsDirectional) { - return _marginDirectionalUtil.from(edgeInsets); - } - throw UnimplementedError( - 'marginFrom is not implemented for ${edgeInsets.runtimeType}', - ); -} - -typedef SpaceAttributeBuilder = T Function({ - double? top, - double? bottom, - double? left, - double? right, -}); - -@immutable -class SpaceUtilityFactory { - final SpaceAttributeBuilder _builder; - - const SpaceUtilityFactory(this._builder); - - SpaceAttributeBuilder get only => _builder; - - T all(double all) => _builder(bottom: all, left: all, right: all, top: all); - - T top(double value) => _builder(top: value); - T bottom(double value) => _builder(bottom: value); - T left(double value) => _builder(left: value); - T right(double value) => _builder(right: value); - - T horizontal(double value) => _builder(left: value, right: value); - T vertical(double value) => _builder(bottom: value, top: value); - - T from(EdgeInsets edgeInsets) { - return _builder( - bottom: edgeInsets.bottom, - left: edgeInsets.left, - right: edgeInsets.right, - top: edgeInsets.top, - ); - } - - T shorthand(double p1, [double? p2, double? p3, double? p4]) { - double top = p1; - double bottom = p1; - double left = p1; - double right = p1; - - if (p2 != null) { - left = p2; - right = p2; - } - - if (p3 != null) bottom = p3; - if (p4 != null) left = p4; - - return _builder(bottom: bottom, left: left, right: right, top: top); - } -} - -typedef SpaceDirectionalBuilder = T - Function({double? top, double? bottom, double? start, double? end}); - -@immutable -class SpaceDirectionalUtilityFactory { - final SpaceDirectionalBuilder _builder; - - const SpaceDirectionalUtilityFactory(this._builder); - - SpaceDirectionalBuilder get only => _builder; - - T start(double value) => _builder(start: value); - T end(double value) => _builder(end: value); - - T from(EdgeInsetsDirectional edgeInsets) { - return _builder( - bottom: edgeInsets.bottom, - end: edgeInsets.end, - start: edgeInsets.start, - top: edgeInsets.top, - ); - } - - T shorthand(double p1, [double? p2, double? p3, double? p4]) { - double top = p1; - double bottom = p1; - double start = p1; - double end = p1; - - if (p2 != null) { - start = p2; - end = p2; - } - - if (p3 != null) bottom = p3; - - if (p4 != null) start = p4; - - return _builder(bottom: bottom, end: end, start: start, top: top); - } -} diff --git a/lib/src/utils/text_directives_util.dart b/lib/src/utils/text_directives_util.dart deleted file mode 100644 index 060e48d13..000000000 --- a/lib/src/utils/text_directives_util.dart +++ /dev/null @@ -1,8 +0,0 @@ -import '../directives/text_directive.dart'; - -/// Directives. -const capitalize = CapitalizeDirective.new; -const upperCase = UppercaseDirective.new; -const lowerCase = LowercaseDirective.new; -const titleCase = TitleCaseDirective.new; -const sentenceCase = SentenceCaseDirective.new; diff --git a/lib/src/utils/text_util.dart b/lib/src/utils/text_util.dart deleted file mode 100644 index e47fc18e2..000000000 --- a/lib/src/utils/text_util.dart +++ /dev/null @@ -1,81 +0,0 @@ -// ignore_for_file: long-parameter-list - -import 'dart:ui'; - -import 'package:flutter/material.dart'; - -import '../attributes/shadow_attribute.dart'; -import '../attributes/strut_style_attribute.dart'; -import '../attributes/text_style_attribute.dart'; -import '../directives/text_directive.dart'; -import '../helpers/extensions/values_ext.dart'; - -StrutStyleAttribute strutStyle(StrutStyle strutStyle) { - return strutStyle.toAttribute(); -} - -TextDirectiveAttribute textDirective(TextDirective directive) => - TextDirectiveAttribute([directive]); - -TextStyleAttribute textStyle({ - String? fontFamily, - FontWeight? fontWeight, - FontStyle? fontStyle, - double? fontSize, - double? letterSpacing, - double? wordSpacing, - TextBaseline? textBaseline, - List? shadows, - Color? color, - Color? backgroundColor, - List? fontFeatures, - TextDecoration? decoration, - Color? decorationColor, - TextDecorationStyle? decorationStyle, - Paint? foreground, - Paint? background, - String? debugLabel, - Locale? locale, - double? height, -}) { - return TextStyleAttribute( - background: background, - backgroundColor: backgroundColor?.toAttribute(), - color: color?.toAttribute(), - debugLabel: debugLabel, - decoration: decoration, - decorationColor: decorationColor?.toAttribute(), - decorationStyle: decorationStyle, - fontFamily: fontFamily, - fontFeatures: fontFeatures, - fontSize: fontSize, - fontStyle: fontStyle, - fontWeight: fontWeight, - foreground: foreground, - height: height, - letterSpacing: letterSpacing, - locale: locale, - shadows: _shadowsFromDto(shadows), - textBaseline: textBaseline, - wordSpacing: wordSpacing, - ); -} - -TextStyleAttribute bold() => textStyle(fontWeight: FontWeight.bold); - -TextStyleAttribute italic() => textStyle(fontStyle: FontStyle.italic); - -ShadowAttribute _shadowFromDto(Shadow shadow) { - return ShadowAttribute( - blurRadius: shadow.blurRadius, - color: shadow.color.toAttribute(), - offset: shadow.offset, - ); -} - -List? _shadowsFromDto(List? shadows) { - if (shadows == null) return null; - if (shadows.isEmpty) return []; - - return shadows.map(_shadowFromDto).toList(); -} diff --git a/lib/src/variants/context_variant.dart b/lib/src/variants/context_variant.dart index 30cd4d293..7dfb617fb 100644 --- a/lib/src/variants/context_variant.dart +++ b/lib/src/variants/context_variant.dart @@ -2,17 +2,51 @@ import 'dart:core'; import 'package:flutter/material.dart'; -import '../attributes/attribute.dart'; import '../attributes/variant_attribute.dart'; +import '../core/attribute.dart'; import '../factory/style_mix.dart'; import 'variant.dart'; +/// A typedef for a function that determines if a specific context condition is met. typedef WhenContextFunction = bool Function(BuildContext context); +/// A variant of styling that is applied based on specific context conditions. +/// +/// `ContextVariant` extends the functionality of the `Variant` class by introducing +/// context-sensitive styling. It uses a condition function to determine if the variant's +/// styles should be applied, based on the current build context. +/// +/// Example: +/// Creating an `onDark` variant that applies styles when the app's theme is in dark mode: +/// ```dart +/// final onDark = ContextVariant( +/// 'on-dark', +/// when: (BuildContext context) => Theme.of(context).brightness == Brightness.dark, +/// ) +/// +/// final style = StyleMix( +/// textStyle(fontSize: 16), +/// onDark( +/// textStyle(color: Colors.white), +/// ), +/// ); +/// ``` +/// +/// This example defines a `ContextVariant` onDark, which checks if the current +/// theme brightness is dark. If true, the associated styles are applied. @immutable class ContextVariant extends Variant { + /// A function that defines the condition under which this variant should be applied. + /// + /// The [when] function takes a [BuildContext] and returns a boolean indicating whether + /// the condition for this variant is met in the given context. This allows for dynamic + /// styling changes based on runtime context conditions, such as theme brightness or screen size. final WhenContextFunction when; + /// Constructs a `ContextVariant` with a given [name] and a context condition function [when]. + /// + /// The [name] uniquely identifies the variant and is used in style resolution, while the + /// [when] function determines the applicability of the variant based on the given context. const ContextVariant(super.name, {required this.when}); @override diff --git a/lib/src/variants/multi_variant.dart b/lib/src/variants/multi_variant.dart index d5b3fdc52..89fb75e21 100644 --- a/lib/src/variants/multi_variant.dart +++ b/lib/src/variants/multi_variant.dart @@ -1,69 +1,160 @@ import 'package:flutter/material.dart'; -import '../attributes/attribute.dart'; import '../attributes/variant_attribute.dart'; +import '../core/attribute.dart'; import '../factory/style_mix.dart'; import 'context_variant.dart'; import 'variant.dart'; -enum MultiVariantType { and, or } +enum MultiVariantOperator { and, or } + +/// `MultiVariant` is a specialized form of `Variant` that allows combining multiple +/// variants together, using logical operations. This enables complex style definitions +/// that depend on multiple conditions. +/// +/// The class supports two types of combinations: +/// - `MultiVariantType.and`: Applies styles when all included variants are active. +/// - `MultiVariantType.or`: Applies styles when any of the included variants are active. +/// +/// `MultiVariant` also incorporates context-aware variants, allowing styles to adapt +/// based on the build context. This feature is especially useful for responsive +/// design or theming. +/// +/// Example Usage: +/// ```dart +/// final variantA = Variant('A'); +/// final variantB = Variant('B'); +/// final combinedAndVariant = MultiVariant.and([variantA, variantB]); +/// final combinedOrVariant = MultiVariant.or([variantA, variantB]); +/// +/// final style = StyleMix( +/// textStyle(fontSize: 16), +/// combinedAndVariant( +/// textStyle(color: Colors.blue), +/// ), +/// combinedOrVariant( +/// textStyle(color: Colors.green), +/// ), +/// ); +/// ``` +/// +/// In this example, `combinedAndVariant` applies its styles only when both `variantA` +/// and `variantB` are active, while `combinedOrVariant` applies its styles if either +/// `variantA` or `variantB` is active. This allows for flexible and dynamic styling +/// based on multiple conditions. @immutable class MultiVariant extends Variant { + /// A list of [Variant] instances contained within this `MultiVariant`. final List variants; - final MultiVariantType type; - const MultiVariant._(super.name, this.variants, {required this.type}); + /// The type operator of this `MultiVariant`, defining its category or specific behavior. + /// + /// This field is crucial in differentiating various `MultiVariant` instances and + /// understanding and applying their behavior. + final MultiVariantOperator operatorType; + + const MultiVariant._(super.name, this.variants, {required this.operatorType}); factory MultiVariant( Iterable variants, { - MultiVariantType type = MultiVariantType.and, + required MultiVariantOperator type, }) { final sortedVariants = variants.toList() ..sort(((a, b) => a.name.compareTo(b.name))); final combinedName = sortedVariants.map((e) => e.name).join('-'); - return MultiVariant._(combinedName, sortedVariants, type: type); + return MultiVariant._(combinedName, sortedVariants, operatorType: type); } - factory MultiVariant.and(Iterable variants) => MultiVariant( - variants, - ); + /// Factory constructor to create a `MultiVariant` where all provided variants need to be active (`MultiVariantType.and`). + /// + /// It initializes a `MultiVariant` with the given [variants] and sets the type to `MultiVariantType.and`. + factory MultiVariant.and(Iterable variants) { + return MultiVariant(variants, type: MultiVariantOperator.and); + } - factory MultiVariant.or(Iterable variants) => - MultiVariant(variants, type: MultiVariantType.or); + /// Factory constructor to create a `MultiVariant` where any one of the provided variants needs to be active (`MultiVariantType.or`). + /// + /// It initializes a `MultiVariant` with the given [variants] and sets the type to `MultiVariantType.or`. + factory MultiVariant.or(Iterable variants) { + return MultiVariant(variants, type: MultiVariantOperator.or); + } - // Remove all variants in given a list + /// Removes specified variants from this `MultiVariant`. + /// + /// This method returns a new variant after removing the specified [variantsToRemove]. + /// If only one variant remains after removal, it returns that single variant instead of a `MultiVariant`. + /// This is useful for dynamically adjusting styles by excluding certain variants. + /// + /// Example: + /// ```dart + /// final combinedVariant = MultiVariant.and([variantA, variantB, variantC]); + /// final updatedVariant = combinedVariant.remove([variantA]); + /// ``` + /// In this example, `updatedVariant` will be a combination of `variantB` and `variantC`. + /// This is useful for procedurally applying variants based on runtime conditions. Variant remove(Iterable variantsToRemove) { - final remainingVariants = - variants.where((e) => !variantsToRemove.contains(e)); + final updatedVariants = variants..removeWhere(variantsToRemove.contains); - return remainingVariants.length == 1 - ? remainingVariants.first - : MultiVariant(remainingVariants); + return updatedVariants.length == 1 + ? updatedVariants.first + : MultiVariant(updatedVariants, type: operatorType); } - bool matches(Iterable otherVariants) { - return type == MultiVariantType.and - ? variants.every(otherVariants.contains) - : variants.any(otherVariants.contains); + /// Determines if the current `MultiVariant` matches a set of provided variants. + /// + /// This method evaluates whether the variants within this `MultiVariant` align with the given [matchVariants] based on its `type`: + /// - `MultiVariantType.and`: Returns true if every variant in this `MultiVariant` is present in [matchVariants]. + /// - `MultiVariantType.or`: Returns true if at least one of the variants in this `MultiVariant` is present in [matchVariants]. + /// + /// This method is particularly useful for checking if a composite style, represented by this `MultiVariant`, + /// should be applied based on a specific set of active variants. + /// + /// Example: + /// ```dart + /// final combinedVariant = MultiVariant.and([variantA, variantB]); + /// bool isMatched = combinedVariant.matches([variantA, variantB, variantC]); + /// ``` + /// Here, `isMatched` will be true for `MultiVariantType.and` if both `variantA` and `variantB` are included in the provided list. + /// For `MultiVariantType.or`, `isMatched` would be true if either `variantA` or `variantB` is in the list. + bool matches(Iterable matchVariants) { + final matchSet = matchVariants.toSet(); + final variantSet = variants.toSet(); + + return operatorType == MultiVariantOperator.and + ? variantSet.difference(matchSet).isEmpty + : variantSet.intersection(matchSet).isNotEmpty; } + /// Evaluates if the `MultiVariant` should be applied based on the build context. + /// + /// For `MultiVariantType.or`, it returns true if any of the context-aware variants (`ContextVariant`) + /// evaluates true in the given [context]. For `MultiVariantType.and`, it returns true only if all context-aware + /// variants evaluate true, and if all variants in the `MultiVariant` are context-aware. + /// + /// This method enables context-sensitive styling, allowing the application of styles based on runtime + /// conditions like screen size, orientation, or theme. + /// + /// Example: + /// ```dart + /// final combinedVariant = MultiVariant.or([contextVariantA, contextVariantB]); + /// bool isApplicable = combinedVariant.when(context); + /// ``` + /// `isApplicable` will be true if either `contextVariantA` or `contextVariantB` is applicable in the given context. bool when(BuildContext context) { final contextVariants = variants.whereType(); - bool matchContextVariant(ContextVariant variant) => variant.when(context); - - if (type == MultiVariantType.or) { - return contextVariants.any(matchContextVariant); - } - - // If all variants are context variants apply the context - return contextVariants.length == variants.length - ? contextVariants.every(matchContextVariant) - : false; + return operatorType == MultiVariantOperator.or + ? contextVariants.any((variant) => variant.when(context)) + : contextVariants.length == variants.length && + contextVariants.every((variant) => variant.when(context)); } + /// A method for creating a new `MultiVariantAttribute` instance. + /// + /// It takes up to 20 optional [Attribute] parameters and creates a new `MultiVariantAttribute` using these attributes. + /// This method allows for easy creation of a `MultiVariantAttribute` with custom attributes. @override MultiVariantAttribute call([ Attribute? p1, @@ -92,7 +183,6 @@ class MultiVariant extends Variant { p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, ].whereType(); - // Create a ContextVariantAttribute using the collected parameters. return MultiVariantAttribute(this, StyleMix.create(params)); } diff --git a/lib/src/variants/variant.dart b/lib/src/variants/variant.dart index 0c56ea979..e9b144452 100644 --- a/lib/src/variants/variant.dart +++ b/lib/src/variants/variant.dart @@ -1,29 +1,77 @@ import 'package:flutter/material.dart'; -import '../attributes/attribute.dart'; import '../attributes/variant_attribute.dart'; -import '../core/equality/compare_mixin.dart'; +import '../core/attribute.dart'; import '../factory/style_mix.dart'; +import '../helpers/compare_mixin.dart'; import 'multi_variant.dart'; -/// A class representing a variant, which is a combination of attributes. -/// It can be combined with other variants using logical AND (&) and OR (|) operations. +/// An immutable class representing a styling variant. +/// +/// Variants encapsulate a set of styles that can be applied together under certain conditions +/// in your application, making it easy to switch between different sets of styles. +/// +/// You can think of variant as a switch that applies a certain set of styles when it's turned on. +/// +/// The `Variant` class is designed to be immutable and can be used in conjunction +/// with [StyleMix] and [Attribute] to define specific styling rules. +/// +/// Example Usage: +/// ```dart +/// const outlinedVariant = Variant('outlined'); +/// const filledVariant = Variant('filled'); + +/// final style = StyleMix( +/// // shared attributes between all variants +/// textStyle(fontSize: 16), +/// padding(10, 20), +/// outlinedVariant( +/// border(color: Colors.black, width: 1), +/// extStyle(color: Colors.black), +/// ), +/// filledVariant( +/// backgroundColor(Colors.black), +/// textStyle(color: Colors.white), +/// ), +/// ); +/// ``` @immutable class Variant with Comparable { final String name; - /// Creates a new [Variant] with a given [name] and an optional [inverse] flag. + /// Constructs a `Variant` with the given [name]. + /// + /// The [name] parameter uniquely identifies the variant and is used in style resolution. const Variant(this.name); - /// Combines this variant with another [variant] using a logical AND operation. + /// Combines this variant with another [variant] using an 'AND' operation. + /// + /// This operator returns a [MultiVariant] that represents a combination of both + /// variants. It is useful for defining styles that should be applied when + /// multiple conditions are met. + /// + /// Example: + /// ```dart + /// final combinedVariant = variant1 & variant2; + /// ``` MultiVariant operator &(Variant variant) => MultiVariant.and([this, variant]); - /// Combines this variant with another [variant] using a logical OR operation. + /// Combines this variant with another [variant] using an 'OR' operation. + /// + /// This operator returns a [MultiVariant] that represents either of the variants. + /// It is useful for defining styles that should be applied when any one of + /// multiple conditions is met. + /// + /// Example: + /// ```dart + /// final eitherVariant = variant1 | variant2; + /// ``` MultiVariant operator |(Variant variant) => MultiVariant.or([this, variant]); - /// Applies the variant to a set of attributes and creates a [VariantAttribute] instance. - /// Up to 12 optional [Attribute] parameters can be provided. - + /// Defines styles to be applied when this variant is active. + /// + /// You can define up to 20 styling [`Attribute`]s to be applied when this `Variant` is activated. + /// Null values are ignored and do not affect the resulting styling rules. VariantAttribute call([ Attribute? p1, Attribute? p2, @@ -51,8 +99,6 @@ class Variant with Comparable { p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, ].whereType(); - // Create a VariantAttribute using the collected parameters. - return VariantAttribute(this, StyleMix.create(params)); } diff --git a/lib/src/widgets/container_widget.dart b/lib/src/widgets/container_widget.dart deleted file mode 100644 index 17f5bcd5e..000000000 --- a/lib/src/widgets/container_widget.dart +++ /dev/null @@ -1,68 +0,0 @@ -// ignore_for_file: avoid-unnecessary-reassignment - -import 'package:flutter/material.dart'; - -import '../specs/container_spec.dart'; -import 'styled_widget.dart'; - -typedef Box = StyledContainer; - -class StyledContainer extends StyledWidget { - const StyledContainer({super.style, super.key, super.inherit, this.child}); - - final Widget? child; - - @override - Widget build(BuildContext context) { - return buildWithStyle(context, (data) { - final spec = ContainerSpec.resolve(data); - - return Container( - alignment: spec.alignment, - padding: spec.padding, - decoration: spec.decoration, - constraints: spec.constraints, - margin: spec.margin, - transform: spec.transform, - clipBehavior: spec.clipBehavior ?? Clip.none, - child: child, - ); - }); - } -} - -// class AnimatedStyledContainer extends AnimatedStyledWidget { -// const AnimatedStyledContainer({ -// super.style, -// super.key, -// super.inherit, -// this.child, -// this.mix, -// }); - -// @override -// @Deprecated('Use the style parameter instead') -// final StyleMix? mix; -// final Widget? child; - -// @override -// Widget build(BuildContext context) { -// return buildWithStyle(context, (data) { -// final spec = ContainerSpec.resolve(data); - -// return AnimatedContainer( -// alignment: spec.alignment, -// padding: spec.padding, -// decoration: spec.decoration, -// width: spec.width, -// height: spec.height, -// constraints: spec.constraints, -// margin: spec.margin, -// transform: spec.transform, -// curve: curve, -// duration: duration, -// child: child, -// ); -// }); -// } -// } diff --git a/lib/src/widgets/flex_widget.dart b/lib/src/widgets/flex_widget.dart deleted file mode 100644 index 866878607..000000000 --- a/lib/src/widgets/flex_widget.dart +++ /dev/null @@ -1,111 +0,0 @@ -import 'package:flutter/widgets.dart'; - -import '../specs/flex_spec.dart'; -import 'container_widget.dart'; -import 'styled_widget.dart'; - -class StyledFlex extends StyledWidget { - const StyledFlex({ - super.style, - super.key, - super.inherit, - required this.direction, - this.children = const [], - }); - - final List children; - final Axis direction; - - @override - Widget build(BuildContext context) { - return buildWithStyle(context, (data) { - final spec = FlexSpec.resolve(data); - List _childrenWithGap() { - final spacedChildren = []; - for (int i = 0; i < children.length; i++) { - spacedChildren.add(children[i]); - if (i < children.length - 1) { - // spacedChildren.add(Gap(gap)); - } - } - - return spacedChildren; - } - - final fallback = Flex(direction: direction); - - return Flex( - direction: direction, - mainAxisAlignment: spec.mainAxisAlignment ?? fallback.mainAxisAlignment, - mainAxisSize: spec.mainAxisSize ?? fallback.mainAxisSize, - crossAxisAlignment: - spec.crossAxisAlignment ?? fallback.crossAxisAlignment, - verticalDirection: spec.verticalDirection ?? fallback.verticalDirection, - children: _childrenWithGap(), - ); - }); - } -} - -class StyledRow extends StyledFlex { - const StyledRow({ - super.style, - super.key, - super.inherit, - required super.children, - }) : super(direction: Axis.horizontal); -} - -class StyledColumn extends StyledFlex { - const StyledColumn({ - super.style, - super.key, - super.inherit, - super.children, - }) : super(direction: Axis.vertical); -} - -class FlexBox extends StyledWidget { - const FlexBox({ - super.style, - super.key, - super.inherit, - required this.direction, - required this.children, - }); - - final List children; - final Axis direction; - - @override - Widget build(BuildContext context) { - return buildWithStyle(context, (data) { - return StyledContainer( - inherit: true, - child: StyledFlex( - inherit: true, - direction: direction, - children: children, - ), - ); - }); - } -} - -class HBox extends FlexBox { - const HBox({ - super.style, - super.key, - super.inherit, - super.children = const [], - }) : super(direction: Axis.horizontal); -} - -class VBox extends FlexBox { - const VBox({ - super.style, - super.key, - super.inherit, - super.children = const [], - }) : super(direction: Axis.vertical); -} diff --git a/lib/src/widgets/gap_widget.dart b/lib/src/widgets/gap_widget.dart new file mode 100644 index 000000000..2e740c099 --- /dev/null +++ b/lib/src/widgets/gap_widget.dart @@ -0,0 +1,73 @@ +// ignore_for_file: avoid-mutating-parameters + +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +class Gap extends LeafRenderObjectWidget { + const Gap(this.size, {Key? key}) : super(key: key); + + final double size; + + @override + RenderGap createRenderObject(BuildContext context) { + return RenderGap(mainAxisExtent: size); + } + + @override + void updateRenderObject(BuildContext context, RenderGap renderObject) { + renderObject.mainAxisExtent = size; + } +} + +class RenderGap extends RenderBox { + double _mainAxisExtent; + + RenderGap({required double mainAxisExtent}) + : _mainAxisExtent = mainAxisExtent; + + Axis? get _direction { + final parentRenderObject = parent; + + return parentRenderObject is RenderFlex + ? parentRenderObject.direction + : null; + } + + double get mainAxisExtent => _mainAxisExtent; + set mainAxisExtent(double value) { + if (_mainAxisExtent == value) return; + _mainAxisExtent = value; + markNeedsLayout(); + } + + @override + void performLayout() { + final direction = _direction; + final isHorizontal = direction == Axis.horizontal; + + double extent; + extent = isHorizontal + ? constraints.hasBoundedWidth + ? min(_mainAxisExtent, constraints.maxWidth) + : _mainAxisExtent + : constraints.hasBoundedHeight + ? min(_mainAxisExtent, constraints.maxHeight) + : _mainAxisExtent; + size = isHorizontal + ? Size(extent, constraints.maxHeight) + : Size(constraints.maxWidth, extent); + } + + @override + void paint(PaintingContext context, Offset offset) { + // No painting required as this widget is just a gap. + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DoubleProperty('mainAxisExtent', mainAxisExtent)); + } +} diff --git a/lib/src/widgets/icon_widget.dart b/lib/src/widgets/icon_widget.dart deleted file mode 100644 index b942bc766..000000000 --- a/lib/src/widgets/icon_widget.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../specs/icon_spec.dart'; -import 'styled_widget.dart'; - -class StyledIcon extends StyledWidget { - const StyledIcon( - this.icon, { - this.semanticLabel, - super.style, - super.key, - super.inherit, - }); - - final IconData? icon; - final String? semanticLabel; - - @override - Widget build(BuildContext context) { - return buildWithStyle(context, (data) { - // Resolve style attributes - final spec = IconSpec.resolve(data); - - return Icon( - icon, - size: spec.size, - color: spec.color, - semanticLabel: semanticLabel, - textDirection: spec.textDirection, - ); - }); - } -} - -class AnimatedStyledIcon extends StyledWidget { - const AnimatedStyledIcon( - this.icon, { - this.semanticLabel, - super.style, - super.key, - required this.progress, - super.inherit, - }); - - final AnimatedIconData icon; - final String? semanticLabel; - final Animation progress; - - @override - Widget build(BuildContext context) { - return buildWithStyle(context, (data) { - // Resolve style attributes - final spec = IconSpec.resolve(data); - - return AnimatedIcon( - icon: icon, - progress: progress, - color: spec.color, - size: spec.size, - semanticLabel: semanticLabel, - textDirection: spec.textDirection, - ); - }); - } -} diff --git a/lib/src/widgets/stack_widget.dart b/lib/src/widgets/stack_widget.dart deleted file mode 100644 index 0a2121cc3..000000000 --- a/lib/src/widgets/stack_widget.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/widgets.dart'; - -import '../specs/stack_spec.dart'; -import 'container_widget.dart'; -import 'styled_widget.dart'; - -class StyledStack extends StyledWidget { - const StyledStack({ - this.children = const [], - super.inherit, - super.key, - super.style, - }); - - final List children; - - @override - Widget build(BuildContext context) { - return buildWithStyle(context, (data) { - final spec = StackSpec.resolve(data); - - const fallback = Stack(); - - return Stack( - alignment: spec.alignment ?? fallback.alignment, - textDirection: spec.textDirection, - fit: spec.fit ?? fallback.fit, - clipBehavior: spec.clipBehavior ?? fallback.clipBehavior, - children: children, - ); - }); - } -} - -class ZBox extends StyledWidget { - const ZBox({ - this.children = const [], - super.inherit, - super.key, - super.style, - }); - - final List children; - - @override - Widget build(BuildContext context) { - return buildWithStyle(context, (data) { - return StyledContainer( - inherit: true, - child: StyledStack(inherit: true, children: children), - ); - }); - } -} diff --git a/lib/src/widgets/styled_widget.dart b/lib/src/widgets/styled_widget.dart index 89c069a0a..ec1510406 100644 --- a/lib/src/widgets/styled_widget.dart +++ b/lib/src/widgets/styled_widget.dart @@ -19,13 +19,8 @@ abstract class StyledWidget extends StatelessWidget { final bool inherit; - Widget buildWithStyle(BuildContext context, MixBuilder builder) { - return Mix.build( - context, - style: style, - builder: builder, - inherit: inherit, - ); + Widget withMix(BuildContext context, MixBuilder builder) { + return MixProvider.build(context, style: style, builder: builder); } @override diff --git a/lib/src/widgets/text_widget.dart b/lib/src/widgets/text_widget.dart deleted file mode 100644 index 971cd6ef1..000000000 --- a/lib/src/widgets/text_widget.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../specs/text_spec.dart'; -import 'styled_widget.dart'; - -@Deprecated('Use StyledText now') -typedef TextMix = StyledText; - -class StyledText extends StyledWidget { - const StyledText( - this.text, { - this.semanticsLabel, - super.style, - super.key, - super.inherit, - this.locale, - }); - - final String text; - final String? semanticsLabel; - final Locale? locale; - - @override - Widget build(BuildContext context) { - return buildWithStyle(context, (data) { - final spec = TextSpec.resolve(data); - - return Text( - spec.applyTextDirectives(text), - style: spec.style, - strutStyle: spec.strutStyle, - textAlign: spec.textAlign, - textDirection: spec.textDirection ?? TextDirection.ltr, - locale: locale, - softWrap: spec.softWrap, - overflow: spec.overflow, - textScaleFactor: spec.textScaleFactor, - maxLines: spec.maxLines, - semanticsLabel: semanticsLabel, - textWidthBasis: spec.textWidthBasis, - textHeightBehavior: spec.textHeightBehavior, - ); - }); - } -} diff --git a/test/helpers/attribute_generator.dart b/test/helpers/attribute_generator.dart index 6d1c11d23..648ebfe3d 100644 --- a/test/helpers/attribute_generator.dart +++ b/test/helpers/attribute_generator.dart @@ -3,30 +3,11 @@ import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:mix/mix.dart'; -class AttributeGenerator { - const AttributeGenerator(); +class RandomGenerator { + const RandomGenerator._(); - PaddingAttribute padding({ - double? top, - double? bottom, - double? left, - double? right, - }) { - final random = Random(); - - return PaddingAttribute( - top: top ?? random.nextDouble() * 20, - bottom: bottom ?? random.nextDouble() * 20, - left: left ?? random.nextDouble() * 20, - right: right ?? random.nextDouble() * 20, - ); - } - - BoxConstraintsAttribute boxConstraints({ - double? width, - double? height, + static BoxConstraints boxConstraints({ double? minWidth, double? maxWidth, double? minHeight, @@ -40,9 +21,7 @@ class AttributeGenerator { maxHeight ??= minWidth + random.nextDouble() * 200; maxWidth ??= minHeight + random.nextDouble() * 200; - return BoxConstraintsAttribute( - width: width, - height: height, + return BoxConstraints( minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, @@ -50,7 +29,31 @@ class AttributeGenerator { ); } - MarginAttribute margin({ + static StrutStyle strutStyle({ + String? fontFamily, + List? fontFamilyFallback, + double? fontSize, + FontWeight? fontWeight, + FontStyle? fontStyle, + double? height, + double? leading, + bool? forceStrutHeight, + }) { + final random = Random(); + + return StrutStyle( + fontFamily: fontFamily ?? 'Roboto', + fontFamilyFallback: fontFamilyFallback ?? ['Roboto'], + fontSize: fontSize ?? random.nextDouble() * 20, + fontWeight: fontWeight ?? FontWeight.values.random(), + fontStyle: fontStyle ?? FontStyle.values.random(), + height: height ?? random.nextDouble() * 20, + leading: leading ?? random.nextDouble() * 20, + forceStrutHeight: forceStrutHeight ?? random.nextBool(), + ); + } + + static EdgeInsets edgeInsets({ double? top, double? bottom, double? left, @@ -58,7 +61,7 @@ class AttributeGenerator { }) { final random = Random(); - return MarginAttribute( + return EdgeInsets.only( top: top ?? random.nextDouble() * 20, bottom: bottom ?? random.nextDouble() * 20, left: left ?? random.nextDouble() * 20, @@ -66,7 +69,23 @@ class AttributeGenerator { ); } - TransformAttribute transform({ + static EdgeInsetsDirectional edgeInsetsDirectional({ + double? top, + double? bottom, + double? start, + double? end, + }) { + final random = Random(); + + return EdgeInsetsDirectional.only( + top: top ?? random.nextDouble() * 20, + bottom: bottom ?? random.nextDouble() * 20, + start: start ?? random.nextDouble() * 20, + end: end ?? random.nextDouble() * 20, + ); + } + + static Matrix4 matrix4({ double? x, double? y, double? z, @@ -77,229 +96,231 @@ class AttributeGenerator { x ?? random.nextDouble() * 20, y ?? random.nextDouble() * 20, z ?? random.nextDouble() * 20, - ).toAttribute(); - } - - ClipAttribute clip([Clip? clip]) { - return clip?.toAttribute() ?? - Random().randomElement([ - Clip.none, - Clip.antiAlias, - Clip.antiAliasWithSaveLayer, - Clip.hardEdge, - ]).toAttribute(); - } - - TextOverflowAttribute overflow([TextOverflow? overflow]) { - return overflow?.toAttribute() ?? - Random().randomElement([ - TextOverflow.clip, - TextOverflow.visible, - ]).toAttribute(); + ); } - TextDirectionAttribute textDirection([TextDirection? direction]) { - return direction?.toAttribute() ?? - Random().randomElement([ - TextDirection.ltr, - TextDirection.rtl, - ]).toAttribute(); + TextOverflow textOverflow([TextOverflow? overflow]) { + return overflow ?? TextOverflow.values.random(); } - AxisAttribute axis([Axis? axis]) { - return axis?.toAttribute() ?? - Random().randomElement([ - Axis.horizontal, - Axis.vertical, - ]).toAttribute(); + Axis axis([Axis? axis]) { + return axis ?? Random().randomElement(Axis.values); } - MainAxisAlignmentAttribute mainAxisAlignment([MainAxisAlignment? alignment]) { - return alignment?.toAttribute() ?? - Random().randomElement([ - MainAxisAlignment.start, - MainAxisAlignment.end, - MainAxisAlignment.center, - MainAxisAlignment.spaceBetween, - MainAxisAlignment.spaceAround, - MainAxisAlignment.spaceEvenly, - ]).toAttribute(); + MainAxisAlignment mainAxisAlignment([MainAxisAlignment? alignment]) { + return alignment ?? MainAxisAlignment.values.random(); } - MainAxisSizeAttribute mainAxisSize([MainAxisSize? size]) { - return size?.toAttribute() ?? - Random().randomElement([ - MainAxisSize.max, - MainAxisSize.min, - ]).toAttribute(); + MainAxisSize mainAxisSize([MainAxisSize? size]) { + return size ?? MainAxisSize.values.random(); } - CrossAxisAlignmentAttribute crossAxisAlignment( - [CrossAxisAlignment? alignment]) { - return alignment?.toAttribute() ?? - Random().randomElement([ - CrossAxisAlignment.start, - CrossAxisAlignment.end, - CrossAxisAlignment.center, - CrossAxisAlignment.stretch, - CrossAxisAlignment.baseline, - ]).toAttribute(); + CrossAxisAlignment crossAxisAlignment([CrossAxisAlignment? alignment]) { + return alignment ?? CrossAxisAlignment.values.random(); } - TextBaselineAttribute textBaseline([TextBaseline? baseline]) { - return baseline?.toAttribute() ?? - Random().randomElement([ - TextBaseline.alphabetic, - TextBaseline.ideographic, - ]).toAttribute(); + TextBaseline textBaseline([TextBaseline? baseline]) { + return baseline ?? TextBaseline.values.random(); } - VerticalDirectionAttribute verticalDirection([VerticalDirection? direction]) { - return direction?.toAttribute() ?? - Random().randomElement([ - VerticalDirection.down, - VerticalDirection.up, - ]).toAttribute(); + VerticalDirection verticalDirection([VerticalDirection? direction]) { + return direction ?? VerticalDirection.values.random(); } - BorderRadiusAttribute borderRadius({ + static BorderRadius borderRadius({ double? topLeft, double? topRight, double? bottomLeft, double? bottomRight, }) { - final random = Random(); - - return BorderRadiusAttribute( - topLeft: Radius.circular(topLeft ?? random.nextDouble() * 20), - topRight: Radius.circular(topRight ?? random.nextDouble() * 20), - bottomLeft: Radius.circular(bottomLeft ?? random.nextDouble() * 20), - bottomRight: Radius.circular(bottomRight ?? random.nextDouble() * 20), + return BorderRadius.only( + topLeft: RandomGenerator.radius(topLeft), + topRight: RandomGenerator.radius(topRight), + bottomLeft: RandomGenerator.radius(bottomLeft), + bottomRight: RandomGenerator.radius(bottomRight), ); } - BorderRadiusDirectionalAttribute borderRadiusDirectional({ + static BorderRadiusDirectional borderDirectionalRadius({ double? topStart, double? topEnd, double? bottomStart, double? bottomEnd, }) { - final random = Random(); - - return BorderRadiusDirectionalAttribute( - topStart: Radius.circular(topStart ?? random.nextDouble() * 20), - topEnd: Radius.circular(topEnd ?? random.nextDouble() * 20), - bottomStart: Radius.circular(bottomStart ?? random.nextDouble() * 20), - bottomEnd: Radius.circular(bottomEnd ?? random.nextDouble() * 20), + return BorderRadiusDirectional.only( + topStart: RandomGenerator.radius(topStart), + topEnd: RandomGenerator.radius(topEnd), + bottomStart: RandomGenerator.radius(bottomStart), + bottomEnd: RandomGenerator.radius(bottomEnd), ); } - BorderAttribute border({ - BorderSideAttribute? left, - BorderSideAttribute? right, - BorderSideAttribute? top, - BorderSideAttribute? bottom, - }) { - return BorderAttribute( - left: left ?? borderSide(), - right: right ?? borderSide(), - top: top ?? borderSide(), - bottom: bottom ?? borderSide(), - ); + static Radius radius([double? radius]) { + return Radius.circular(radius ?? Random().nextDouble() * 20); } - ColorAttribute color([Color? color]) { - return ColorAttribute( - color ?? - Color.fromARGB( - 255, - Random().nextInt(255), - Random().nextInt(255), - Random().nextInt(255), - ), + static Border border({ + BorderSide? left, + BorderSide? right, + BorderSide? top, + BorderSide? bottom, + }) { + return Border( + left: left ?? RandomGenerator.borderSide(), + right: right ?? RandomGenerator.borderSide(), + top: top ?? RandomGenerator.borderSide(), + bottom: bottom ?? RandomGenerator.borderSide(), ); } - BorderSideAttribute borderSide({ - ColorAttribute? color, + static BorderSide borderSide({ + Color? color, double? width, BorderStyle? style, }) { - return BorderSideAttribute( - color: color ?? this.color(), + return BorderSide( + color: color ?? RandomGenerator.color(), width: width ?? Random().nextDouble() * 4, style: style ?? BorderStyle.values.random(), ); } - ShadowAttribute shadow({ - ColorAttribute? color, + static BorderDirectional borderDirectional({ + BorderSide? top, + BorderSide? bottom, + BorderSide? start, + BorderSide? end, + }) { + return BorderDirectional( + top: top ?? RandomGenerator.borderSide(), + bottom: bottom ?? RandomGenerator.borderSide(), + start: start ?? RandomGenerator.borderSide(), + end: end ?? RandomGenerator.borderSide(), + ); + } + + static Color color([Color? color]) { + return color ?? + Color.fromARGB( + 255, + Random().nextInt(255), + Random().nextInt(255), + Random().nextInt(255), + ); + } + + static Shadow shadow({ + Color? color, Offset? offset, double? blurRadius, }) { - return ShadowAttribute( - color: color ?? this.color(), + return Shadow( + color: color ?? RandomGenerator.color(), offset: offset ?? const Offset(0, 0), blurRadius: blurRadius ?? Random().nextDouble() * 4, ); } - AlignmentAttribute alignment() { - return Random().randomElement([ - Alignment.center, - Alignment.centerLeft, - Alignment.centerRight, - Alignment.topCenter, - Alignment.topLeft, - Alignment.topRight, - Alignment.bottomCenter, - Alignment.bottomLeft, - Alignment.bottomRight, - ]).toAttribute(); - } - - BoxDecorationAttribute boxDecoration({ - ColorAttribute? color, - BorderAttribute? border, - BorderRadiusAttribute? borderRadius, - List? boxShadow, - BoxShapeAttribute? shape, + static Alignment alignment([Alignment? alignment]) { + return alignment ?? + Random().randomElement([ + Alignment.center, + Alignment.centerLeft, + Alignment.centerRight, + Alignment.topCenter, + Alignment.topLeft, + Alignment.topRight, + Alignment.bottomCenter, + Alignment.bottomLeft, + Alignment.bottomRight, + ]); + } + + static AlignmentDirectional alignmentDirectional( + [AlignmentDirectional? alignment]) { + return alignment ?? + Random().randomElement([ + AlignmentDirectional.center, + AlignmentDirectional.centerStart, + AlignmentDirectional.centerEnd, + AlignmentDirectional.topCenter, + AlignmentDirectional.topStart, + AlignmentDirectional.topEnd, + AlignmentDirectional.bottomCenter, + AlignmentDirectional.bottomStart, + AlignmentDirectional.bottomEnd, + ]); + } + + static BoxDecoration boxDecoration({ + Color? color, + BoxBorder? border, + BorderRadiusGeometry? borderRadius, + List? boxShadow, + BoxShape? shape, + Gradient? gradient, + }) { + final boxShadowList = boxShadow ?? [RandomGenerator.boxShadow()]; + return BoxDecoration( + color: color ?? RandomGenerator.color(), + border: border ?? RandomGenerator.border(), + borderRadius: borderRadius ?? RandomGenerator.borderRadius(), + boxShadow: boxShadowList, + shape: shape ?? BoxShape.values.random(), + gradient: gradient ?? RandomGenerator.linearGradient(), + ); + } + + static Offset offset({ + double? dx, + double? dy, }) { - return BoxDecorationAttribute( - color: color ?? this.color(), - border: border ?? this.border(), - borderRadius: borderRadius ?? this.borderRadius(), - boxShadow: boxShadow ?? - [ - this.boxShadow(), - ], - shape: shape ?? BoxShape.values.random().toAttribute(), + return Offset( + dx ?? Random().nextDouble() * 20, + dy ?? Random().nextDouble() * 20, ); } - BoxShadowAttribute boxShadow({ - ColorAttribute? color, + static BoxShadow boxShadow({ + Color? color, Offset? offset, double? blurRadius, double? spreadRadius, }) { - return BoxShadowAttribute( - color: color ?? this.color(), - offset: offset ?? - Offset( - Random().nextMaxDouble(10), - Random().nextMaxDouble(10), - ), + return BoxShadow( + color: color ?? RandomGenerator.color(), + offset: offset ?? RandomGenerator.offset(), blurRadius: blurRadius ?? Random().nextMaxDouble(10), spreadRadius: spreadRadius ?? Random().nextMaxDouble(10), ); } - TextStyleAttribute textStyle() { - return TextStyleAttribute( - color: color(), - backgroundColor: color(), - decorationColor: color(), + static LinearGradient linearGradient({ + AlignmentGeometry? begin, + AlignmentGeometry? end, + List? colors, + List? stops, + TileMode? tileMode, + }) { + return LinearGradient( + begin: begin ?? RandomGenerator.alignment(), + end: end ?? RandomGenerator.alignment(), + colors: colors ?? [RandomGenerator.color()], + stops: stops ?? [Random().nextDouble()], + tileMode: tileMode ?? TileMode.values.random(), + ); + } + + TextStyle textStyle() { + final shadows = [ + RandomGenerator.shadow(), + RandomGenerator.shadow(), + RandomGenerator.shadow(), + ]; + return TextStyle( + color: RandomGenerator.color(), + backgroundColor: RandomGenerator.color(), + decorationColor: RandomGenerator.color(), decorationStyle: TextDecorationStyle.values.random(), fontFamily: 'Roboto', fontSize: Random().nextDoubleInRange(12, 32), @@ -309,9 +330,7 @@ class AttributeGenerator { wordSpacing: Random().nextDoubleInRange(0, 2), height: Random().nextDoubleInRange(0, 2), locale: const Locale('en', 'US'), - shadows: [ - shadow(), - ], + shadows: shadows, decoration: [ TextDecoration.none, TextDecoration.underline, diff --git a/test/helpers/equatable_mixin_test.dart b/test/helpers/equatable_mixin_test.dart index 17fbedc7d..7f64de5ee 100644 --- a/test/helpers/equatable_mixin_test.dart +++ b/test/helpers/equatable_mixin_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/core/equality/compare_mixin.dart'; +import 'package:mix/src/helpers/compare_mixin.dart'; void main() { group('EquatableMixin', () { diff --git a/test/helpers/string_ext_test.dart b/test/helpers/string_ext_test.dart index 38c39723a..825e491a9 100644 --- a/test/helpers/string_ext_test.dart +++ b/test/helpers/string_ext_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/helpers/extensions/string_ext.dart'; +import 'package:mix/src/helpers/string_ext.dart'; void main() { group('ChangeCase - _groupWords', () { diff --git a/test/helpers/testing_utils.dart b/test/helpers/testing_utils.dart index 9b3b75729..aa0c64e91 100644 --- a/test/helpers/testing_utils.dart +++ b/test/helpers/testing_utils.dart @@ -6,7 +6,7 @@ import 'package:meta/meta.dart'; import 'package:mix/mix.dart'; import 'package:mockito/mockito.dart'; -export 'package:mix/src/helpers/extensions/values_ext.dart'; +export 'package:mix/src/core/extensions/values_ext.dart'; class MockBuildContext extends Mock implements BuildContext {} @@ -98,17 +98,30 @@ extension WidgetTesterExt on WidgetTester { Widget widget, { StyleMix style = StyleMix.empty, MixThemeData theme = const MixThemeData.empty(), + }) async { + await pumpWithMixTheme( + Builder( + builder: (BuildContext context) { + // Populate MixData into the widget tree if needed + return MixProvider.build( + context, + style: style, + builder: (_) => widget, + ); + }, + ), + ); + } + + Future pumpWithMixTheme( + Widget widget, { + MixThemeData theme = const MixThemeData.empty(), }) async { await pumpWidget( MaterialApp( home: MixTheme( data: theme, - child: Builder( - builder: (BuildContext context) { - // Populate MixData into the widget tree if needed - return Mix.build(context, style: style, builder: (_) => widget); - }, - ), + child: widget, ), ), ); @@ -184,61 +197,32 @@ class WrapMixThemeWidget extends StatelessWidget { class MockDoubleScalarAttribute extends ScalarAttribute { const MockDoubleScalarAttribute(super.value); - - @override - MockDoubleScalarAttribute create(double value) => - MockDoubleScalarAttribute(value); } class MockIntScalarAttribute extends ScalarAttribute { const MockIntScalarAttribute(super.value); - - @override - MockIntScalarAttribute create(int value) => MockIntScalarAttribute(value); } -class MockDoubleDecoratorAttribute extends Decorator { +class MockDoubleDecoratorAttribute extends Decorator { final double value; - const MockDoubleDecoratorAttribute(this.value); - - @override - MockDoubleDecoratorAttribute merge(MockDoubleDecoratorAttribute? other) { - return MockDoubleDecoratorAttribute(other?.value ?? value); - } - - @override - double resolve(MixData mix) => value; + const MockDoubleDecoratorAttribute(this.value, {super.key}); @override get props => [value]; @override - Widget build(child, value) { - return SizedBox( - height: value, - width: value, - child: child, - ); - } + Type get type => MockDoubleDecoratorAttribute; } class MockBooleanScalarAttribute extends ScalarAttribute { const MockBooleanScalarAttribute(super.value); - - @override - MockBooleanScalarAttribute create(bool value) => - MockBooleanScalarAttribute(value); } class MockStringScalarAttribute extends ScalarAttribute { const MockStringScalarAttribute(super.value); - - @override - MockStringScalarAttribute create(String value) => - MockStringScalarAttribute(value); } class MockInvalidAttribute extends Attribute { @@ -246,11 +230,6 @@ class MockInvalidAttribute extends Attribute { @override get props => []; - - @override - MockInvalidAttribute merge(MockInvalidAttribute? other) { - return this; - } } const mockVariant = Variant('mock-variant'); @@ -264,17 +243,10 @@ void testScalarAttribute, V>( group(groupName, () { for (var value1 in values) { for (var value2 in values.where((v) => v != value1)) { - test('merge $value1 with $value2', () { - final attr1 = builder(value1); - final attr2 = builder(value2); - final merged = attr1.merge(attr2); - expect(merged.value, equals(value2)); - }); - test('resolve $value1', () { final attr = builder(value1); - final resolvedValue = attr.resolve(EmptyMixData); - expect(resolvedValue, equals(value1)); + + expect(attr.value, equals(value1)); }); test('check equality between $value1 and $value2', () { @@ -289,18 +261,11 @@ void testScalarAttribute, V>( expect(attr1, equals(attr2)); }); } - - test('merge null with $value1 returns itself', () { - final attr1 = builder(value1); - final merged = attr1.merge(null); - expect(merged, equals(attr1)); - }); - - test('resolves correctly', () { - final attr = builder(value1); - final resolvedValue = attr.resolve(EmptyMixData); - expect(resolvedValue, equals(value1)); - }); } }); } + +class UtilityTestAttribute + extends ScalarAttribute, T> { + const UtilityTestAttribute(super.value); +} diff --git a/test/src/attributes/alignment_attribute_test.dart b/test/src/attributes/alignment_attribute_test.dart deleted file mode 100644 index d6611f131..000000000 --- a/test/src/attributes/alignment_attribute_test.dart +++ /dev/null @@ -1,154 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/attributes/alignment_attribute.dart'; - -import '../../helpers/testing_utils.dart'; - -void main() { - group('AlignmentAttribute', () { - test('class resolves correctly', () { - const alignment = AlignmentAttribute(x: 0.5, y: 0.6); - - final resolvedValue = alignment.resolve(EmptyMixData); - - expect(alignment, isA()); - expect(alignment, isA()); - expect(resolvedValue, isA()); - expect(resolvedValue.x, 0.5); - expect(resolvedValue.y, 0.6); - }); - test('merge returns merged object correctly', () { - const attr1 = AlignmentAttribute(x: 0.1, y: 0.2); - const attr2 = AlignmentAttribute(x: 0.3); - - final merged = attr1.merge(attr2); - - final resolvedValue = merged.resolve(EmptyMixData); - - expect(resolvedValue.x, 0.3); // should take from attr2 - expect(resolvedValue.y, 0.2); // should take from attr1 - expect(resolvedValue, isA()); - }); - - test('AlignmentAttribute merge returns itself if other is null', () { - const attr1 = AlignmentAttribute(x: 0.1, y: 0.2); - - final merged = attr1.merge(null); - - expect(merged, attr1); - }); - - test( - 'AlignmentAttribute resolve returns correct Alignment', - () { - const attr = AlignmentAttribute(x: 0.5, y: 0.5); - - final alignment = attr.resolve(EmptyMixData); - - expect(alignment, const Alignment(0.5, 0.5)); - }, - ); - - test( - 'AlignmentAttribute resolve uses default values if null', - () { - const attr = AlignmentAttribute(); - - final alignment = attr.resolve(EmptyMixData); - - expect(alignment, const Alignment(0.0, 0.0)); - }, - ); - - test('AlignmentAttribute equality holds when properties are the same', () { - const attr1 = AlignmentAttribute(x: 0.1, y: 0.2); - const attr2 = AlignmentAttribute(x: 0.1, y: 0.2); - - expect(attr1, attr2); - }); - - test('AlignmentAttribute equality fails when properties are different', () { - const attr1 = AlignmentAttribute(x: 0.1, y: 0.2); - const attr2 = AlignmentAttribute(x: 0.2, y: 0.2); - - expect(attr1, isNot(attr2)); - }); - }); - - group('AlignmentDirectionalAttribute', () { - test('class resolves correctly', () { - const alignment = AlignmentDirectionalAttribute(start: 0.5, y: 0.6); - - final resolvedValue = alignment.resolve(EmptyMixData); - - expect(alignment, isA()); - expect(alignment, isA()); - expect(resolvedValue, isA()); - expect(resolvedValue, isA()); - expect(resolvedValue.start, 0.5); - expect(resolvedValue.y, 0.6); - }); - - test('merge returns merged object correctly', () { - const attr1 = AlignmentDirectionalAttribute(start: 0.1, y: 0.2); - const attr2 = AlignmentDirectionalAttribute(start: 0.3); - - final merged = attr1.merge(attr2); - - final resolvedValue = merged.resolve(EmptyMixData); - - expect(resolvedValue.start, 0.3); // should take from attr2 - expect(resolvedValue.y, 0.2); // should take from attr1 - expect(resolvedValue, isA()); - }); - - test('AlignmentDirectionalAttribute merge returns itself if other is null', - () { - const attr1 = AlignmentDirectionalAttribute(start: 0.1, y: 0.2); - - final merged = attr1.merge(null); - - expect(merged, attr1); - }); - - test( - 'AlignmentDirectionalAttribute resolve returns correct AlignmentDirectional', - () { - const attr = AlignmentDirectionalAttribute(start: 0.5, y: 0.5); - - final alignment = attr.resolve(EmptyMixData); - - expect(alignment, const AlignmentDirectional(0.5, 0.5)); - }, - ); - - test( - 'AlignmentDirectionalAttribute resolve uses default values if null', - () { - const attr = AlignmentDirectionalAttribute(); - - final alignment = attr.resolve(EmptyMixData); - - expect(alignment, const AlignmentDirectional(0.0, 0.0)); - }, - ); - - test( - 'AlignmentDirectionalAttribute equality holds when properties are the same', - () { - const attr1 = AlignmentDirectionalAttribute(start: 0.1, y: 0.2); - const attr2 = AlignmentDirectionalAttribute(start: 0.1, y: 0.2); - - expect(attr1, attr2); - }); - - test( - 'AlignmentDirectionalAttribute equality fails when properties are different', - () { - const attr1 = AlignmentDirectionalAttribute(start: 0.1, y: 0.2); - const attr2 = AlignmentDirectionalAttribute(start: 0.2, y: 0.2); - - expect(attr1, isNot(attr2)); - }); - }); -} diff --git a/test/src/attributes/border/border_attribute_test.dart b/test/src/attributes/border/border_attribute_test.dart index 7700df843..98495ff07 100644 --- a/test/src/attributes/border/border_attribute_test.dart +++ b/test/src/attributes/border/border_attribute_test.dart @@ -1,221 +1,73 @@ -import 'package:flutter/rendering.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/attributes/border/border_attribute.dart'; +import 'package:mix/mix.dart'; import '../../../helpers/testing_utils.dart'; void main() { - group('BorderAttribute', () { - test('merge should combine two BorderAttributes correctly', () { - const borderAttr1 = BorderAttribute( - top: BorderSideAttribute(width: 5.0), - bottom: BorderSideAttribute(width: 10.0), - left: BorderSideAttribute(width: 15.0), - right: BorderSideAttribute(width: 20.0), - ); - const borderAttr2 = BorderAttribute( - left: BorderSideAttribute(width: 25.0), - ); - - final mergedBorderAttr = borderAttr1.merge(borderAttr2); - - expect(mergedBorderAttr.top, borderAttr1.top); - expect(mergedBorderAttr.bottom, borderAttr1.bottom); - expect(mergedBorderAttr.left, borderAttr2.left); - expect(mergedBorderAttr.right, borderAttr1.right); - }); - - test('resolve should create a Border with the correct values', () { - const borderAttr = BorderAttribute( - top: BorderSideAttribute(width: 5.0), - bottom: BorderSideAttribute(width: 10.0), - left: BorderSideAttribute(width: 15.0), - right: BorderSideAttribute(width: 20.0), - ); - - final resolvedBorder = borderAttr.resolve(EmptyMixData); - - expect(resolvedBorder.top, const BorderSide(width: 5.0)); - expect(resolvedBorder.bottom, const BorderSide(width: 10.0)); - expect(resolvedBorder.left, const BorderSide(width: 15.0)); - expect(resolvedBorder.right, const BorderSide(width: 20.0)); - }); - - test('Equality holds when properties are the same', () { - const attr1 = BorderAttribute( - top: BorderSideAttribute(width: 5.0), - bottom: BorderSideAttribute(width: 10.0), - left: BorderSideAttribute(width: 15.0), - right: BorderSideAttribute(width: 20.0), - ); - const attr2 = BorderAttribute( - top: BorderSideAttribute(width: 5.0), - bottom: BorderSideAttribute(width: 10.0), - left: BorderSideAttribute(width: 15.0), - right: BorderSideAttribute(width: 20.0), - ); - expect(attr1, attr2); - }); - - test('Equality fails when properties are different', () { - const attr1 = BorderAttribute( - top: BorderSideAttribute(width: 5.0), - bottom: BorderSideAttribute(width: 10.0), - left: BorderSideAttribute(width: 15.0), - right: BorderSideAttribute(width: 20.0), - ); - - const attr2 = BorderAttribute( - top: BorderSideAttribute(width: 5.0), - bottom: BorderSideAttribute(width: 10.0), - left: BorderSideAttribute(width: 15.0), - right: BorderSideAttribute(width: 25.0), - ); - expect(attr1, isNot(attr2)); - }); - }); - - test('Matches Border constructors', () { - const allAttr = BorderAttribute.all(BorderSideAttribute(width: 10.0)); - final allValue = Border.all(width: 10.0); - - final resolvedAllValue = allAttr.resolve(EmptyMixData); + group('BoxBorderAttribute', () { + const borderSide = BorderSide( + width: 1.0, + color: Colors.red, + style: BorderStyle.solid, + ); - expect(resolvedAllValue, allValue); + const border = Border.fromBorderSide(borderSide); - const symmetricAttr = BorderAttribute.symmetric( - vertical: BorderSideAttribute(width: 5.0), - horizontal: BorderSideAttribute(width: 10.0), + final border2 = Border.all( + width: 2.0, + color: Colors.blue, + style: BorderStyle.none, ); - const symmetricValue = Border.symmetric( - vertical: BorderSide(width: 5.0), - horizontal: BorderSide(width: 10.0), + const borderDirectional = BorderDirectional( + top: borderSide, + start: borderSide, + end: borderSide, + bottom: borderSide, ); - final resolvedSymmetricValue = symmetricAttr.resolve(EmptyMixData); + test('resolve Border', () { + final borderAttribute = BoxBorderAttribute(border.toDto()); - expect(resolvedSymmetricValue, symmetricValue); + final resolvedBorder = borderAttribute.resolve(EmptyMixData) as Border; - const onlyAttr = BorderAttribute( - top: BorderSideAttribute(width: 5.0), - right: BorderSideAttribute(width: 10.0), - bottom: BorderSideAttribute(width: 15.0), - left: BorderSideAttribute(width: 20.0), - ); + expect(resolvedBorder, isA()); + expect(resolvedBorder.top, border.top); + expect(resolvedBorder.bottom, border.bottom); + expect(resolvedBorder.left, border.left); + expect(resolvedBorder.right, border.right); + }); - const onlyValue = Border( - top: BorderSide(width: 5.0), - right: BorderSide(width: 10.0), - bottom: BorderSide(width: 15.0), - left: BorderSide(width: 20.0), - ); + test('resolve BorderDirectional', () { + final borderAttribute = BoxBorderAttribute(borderDirectional.toDto()); - final resolvedOnlyValue = onlyAttr.resolve(EmptyMixData); + final resolvedBorder = + borderAttribute.resolve(EmptyMixData) as BorderDirectional; - expect(resolvedOnlyValue, onlyValue); - }); - - group('BorderDirectionalAttribute', () { - test('merge should combine two BorderDirectionalAttributes correctly', () { - const borderAttr1 = BorderDirectionalAttribute( - top: BorderSideAttribute(width: 5.0), - bottom: BorderSideAttribute(width: 10.0), - start: BorderSideAttribute(width: 15.0), - end: BorderSideAttribute(width: 20.0), - ); - const borderAttr2 = BorderDirectionalAttribute( - start: BorderSideAttribute(width: 25.0), - ); - - final mergedBorderAttr = borderAttr1.merge(borderAttr2); - - expect(mergedBorderAttr.top, borderAttr1.top); - expect(mergedBorderAttr.bottom, borderAttr1.bottom); - expect(mergedBorderAttr.start, borderAttr2.start); - expect(mergedBorderAttr.end, borderAttr1.end); + expect(resolvedBorder, isA()); + expect(resolvedBorder.top, border.top); + expect(resolvedBorder.bottom, border.bottom); + expect(resolvedBorder.start, border.left); + expect(resolvedBorder.end, border.right); }); - test('resolve should create a BorderDirectional with the correct values', - () { - const borderAttr = BorderDirectionalAttribute( - top: BorderSideAttribute(width: 5.0), - bottom: BorderSideAttribute(width: 10.0), - start: BorderSideAttribute(width: 15.0), - end: BorderSideAttribute(width: 20.0), - ); - - final resolvedBorder = borderAttr.resolve(EmptyMixData); - - expect(resolvedBorder.top, const BorderSide(width: 5.0)); - expect(resolvedBorder.bottom, const BorderSide(width: 10.0)); - expect(resolvedBorder.start, const BorderSide(width: 15.0)); - expect(resolvedBorder.end, const BorderSide(width: 20.0)); - }); + test('merge', () { + final borderAttribute1 = BoxBorderAttribute(border.toDto()); - test('Equality holds when properties are the same', () { - const attr1 = BorderDirectionalAttribute( - top: BorderSideAttribute(width: 5.0), - bottom: BorderSideAttribute(width: 10.0), - start: BorderSideAttribute(width: 15.0), - end: BorderSideAttribute(width: 20.0), - ); - const attr2 = BorderDirectionalAttribute( - top: BorderSideAttribute(width: 5.0), - bottom: BorderSideAttribute(width: 10.0), - start: BorderSideAttribute(width: 15.0), - end: BorderSideAttribute(width: 20.0), - ); - expect(attr1, attr2); - }); + final borderAttribute2 = BoxBorderAttribute(border2.toDto()); - test('Equality fails when properties are different', () { - const attr1 = BorderDirectionalAttribute( - top: BorderSideAttribute(width: 5.0), - bottom: BorderSideAttribute(width: 10.0), - start: BorderSideAttribute(width: 15.0), - end: BorderSideAttribute(width: 20.0), - ); - - const attr2 = BorderDirectionalAttribute( - top: BorderSideAttribute(width: 5.0), - bottom: BorderSideAttribute(width: 10.0), - start: BorderSideAttribute(width: 15.0), - end: BorderSideAttribute(width: 25.0), - ); - expect(attr1, isNot(attr2)); - }); + final mergedAttribute = borderAttribute1.merge(borderAttribute2); + + final resolvedBorder = mergedAttribute.resolve(EmptyMixData) as Border; - test('Matches Border constructors', () { - const allAttr = - BorderDirectionalAttribute.all(BorderSideAttribute(width: 10.0)); - const allValue = BorderDirectional( - bottom: BorderSide(width: 10.0), - end: BorderSide(width: 10.0), - start: BorderSide(width: 10.0), - top: BorderSide(width: 10.0), - ); - - final resolvedAllValue = allAttr.resolve(EmptyMixData); - expect(resolvedAllValue, allValue); - - const onlyAttr = BorderDirectionalAttribute( - top: BorderSideAttribute(width: 5.0), - start: BorderSideAttribute(width: 10.0), - end: BorderSideAttribute(width: 15.0), - bottom: BorderSideAttribute(width: 20.0), - ); - - const onlyValue = BorderDirectional( - top: BorderSide(width: 5.0), - start: BorderSide(width: 10.0), - end: BorderSide(width: 15.0), - bottom: BorderSide(width: 20.0), - ); - - final resolvedOnlyValue = onlyAttr.resolve(EmptyMixData); - - expect(resolvedOnlyValue, onlyValue); + expect(mergedAttribute, isA()); + expect(mergedAttribute.value, isA()); + expect(resolvedBorder, isA()); + expect(resolvedBorder.top, border2.top); + expect(resolvedBorder.bottom, border2.bottom); + expect(resolvedBorder.left, border2.left); + expect(resolvedBorder.right, border2.right); }); }); } diff --git a/test/src/attributes/border/border_dto_test.dart b/test/src/attributes/border/border_dto_test.dart new file mode 100644 index 000000000..aa2e75913 --- /dev/null +++ b/test/src/attributes/border/border_dto_test.dart @@ -0,0 +1,158 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('BoxBorderDto', () { + test('isDirectional', () { + const borderDto = BoxBorderDto( + top: BorderSideDto(width: 1.0), + bottom: BorderSideDto(width: 1.0), + left: BorderSideDto(width: 1.0), + right: BorderSideDto(width: 1.0), + ); + + expect(borderDto.isDirectional, false); + + const directionalDto = BoxBorderDto( + top: BorderSideDto(width: 1.0), + bottom: BorderSideDto(width: 1.0), + start: BorderSideDto(width: 1.0), + end: BorderSideDto(width: 1.0), + ); + + expect(directionalDto.isDirectional, true); + }); + + test('merge', () { + const borderDto1 = BoxBorderDto( + top: BorderSideDto(width: 1.0), + bottom: BorderSideDto(width: 1.0), + left: BorderSideDto(width: 1.0), + right: BorderSideDto(width: 1.0), + ); + + const borderDto2 = BoxBorderDto( + top: BorderSideDto(width: 2.0), + bottom: BorderSideDto(width: 2.0), + left: BorderSideDto(width: 2.0), + right: BorderSideDto(width: 2.0), + ); + + final merged = borderDto1.merge(borderDto2); + + expect(merged.top, borderDto2.top); + expect(merged.bottom, borderDto2.bottom); + expect(merged.left, borderDto2.left); + expect(merged.right, borderDto2.right); + }); + + // resolve + test('resolve() Border', () { + const borderDto = BoxBorderDto( + top: BorderSideDto(width: 5.0), + bottom: BorderSideDto(width: 10.0), + left: BorderSideDto(width: 15.0), + right: BorderSideDto(width: 20.0), + ); + + final resolvedBorder = borderDto.resolve(EmptyMixData) as Border; + + expect(resolvedBorder.top, const BorderSide(width: 5.0)); + expect(resolvedBorder.bottom, const BorderSide(width: 10.0)); + expect(resolvedBorder.left, const BorderSide(width: 15.0)); + expect(resolvedBorder.right, const BorderSide(width: 20.0)); + }); + + test('resolve() BorderDirectional', () { + const borderDto = BoxBorderDto( + top: BorderSideDto(width: 5.0), + bottom: BorderSideDto(width: 10.0), + start: BorderSideDto(width: 15.0), + end: BorderSideDto(width: 20.0), + ); + + final resolvedBorder = + borderDto.resolve(EmptyMixData) as BorderDirectional; + + expect(resolvedBorder.top, const BorderSide(width: 5.0)); + expect(resolvedBorder.bottom, const BorderSide(width: 10.0)); + expect(resolvedBorder.start, const BorderSide(width: 15.0)); + expect(resolvedBorder.end, const BorderSide(width: 20.0)); + }); + }); + + // BorderSideDto tests + group('BorderSideDto', () { + test('should correctly merge with another BorderSideDto', () { + const borderSideDto1 = BorderSideDto( + width: 1.0, + color: ColorDto(Colors.red), + style: BorderStyle.solid, + ); + + const borderSideDto2 = BorderSideDto( + width: 2.0, + color: ColorDto(Colors.blue), + style: BorderStyle.solid, + ); + + final merged = borderSideDto1.merge(borderSideDto2); + + expect(merged.width, borderSideDto2.width); + expect(merged.color, borderSideDto2.color); + expect(merged.style, borderSideDto2.style); + }); + + // copywith + test('copyWith should correctly copy the BorderSideDto', () { + const borderSideDto = BorderSideDto( + width: 1.0, + color: ColorDto(Colors.red), + style: BorderStyle.solid, + ); + + final copied = borderSideDto.copyWith( + width: 2.0, + color: const ColorDto(Colors.blue), + ); + + expect(copied.width, 2.0); + expect(copied.color, const ColorDto(Colors.blue)); + expect(copied.style, BorderStyle.solid); + }); + + // from + test('from should correctly create a BorderSideDto from a BorderSide', () { + const borderSide = BorderSide( + width: 1.0, + color: Colors.red, + style: BorderStyle.solid, + ); + + final borderSideDto = BorderSideDto.from(borderSide); + + expect(borderSideDto.width, borderSide.width); + expect(borderSideDto.color, ColorDto(borderSide.color)); + expect(borderSideDto.style, borderSide.style); + }); + + // resolve + test('resolve should correctly create a BorderSide from a BorderSideDto', + () { + const borderSideDto = BorderSideDto( + width: 1.0, + color: ColorDto(Colors.red), + style: BorderStyle.solid, + ); + + final resolved = borderSideDto.resolve(EmptyMixData); + + expect(resolved.width, borderSideDto.width); + expect(resolved.color, borderSideDto.color?.resolve(EmptyMixData)); + expect(resolved.style, borderSideDto.style); + }); + }); +} diff --git a/test/src/attributes/border/border_radius_attribute_test.dart b/test/src/attributes/border/border_radius_attribute_test.dart deleted file mode 100644 index a9a34caec..000000000 --- a/test/src/attributes/border/border_radius_attribute_test.dart +++ /dev/null @@ -1,424 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/mix.dart'; - -import '../../../helpers/testing_utils.dart'; - -void main() { - group('BorderRadiusAttribute', () { - test('BorderRadiusAttribute.all', () { - const attr = BorderRadiusAttribute.all(Radius.circular(5.0)); - expect(attr.topLeft, const Radius.circular(5.0)); - expect(attr.topRight, const Radius.circular(5.0)); - expect(attr.bottomLeft, const Radius.circular(5.0)); - expect(attr.bottomRight, const Radius.circular(5.0)); - }); - - test('BorderRadiusAttribute.horizontal', () { - const attr = BorderRadiusAttribute.horizontal( - left: Radius.circular(5.0), - right: Radius.circular(10.0), - ); - - expect(attr.topLeft, const Radius.circular(5.0)); - expect(attr.topRight, const Radius.circular(10.0)); - expect(attr.bottomLeft, const Radius.circular(5.0)); - expect(attr.bottomRight, const Radius.circular(10.0)); - }); - - test('BorderRadiusAttribute.vertical', () { - const attr = BorderRadiusAttribute.vertical( - top: Radius.circular(5.0), - bottom: Radius.circular(10.0), - ); - - expect(attr.topLeft, const Radius.circular(5.0)); - expect(attr.topRight, const Radius.circular(5.0)); - expect(attr.bottomLeft, const Radius.circular(10.0)); - expect(attr.bottomRight, const Radius.circular(10.0)); - }); - - test('BorderRadiusAttribute.circular', () { - final attr = BorderRadiusAttribute.circular(5.0); - expect(attr.topLeft, const Radius.circular(5.0)); - expect(attr.topRight, const Radius.circular(5.0)); - expect(attr.bottomLeft, const Radius.circular(5.0)); - expect(attr.bottomRight, const Radius.circular(5.0)); - }); - - test('merge returns merged object correctly', () { - const attr1 = BorderRadiusAttribute.horizontal( - left: Radius.circular(5.0), - right: Radius.circular(10.0), - ); - const attr2 = BorderRadiusAttribute.vertical( - top: Radius.circular(5.0), - bottom: Radius.circular(10.0), - ); - - final merged = attr1.merge(attr2); - - expect(merged.topLeft, attr2.topLeft); - expect(merged.topRight, attr2.topRight); - expect(merged.bottomLeft, attr2.bottomLeft); - expect(merged.bottomRight, attr2.bottomRight); - }); - - test('merge should combine two BorderRadiusAttributes correctly', () { - const borderRadius1 = BorderRadiusAttribute.all(Radius.circular(10)); - const borderRadius2 = BorderRadiusAttribute( - topLeft: Radius.circular(20), - ); - - final mergedBorderRadius = borderRadius1.merge(borderRadius2); - - expect(mergedBorderRadius.topLeft, const Radius.circular(20)); - expect(mergedBorderRadius.topRight, const Radius.circular(10)); - expect(mergedBorderRadius.bottomLeft, const Radius.circular(10)); - expect(mergedBorderRadius.bottomRight, const Radius.circular(10)); - }); - - test('resolve should create a BorderRadius with the correct values', () { - const borderRadius = BorderRadiusAttribute( - topLeft: Radius.circular(10), - topRight: Radius.circular(20), - bottomLeft: Radius.circular(30), - bottomRight: Radius.circular(40), - ); - - final resolvedBorderRadius = borderRadius.resolve(EmptyMixData); - - expect(resolvedBorderRadius.topLeft, const Radius.circular(10)); - expect(resolvedBorderRadius.topRight, const Radius.circular(20)); - expect(resolvedBorderRadius.bottomLeft, const Radius.circular(30)); - expect(resolvedBorderRadius.bottomRight, const Radius.circular(40)); - }); - - test('Equality holds when properties are the same', () { - const attr1 = BorderRadiusAttribute( - topLeft: Radius.circular(5.0), - topRight: Radius.circular(10.0), - bottomLeft: Radius.circular(15.0), - bottomRight: Radius.circular(20.0), - ); - const attr2 = BorderRadiusAttribute( - topLeft: Radius.circular(5.0), - topRight: Radius.circular(10.0), - bottomLeft: Radius.circular(15.0), - bottomRight: Radius.circular(20.0), - ); - expect(attr1, attr2); - }); - - test('Equality fails when properties are different', () { - const attr1 = BorderRadiusAttribute( - topLeft: Radius.circular(5.0), - topRight: Radius.circular(10.0), - bottomLeft: Radius.circular(15.0), - bottomRight: Radius.circular(20.0), - ); - - const attr2 = BorderRadiusAttribute( - topLeft: Radius.circular(5.0), - topRight: Radius.circular(10.0), - bottomLeft: Radius.circular(15.0), - bottomRight: Radius.circular(25.0), - ); - expect(attr1, isNot(attr2)); - }); - - test('Matches BorderRadius contructors', () { - const allAttr = BorderRadiusAttribute.all(Radius.circular(10.0)); - const allValue = BorderRadius.all(Radius.circular(10.0)); - - final resolvedAllValue = allAttr.resolve(EmptyMixData); - - expect(resolvedAllValue, allValue); - - const horizontalAttr = BorderRadiusAttribute.horizontal( - left: Radius.circular(5.0), - right: Radius.circular(10.0), - ); - - const horizontalValue = BorderRadius.horizontal( - left: Radius.circular(5.0), - right: Radius.circular(10.0), - ); - - final resolvedHorizontalValue = horizontalAttr.resolve(EmptyMixData); - - expect(resolvedHorizontalValue, horizontalValue); - - const verticalAttr = BorderRadiusAttribute.vertical( - top: Radius.circular(5.0), - bottom: Radius.circular(10.0), - ); - - const verticalValue = BorderRadius.vertical( - top: Radius.circular(5.0), - bottom: Radius.circular(10.0), - ); - - final resolvedVerticalValue = verticalAttr.resolve(EmptyMixData); - - expect(resolvedVerticalValue, verticalValue); - - final circularAttr = BorderRadiusAttribute.circular(5.0); - final circularValue = BorderRadius.circular(5.0); - - final resolvedCircularValue = circularAttr.resolve(EmptyMixData); - - expect(resolvedCircularValue, circularValue); - - const onlyAttr = BorderRadiusAttribute( - topLeft: Radius.circular(5.0), - topRight: Radius.circular(10.0), - bottomLeft: Radius.circular(15.0), - bottomRight: Radius.circular(20.0), - ); - - const onlyValue = BorderRadius.only( - topLeft: Radius.circular(5.0), - topRight: Radius.circular(10.0), - bottomLeft: Radius.circular(15.0), - bottomRight: Radius.circular(20.0), - ); - - final resolvedOnlyValue = onlyAttr.resolve(EmptyMixData); - - expect(resolvedOnlyValue, onlyValue); - }); - }); - - group('BorderRadiusDirectionalAttribute', () { - test('BorderRadiusDirectionalAttribute.all', () { - const attr = BorderRadiusDirectionalAttribute.all(Radius.circular(5.0)); - expect(attr.topStart, const Radius.circular(5.0)); - expect(attr.topEnd, const Radius.circular(5.0)); - expect(attr.bottomStart, const Radius.circular(5.0)); - expect(attr.bottomEnd, const Radius.circular(5.0)); - }); - - test('BorderRadiusDirectionalAttribute.horizontal', () { - const attr = BorderRadiusDirectionalAttribute.horizontal( - start: Radius.circular(5.0), - end: Radius.circular(10.0), - ); - - expect(attr.topStart, const Radius.circular(5.0)); - expect(attr.topEnd, const Radius.circular(10.0)); - expect(attr.bottomStart, const Radius.circular(5.0)); - expect(attr.bottomEnd, const Radius.circular(10.0)); - }); - - test('BorderRadiusDirectionalAttribute.vertical', () { - const attr = BorderRadiusDirectionalAttribute.vertical( - top: Radius.circular(5.0), - bottom: Radius.circular(10.0), - ); - - expect(attr.topStart, const Radius.circular(5.0)); - expect(attr.topEnd, const Radius.circular(5.0)); - expect(attr.bottomStart, const Radius.circular(10.0)); - expect(attr.bottomEnd, const Radius.circular(10.0)); - }); - - test('BorderRadiusDirectionalAttribute.circular', () { - final attr = BorderRadiusDirectionalAttribute.circular(5.0); - expect(attr.topStart, const Radius.circular(5.0)); - expect(attr.topEnd, const Radius.circular(5.0)); - expect(attr.bottomStart, const Radius.circular(5.0)); - expect(attr.bottomEnd, const Radius.circular(5.0)); - }); - - test('merge returns merged object correctly', () { - const attr1 = BorderRadiusDirectionalAttribute.horizontal( - start: Radius.circular(5.0), - end: Radius.circular(10.0), - ); - const attr2 = BorderRadiusDirectionalAttribute.vertical( - top: Radius.circular(5.0), - bottom: Radius.circular(10.0), - ); - - final merged = attr1.merge(attr2); - - expect(merged.topStart, attr2.topStart); - expect(merged.topEnd, attr2.topEnd); - expect(merged.bottomStart, attr2.bottomStart); - expect(merged.bottomEnd, attr2.bottomEnd); - }); - - test('merge should combine two BorderRadiusDirectionalAttributes correctly', - () { - const borderRadius1 = - BorderRadiusDirectionalAttribute.all(Radius.circular(10)); - const borderRadius2 = BorderRadiusDirectionalAttribute( - topStart: Radius.circular(20), - ); - - final mergedBorderRadius = borderRadius1.merge(borderRadius2); - - expect(mergedBorderRadius.topStart, const Radius.circular(20)); - expect(mergedBorderRadius.topEnd, const Radius.circular(10)); - expect(mergedBorderRadius.bottomStart, const Radius.circular(10)); - expect(mergedBorderRadius.bottomEnd, const Radius.circular(10)); - }); - - test('resolve should create a BorderRadius with the correct values', () { - const borderRadius = BorderRadiusDirectionalAttribute( - topStart: Radius.circular(10), - topEnd: Radius.circular(20), - bottomStart: Radius.circular(30), - bottomEnd: Radius.circular(40), - ); - - final resolvedBorderRadius = borderRadius.resolve(EmptyMixData); - - expect(resolvedBorderRadius.topStart, const Radius.circular(10)); - expect(resolvedBorderRadius.topEnd, const Radius.circular(20)); - expect(resolvedBorderRadius.bottomStart, const Radius.circular(30)); - expect(resolvedBorderRadius.bottomEnd, const Radius.circular(40)); - }); - - test('Equality holds when properties are the same', () { - const attr1 = BorderRadiusDirectionalAttribute( - topStart: Radius.circular(5.0), - topEnd: Radius.circular(10.0), - bottomStart: Radius.circular(15.0), - bottomEnd: Radius.circular(20.0), - ); - const attr2 = BorderRadiusDirectionalAttribute( - topStart: Radius.circular(5.0), - topEnd: Radius.circular(10.0), - bottomStart: Radius.circular(15.0), - bottomEnd: Radius.circular(20.0), - ); - expect(attr1, attr2); - }); - - test('Equality fails when properties are different', () { - const attr1 = BorderRadiusDirectionalAttribute( - topStart: Radius.circular(5.0), - topEnd: Radius.circular(10.0), - bottomStart: Radius.circular(15.0), - bottomEnd: Radius.circular(20.0), - ); - - const attr2 = BorderRadiusDirectionalAttribute( - topStart: Radius.circular(5.0), - topEnd: Radius.circular(10.0), - bottomStart: Radius.circular(15.0), - bottomEnd: Radius.circular(25.0), - ); - expect(attr1, isNot(attr2)); - }); - - test('Matches BorderRadius contructors', () { - const allAttr = - BorderRadiusDirectionalAttribute.all(Radius.circular(10.0)); - const allValue = BorderRadiusDirectional.all(Radius.circular(10.0)); - - final resolvedAllValue = allAttr.resolve(EmptyMixData); - - expect(resolvedAllValue, allValue); - - const horizontalAttr = BorderRadiusDirectionalAttribute.horizontal( - start: Radius.circular(5.0), - end: Radius.circular(10.0), - ); - - const horizontalValue = BorderRadiusDirectional.horizontal( - start: Radius.circular(5.0), - end: Radius.circular(10.0), - ); - - final resolvedHorizontalValue = horizontalAttr.resolve(EmptyMixData); - - expect(resolvedHorizontalValue, horizontalValue); - - const verticalAttr = BorderRadiusDirectionalAttribute.vertical( - top: Radius.circular(5.0), - bottom: Radius.circular(10.0), - ); - - const verticalValue = BorderRadiusDirectional.vertical( - top: Radius.circular(5.0), - bottom: Radius.circular(10.0), - ); - - final resolvedVerticalValue = verticalAttr.resolve(EmptyMixData); - - expect(resolvedVerticalValue, verticalValue); - - final circularAttr = BorderRadiusDirectionalAttribute.circular(5.0); - final circularValue = BorderRadiusDirectional.circular(5.0); - - final resolvedCircularValue = circularAttr.resolve(EmptyMixData); - - expect(resolvedCircularValue, circularValue); - - const onlyAttr = BorderRadiusDirectionalAttribute( - topStart: Radius.circular(5.0), - topEnd: Radius.circular(10.0), - bottomStart: Radius.circular(15.0), - bottomEnd: Radius.circular(20.0), - ); - - const onlyValue = BorderRadiusDirectional.only( - topStart: Radius.circular(5.0), - topEnd: Radius.circular(10.0), - bottomStart: Radius.circular(15.0), - bottomEnd: Radius.circular(20.0), - ); - - final resolvedOnlyValue = onlyAttr.resolve(EmptyMixData); - - expect(resolvedOnlyValue, onlyValue); - }); - }); - - group('BorderSideAttribute', () { - test('from constructor sets all values correctly', () { - final attr = BorderSideAttribute( - color: Colors.red.toAttribute(), - width: 1.0, - style: BorderStyle.solid); - expect(attr.color?.value, Colors.red); - expect(attr.width, 1.0); - expect(attr.style, BorderStyle.solid); - }); - test('resolve returns correct BorderSide', () { - final attr = BorderSideAttribute( - color: Colors.red.toAttribute(), - width: 1.0, - style: BorderStyle.solid); - final borderSide = attr.resolve(EmptyMixData); - expect(borderSide.color, Colors.red); - expect(borderSide.width, 1.0); - expect(borderSide.style, BorderStyle.solid); - }); - test('Equality holds when all attributes are the same', () { - final attr1 = BorderSideAttribute( - color: Colors.red.toAttribute(), - width: 1.0, - style: BorderStyle.solid); - final attr2 = BorderSideAttribute( - color: Colors.red.toAttribute(), - width: 1.0, - style: BorderStyle.solid); - expect(attr1, attr2); - }); - test('Equality fails when attributes are different', () { - final attr1 = BorderSideAttribute( - color: Colors.red.toAttribute(), - width: 1.0, - style: BorderStyle.solid); - final attr2 = BorderSideAttribute( - color: Colors.blue.toAttribute(), - width: 1.0, - style: BorderStyle.solid); - expect(attr1, isNot(attr2)); - }); - }); -} diff --git a/test/src/attributes/border/border_radius_dto_test.dart b/test/src/attributes/border/border_radius_dto_test.dart new file mode 100644 index 000000000..fde8b5816 --- /dev/null +++ b/test/src/attributes/border/border_radius_dto_test.dart @@ -0,0 +1,227 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('BorderRadiusGeometryDto', () { + test('merge returns merged object correctly', () { + const attr1 = BorderRadiusGeometryDto( + topLeft: Radius.circular(5.0), + topRight: Radius.circular(10.0), + bottomLeft: Radius.circular(15.0), + bottomRight: Radius.circular(20.0), + ); + + const attr2 = BorderRadiusGeometryDto( + topLeft: Radius.circular(5.0), + topRight: Radius.circular(10.0), + bottomLeft: Radius.circular(15.0), + bottomRight: Radius.circular(20.0), + ); + + final merged = attr1.merge(attr2); + + expect(merged.topLeft, attr2.topLeft); + expect(merged.topRight, attr2.topRight); + expect(merged.bottomLeft, attr2.bottomLeft); + expect(merged.bottomRight, attr2.bottomRight); + }); + + test('merge should combine two BorderRadiusGeometryDtos correctly', () { + const borderRadius1 = BorderRadiusGeometryDto( + bottomLeft: Radius.circular(10), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(30), + topRight: Radius.circular(40), + ); + const borderRadius2 = BorderRadiusGeometryDto( + topLeft: Radius.circular(20), + ); + + final mergedBorderRadius = borderRadius1.merge(borderRadius2); + + expect(mergedBorderRadius.topLeft, const Radius.circular(20)); + expect(mergedBorderRadius.topRight, const Radius.circular(40)); + expect(mergedBorderRadius.bottomLeft, const Radius.circular(10)); + expect(mergedBorderRadius.bottomRight, const Radius.circular(20)); + }); + + test('resolve should create a BorderRadius with the correct values', () { + const borderRadius = BorderRadiusGeometryDto( + topLeft: Radius.circular(10), + topRight: Radius.circular(20), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(40), + ); + + final resolvedBorderRadius = + borderRadius.resolve(EmptyMixData) as BorderRadius; + + expect(resolvedBorderRadius.topLeft, const Radius.circular(10)); + expect(resolvedBorderRadius.topRight, const Radius.circular(20)); + expect(resolvedBorderRadius.bottomLeft, const Radius.circular(30)); + expect(resolvedBorderRadius.bottomRight, const Radius.circular(40)); + }); + + test('Equality holds when properties are the same', () { + const attr1 = BorderRadiusGeometryDto( + topLeft: Radius.circular(5.0), + topRight: Radius.circular(10.0), + bottomLeft: Radius.circular(15.0), + bottomRight: Radius.circular(20.0), + ); + const attr2 = BorderRadiusGeometryDto( + topLeft: Radius.circular(5.0), + topRight: Radius.circular(10.0), + bottomLeft: Radius.circular(15.0), + bottomRight: Radius.circular(20.0), + ); + expect(attr1, attr2); + }); + + test('Equality fails when properties are different', () { + const attr1 = BorderRadiusGeometryDto( + topLeft: Radius.circular(5.0), + topRight: Radius.circular(10.0), + bottomLeft: Radius.circular(15.0), + bottomRight: Radius.circular(20.0), + ); + + const attr2 = BorderRadiusGeometryDto( + topLeft: Radius.circular(5.0), + topRight: Radius.circular(10.0), + bottomLeft: Radius.circular(15.0), + bottomRight: Radius.circular(25.0), + ); + expect(attr1, isNot(attr2)); + }); + }); + + group('BorderRadiusGeometryDto', () { + test('merge returns merged object correctly', () { + const attr1 = BorderRadiusGeometryDto( + topStart: Radius.circular(5.0), + topEnd: Radius.circular(10.0), + bottomStart: Radius.circular(15.0), + bottomEnd: Radius.circular(20.0), + ); + + const attr2 = BorderRadiusGeometryDto( + topStart: Radius.circular(5.0), + topEnd: Radius.circular(10.0), + bottomStart: Radius.circular(15.0), + bottomEnd: Radius.circular(20.0), + ); + + final merged = attr1.merge(attr2); + + expect(merged.topStart, attr2.topStart); + expect(merged.topEnd, attr2.topEnd); + expect(merged.bottomStart, attr2.bottomStart); + expect(merged.bottomEnd, attr2.bottomEnd); + }); + + test('merge should combine two BorderRadiusGeometryDtos correctly', () { + const borderRadius1 = BorderRadiusGeometryDto( + bottomEnd: Radius.circular(10), + topEnd: Radius.circular(10), + topStart: Radius.circular(10), + bottomStart: Radius.circular(10), + ); + const borderRadius2 = BorderRadiusGeometryDto( + topStart: Radius.circular(20), + ); + + final mergedBorderRadius = borderRadius1.merge(borderRadius2); + + expect(mergedBorderRadius.topStart, const Radius.circular(20)); + expect(mergedBorderRadius.topEnd, const Radius.circular(10)); + expect(mergedBorderRadius.bottomStart, const Radius.circular(10)); + expect(mergedBorderRadius.bottomEnd, const Radius.circular(10)); + }); + + test('resolve should create a BorderRadius with the correct values', () { + const borderRadius = BorderRadiusGeometryDto( + topStart: Radius.circular(10), + topEnd: Radius.circular(20), + bottomStart: Radius.circular(30), + bottomEnd: Radius.circular(40), + ); + + final resolvedBorderRadius = + borderRadius.resolve(EmptyMixData) as BorderRadiusDirectional; + + expect(resolvedBorderRadius.topStart, const Radius.circular(10)); + expect(resolvedBorderRadius.topEnd, const Radius.circular(20)); + expect(resolvedBorderRadius.bottomStart, const Radius.circular(30)); + expect(resolvedBorderRadius.bottomEnd, const Radius.circular(40)); + }); + + test('Equality holds when properties are the same', () { + const attr1 = BorderRadiusGeometryDto( + topStart: Radius.circular(5.0), + topEnd: Radius.circular(10.0), + bottomStart: Radius.circular(15.0), + bottomEnd: Radius.circular(20.0), + ); + const attr2 = BorderRadiusGeometryDto( + topStart: Radius.circular(5.0), + topEnd: Radius.circular(10.0), + bottomStart: Radius.circular(15.0), + bottomEnd: Radius.circular(20.0), + ); + expect(attr1, attr2); + }); + + test('Equality fails when properties are different', () { + const attr1 = BorderRadiusGeometryDto( + topStart: Radius.circular(5.0), + topEnd: Radius.circular(10.0), + bottomStart: Radius.circular(15.0), + bottomEnd: Radius.circular(20.0), + ); + + const attr2 = BorderRadiusGeometryDto( + topStart: Radius.circular(5.0), + topEnd: Radius.circular(10.0), + bottomStart: Radius.circular(15.0), + bottomEnd: Radius.circular(25.0), + ); + expect(attr1, isNot(attr2)); + }); + }); + + group('BorderSideDto', () { + test('from constructor sets all values correctly', () { + final attr = BorderSideDto( + color: Colors.red.toDto(), width: 1.0, style: BorderStyle.solid); + expect(attr.color?.value, Colors.red); + expect(attr.width, 1.0); + expect(attr.style, BorderStyle.solid); + }); + test('resolve returns correct BorderSide', () { + final attr = BorderSideDto( + color: Colors.red.toDto(), width: 1.0, style: BorderStyle.solid); + final borderSide = attr.resolve(EmptyMixData); + expect(borderSide.color, Colors.red); + expect(borderSide.width, 1.0); + expect(borderSide.style, BorderStyle.solid); + }); + test('Equality holds when all attributes are the same', () { + final attr1 = BorderSideDto( + color: Colors.red.toDto(), width: 1.0, style: BorderStyle.solid); + final attr2 = BorderSideDto( + color: Colors.red.toDto(), width: 1.0, style: BorderStyle.solid); + expect(attr1, attr2); + }); + test('Equality fails when attributes are different', () { + final attr1 = BorderSideDto( + color: Colors.red.toDto(), width: 1.0, style: BorderStyle.solid); + final attr2 = BorderSideDto( + color: Colors.blue.toDto(), width: 1.0, style: BorderStyle.solid); + expect(attr1, isNot(attr2)); + }); + }); +} diff --git a/test/src/attributes/border/border_radius_util_test.dart b/test/src/attributes/border/border_radius_util_test.dart new file mode 100644 index 000000000..304a5eedf --- /dev/null +++ b/test/src/attributes/border/border_radius_util_test.dart @@ -0,0 +1,209 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +void main() { + const borderRadius = + BorderRadiusGeometryUtility(BorderRadiusGeometryAttribute.new); + group('BorderRadiusUtility', () { + test('zero should return BorderRadiusAttribute with zero radius', () { + final result = borderRadius.zero(); + expect(result.topLeft, Radius.zero); + expect(result.topRight, Radius.zero); + expect(result.bottomLeft, Radius.zero); + expect(result.bottomRight, Radius.zero); + }); + + test('only should return BorderRadiusAttribute with specified radius', () { + final result = borderRadius.only( + topLeft: const Radius.circular(10), + topRight: const Radius.circular(20), + bottomLeft: const Radius.circular(30), + bottomRight: const Radius.circular(40), + ); + expect(result.topLeft, const Radius.circular(10)); + expect(result.topRight, const Radius.circular(20)); + expect(result.bottomLeft, const Radius.circular(30)); + expect(result.bottomRight, const Radius.circular(40)); + }); + + test('all should return BorderRadiusAttribute with specified radius', () { + final result = borderRadius.all.circular(10); + expect(result.topLeft, const Radius.circular(10)); + expect(result.topRight, const Radius.circular(10)); + expect(result.bottomLeft, const Radius.circular(10)); + expect(result.bottomRight, const Radius.circular(10)); + }); + + test( + 'vertical should return BorderRadiusAttribute with specified top and bottom radius', + () { + final resultTop = borderRadius.top.circular(10); + final resultBottom = borderRadius.bottom.circular(20); + expect(resultTop.topLeft, const Radius.circular(10)); + expect(resultTop.topRight, const Radius.circular(10)); + expect(resultBottom.bottomLeft, const Radius.circular(20)); + expect(resultBottom.bottomRight, const Radius.circular(20)); + }); + + test( + 'horizontal should return BorderRadiusAttribute with specified left and right radius', + () { + final resultLeft = borderRadius.left.circular(10); + final resultRight = borderRadius.right.circular(20); + expect(resultLeft.topLeft, const Radius.circular(10)); + expect(resultLeft.bottomLeft, const Radius.circular(10)); + expect(resultRight.topRight, const Radius.circular(20)); + expect(resultRight.bottomRight, const Radius.circular(20)); + }); + + test( + 'positional should return BorderRadiusAttribute with specified positional radius', + () { + final result = borderRadius(10, 20, 30, 40); + expect(result.topLeft, const Radius.circular(10)); + expect(result.topRight, const Radius.circular(20)); + expect(result.bottomLeft, const Radius.circular(30)); + expect(result.bottomRight, const Radius.circular(40)); + }); + + test('circular should return BorderRadiusAttribute with specified radius', + () { + final result = borderRadius(10); + expect(result.topLeft, const Radius.circular(10)); + expect(result.topRight, const Radius.circular(10)); + expect(result.bottomLeft, const Radius.circular(10)); + expect(result.bottomRight, const Radius.circular(10)); + }); + + // topLeft + test('topLeft should return BorderRadiusAttribute with specified radius', + () { + final result = borderRadius.topLeft.circular(10); + expect(result.topLeft, const Radius.circular(10)); + expect(result.topRight, isNull); + expect(result.bottomLeft, isNull); + expect(result.bottomRight, isNull); + }); + + // topRight + test('topRight should return BorderRadiusAttribute with specified radius', + () { + final result = borderRadius.topRight.circular(10); + expect(result.topLeft, isNull); + expect(result.topRight, const Radius.circular(10)); + expect(result.bottomLeft, isNull); + expect(result.bottomRight, isNull); + }); + + // bottomLeft + test('bottomLeft should return BorderRadiusAttribute with specified radius', + () { + final result = borderRadius.bottomLeft.circular(10); + expect(result.topLeft, isNull); + expect(result.topRight, isNull); + expect(result.bottomLeft, const Radius.circular(10)); + expect(result.bottomRight, isNull); + }); + + // bottomRight + test( + 'bottomRight should return BorderRadiusAttribute with specified radius', + () { + final result = borderRadius.bottomRight.circular(10); + expect(result.topLeft, isNull); + expect(result.topRight, isNull); + expect(result.bottomLeft, isNull); + expect(result.bottomRight, const Radius.circular(10)); + }); + }); + + group('BorderRadiusDirectionalUtility', () { + test('zero should return BorderRadiusDirectionalAttribute with zero radius', + () { + final result = borderRadius.directional.zero(); + expect(result.topStart, Radius.zero); + expect(result.topEnd, Radius.zero); + expect(result.bottomStart, Radius.zero); + expect(result.bottomEnd, Radius.zero); + }); + + test( + 'only should return BorderRadiusDirectionalAttribute with specified radius', + () { + final result = borderRadius.directional.only( + topStart: const Radius.circular(10), + topEnd: const Radius.circular(20), + bottomStart: const Radius.circular(30), + bottomEnd: const Radius.circular(40), + ); + expect(result.topStart, const Radius.circular(10)); + expect(result.topEnd, const Radius.circular(20)); + expect(result.bottomStart, const Radius.circular(30)); + expect(result.bottomEnd, const Radius.circular(40)); + }); + + test( + 'all should return BorderRadiusDirectionalAttribute with specified radius', + () { + final result = borderRadius.directional.all.circular(10); + expect(result.topStart, const Radius.circular(10)); + expect(result.topEnd, const Radius.circular(10)); + expect(result.bottomStart, const Radius.circular(10)); + expect(result.bottomEnd, const Radius.circular(10)); + }); + + test('zero should return BorderRadiusDirectionalAttribute with zero radius', + () { + final result = borderRadius.directional.zero(); + expect(result.topStart, Radius.zero); + expect(result.topEnd, Radius.zero); + expect(result.bottomStart, Radius.zero); + expect(result.bottomEnd, Radius.zero); + }); + + // topStart + test( + 'topStart should return BorderRadiusDirectionalAttribute with specified radius', + () { + final result = borderRadius.directional.topStart.circular(10); + expect(result.topStart, const Radius.circular(10)); + expect(result.topEnd, isNull); + expect(result.bottomStart, isNull); + expect(result.bottomEnd, isNull); + }); + + // topEnd + test( + 'topEnd should return BorderRadiusDirectionalAttribute with specified radius', + () { + final result = borderRadius.directional.topEnd.circular(10); + expect(result.topStart, isNull); + expect(result.topEnd, const Radius.circular(10)); + expect(result.bottomStart, isNull); + expect(result.bottomEnd, isNull); + }); + + // bottomStart + test( + 'bottomStart should return BorderRadiusDirectionalAttribute with specified radius', + () { + final result = borderRadius.directional.bottomStart.circular(10); + expect(result.topStart, isNull); + expect(result.topEnd, isNull); + expect(result.bottomStart, const Radius.circular(10)); + expect(result.bottomEnd, isNull); + }); + + // bottomEnd + test( + 'bottomEnd should return BorderRadiusDirectionalAttribute with specified radius', + () { + final result = borderRadius.directional.bottomEnd.circular(10); + expect(result.topStart, isNull); + expect(result.topEnd, isNull); + expect(result.bottomStart, isNull); + expect(result.bottomEnd, const Radius.circular(10)); + }); + }); +} diff --git a/test/src/attributes/border/border_util_test.dart b/test/src/attributes/border/border_util_test.dart new file mode 100644 index 000000000..a402b5805 --- /dev/null +++ b/test/src/attributes/border/border_util_test.dart @@ -0,0 +1,233 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +void main() { + group('BorderUtility', () { + const border = BoxBorderUtility(BoxBorderAttribute.new); + + test('border.top()', () { + final result = border.top( + color: Colors.red, + width: 10.0, + style: BorderStyle.solid, + strokeAlign: 0.5, + ); + + expect(result.top?.color?.value, Colors.red); + expect(result.top?.width, 10.0); + expect(result.top?.style, BorderStyle.solid); + expect(result.top?.strokeAlign, 0.5); + expect(result.right, null); + expect(result.bottom, null); + expect(result.left, null); + + final resultColor = border.top.color(Colors.yellow); + final resultWidth = border.top.width(20.0); + final resultStyle = border.top.style(BorderStyle.solid); + final resultStrokeAlign = border.top.strokeAlign(0.2); + + expect(resultColor.top?.color?.value, Colors.yellow); + expect(resultWidth.top?.width, 20.0); + expect(resultStyle.top?.style, BorderStyle.solid); + expect(resultStrokeAlign.top?.strokeAlign, 0.2); + }); + + test('border.bottom()', () { + final result = border.bottom( + color: Colors.red, + width: 10.0, + style: BorderStyle.solid, + strokeAlign: 0.5, + ); + + expect(result.bottom?.color?.value, Colors.red); + expect(result.bottom?.width, 10.0); + expect(result.bottom?.style, BorderStyle.solid); + expect(result.bottom?.strokeAlign, 0.5); + expect(result.right, null); + expect(result.top, null); + expect(result.left, null); + + final resultColor = border.bottom.color(Colors.yellow); + final resultWidth = border.bottom.width(20.0); + final resultStyle = border.bottom.style(BorderStyle.solid); + final resultStrokeAlign = border.bottom.strokeAlign(0.2); + + expect(resultColor.bottom?.color?.value, Colors.yellow); + expect(resultWidth.bottom?.width, 20.0); + expect(resultStyle.bottom?.style, BorderStyle.solid); + expect(resultStrokeAlign.bottom?.strokeAlign, 0.2); + }); + + test('border.left()', () { + final result = border.left( + color: Colors.red, + width: 10.0, + style: BorderStyle.solid, + strokeAlign: 0.5, + ); + expect(result.left?.color?.value, Colors.red); + expect(result.left?.width, 10.0); + expect(result.left?.style, BorderStyle.solid); + expect(result.left?.strokeAlign, 0.5); + expect(result.right, null); + expect(result.top, null); + expect(result.bottom, null); + + final resultColor = border.left.color(Colors.yellow); + final resultWidth = border.left.width(20.0); + final resultStyle = border.left.style(BorderStyle.solid); + final resultStrokeAlign = border.left.strokeAlign(0.2); + + expect(resultColor.left?.color?.value, Colors.yellow); + expect(resultWidth.left?.width, 20.0); + expect(resultStyle.left?.style, BorderStyle.solid); + expect(resultStrokeAlign.left?.strokeAlign, 0.2); + }); + + test('border.right()', () { + final result = border.right( + color: Colors.red, + width: 10.0, + style: BorderStyle.solid, + strokeAlign: 0.5, + ); + expect(result.right?.color?.value, Colors.red); + expect(result.right?.width, 10.0); + expect(result.right?.style, BorderStyle.solid); + expect(result.right?.strokeAlign, 0.5); + expect(result.left, null); + expect(result.top, null); + expect(result.bottom, null); + + final resultColor = border.right.color(Colors.yellow); + final resultWidth = border.right.width(20.0); + final resultStyle = border.right.style(BorderStyle.solid); + final resultStrokeAlign = border.right.strokeAlign(0.2); + + expect(resultColor.right?.color?.value, Colors.yellow); + expect(resultWidth.right?.width, 20.0); + expect(resultStyle.right?.style, BorderStyle.solid); + expect(resultStrokeAlign.right?.strokeAlign, 0.2); + }); + + test('border.horizontal()', () { + final result = border.horizontal( + color: Colors.blue, + width: 5.0, + style: BorderStyle.solid, + strokeAlign: 0.3, + ); + expect(result.top?.color?.value, Colors.blue); + expect(result.top?.width, 5.0); + expect(result.top?.style, BorderStyle.solid); + expect(result.top?.strokeAlign, 0.3); + expect(result.bottom?.color?.value, Colors.blue); + expect(result.bottom?.width, 5.0); + expect(result.bottom?.style, BorderStyle.solid); + expect(result.bottom?.strokeAlign, 0.3); + expect(result.left, null); + expect(result.right, null); + + final resultColor = border.horizontal.color(Colors.yellow); + final resultWidth = border.horizontal.width(20.0); + final resultStyle = border.horizontal.style(BorderStyle.solid); + final resultStrokeAlign = border.horizontal.strokeAlign(0.2); + + expect(resultColor.top?.color?.value, Colors.yellow); + expect(resultWidth.top?.width, 20.0); + expect(resultStyle.top?.style, BorderStyle.solid); + expect(resultStrokeAlign.top?.strokeAlign, 0.2); + + expect(resultColor.bottom?.color?.value, Colors.yellow); + expect(resultWidth.bottom?.width, 20.0); + expect(resultStyle.bottom?.style, BorderStyle.solid); + expect(resultStrokeAlign.bottom?.strokeAlign, 0.2); + }); + + test('border.vertical()', () { + final result = border.vertical( + color: Colors.green, + width: 7.0, + style: BorderStyle.solid, + strokeAlign: 0.2, + ); + expect(result.left?.color?.value, Colors.green); + expect(result.left?.width, 7.0); + expect(result.left?.style, BorderStyle.solid); + expect(result.left?.strokeAlign, 0.2); + expect(result.right?.color?.value, Colors.green); + expect(result.right?.width, 7.0); + expect(result.right?.style, BorderStyle.solid); + expect(result.right?.strokeAlign, 0.2); + expect(result.top, null); + expect(result.bottom, null); + + final resultColor = border.vertical.color(Colors.yellow); + final resultWidth = border.vertical.width(20.0); + final resultStyle = border.vertical.style(BorderStyle.solid); + final resultStrokeAlign = border.vertical.strokeAlign(0.2); + + expect(resultColor.left?.color?.value, Colors.yellow); + expect(resultWidth.left?.width, 20.0); + expect(resultStyle.left?.style, BorderStyle.solid); + expect(resultStrokeAlign.left?.strokeAlign, 0.2); + + expect(resultColor.right?.color?.value, Colors.yellow); + expect(resultWidth.right?.width, 20.0); + expect(resultStyle.right?.style, BorderStyle.solid); + expect(resultStrokeAlign.right?.strokeAlign, 0.2); + }); + + test('border.all()', () { + final result = border.all( + color: Colors.purple, + width: 3.0, + style: BorderStyle.solid, + strokeAlign: 0.1, + ); + expect(result.top?.color?.value, Colors.purple); + expect(result.top?.width, 3.0); + expect(result.top?.style, BorderStyle.solid); + expect(result.top?.strokeAlign, 0.1); + expect(result.bottom?.color?.value, Colors.purple); + expect(result.bottom?.width, 3.0); + expect(result.bottom?.style, BorderStyle.solid); + expect(result.bottom?.strokeAlign, 0.1); + expect(result.left?.color?.value, Colors.purple); + expect(result.left?.width, 3.0); + expect(result.left?.style, BorderStyle.solid); + expect(result.left?.strokeAlign, 0.1); + expect(result.right?.color?.value, Colors.purple); + expect(result.right?.width, 3.0); + expect(result.right?.style, BorderStyle.solid); + expect(result.right?.strokeAlign, 0.1); + + final resultColor = border.all.color(Colors.yellow); + final resultWidth = border.all.width(20.0); + final resultStyle = border.all.style(BorderStyle.solid); + final resultStrokeAlign = border.all.strokeAlign(0.2); + + expect(resultColor.top?.color?.value, Colors.yellow); + expect(resultWidth.top?.width, 20.0); + expect(resultStyle.top?.style, BorderStyle.solid); + expect(resultStrokeAlign.top?.strokeAlign, 0.2); + + expect(resultColor.bottom?.color?.value, Colors.yellow); + expect(resultWidth.bottom?.width, 20.0); + expect(resultStyle.bottom?.style, BorderStyle.solid); + expect(resultStrokeAlign.bottom?.strokeAlign, 0.2); + + expect(resultColor.left?.color?.value, Colors.yellow); + expect(resultWidth.left?.width, 20.0); + expect(resultStyle.left?.style, BorderStyle.solid); + expect(resultStrokeAlign.left?.strokeAlign, 0.2); + + expect(resultColor.right?.color?.value, Colors.yellow); + expect(resultWidth.right?.width, 20.0); + expect(resultStyle.right?.style, BorderStyle.solid); + expect(resultStrokeAlign.right?.strokeAlign, 0.2); + }); + }); +} diff --git a/test/src/attributes/color/color_dto_test.dart b/test/src/attributes/color/color_dto_test.dart new file mode 100644 index 000000000..47fc85fba --- /dev/null +++ b/test/src/attributes/color/color_dto_test.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/src/attributes/color/color_dto.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('ColorAttribute', () { + const colorList = [ + Colors.red, + Colors.green, + Colors.blue, + Colors.yellow, + Colors.orange, + Colors.purple, + Colors.pink, + Colors.black, + Colors.white, + Colors.grey, + ]; + + // ignore: avoid_function_literals_in_foreach_calls + return colorList.forEach((color) { + test('ColorAttribute.from resolves correctly', () { + final colorAttribute = ColorDto(color); + final resolvedValue = colorAttribute.resolve(EmptyMixData); + expect(resolvedValue, color); + }); + }); + }); +} diff --git a/test/src/attributes/color_attribute_test.dart b/test/src/attributes/color_attribute_test.dart deleted file mode 100644 index 9385ea298..000000000 --- a/test/src/attributes/color_attribute_test.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:mix/src/attributes/color_attribute.dart'; - -import '../../helpers/testing_utils.dart'; - -void main() { - const colorList = [ - Colors.red, - Colors.green, - Colors.blue, - Colors.yellow, - Colors.orange, - Colors.purple, - Colors.pink, - Colors.black, - Colors.white, - Colors.grey, - ]; - testScalarAttribute( - 'ColorAttribute', - (value) => ColorAttribute(value), - colorList, - ); - - testScalarAttribute( - 'ImageColorAttribute', - (value) => ImageColorAttribute(value), - colorList, - ); - - testScalarAttribute( - 'IconColorAttribute', - (value) => IconColorAttribute(value), - colorList, - ); -} diff --git a/test/src/attributes/constraints/constraints_attribute_test.dart b/test/src/attributes/constraints/constraints_attribute_test.dart new file mode 100644 index 000000000..bdd1fb07b --- /dev/null +++ b/test/src/attributes/constraints/constraints_attribute_test.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; +import 'package:mix/src/attributes/constraints/constraints_dto.dart'; + +import '../../../helpers/attribute_generator.dart'; +import '../../../helpers/testing_utils.dart'; + +void main() { + group('BoxConstraintsAttribute', () { + final constraints = RandomGenerator.boxConstraints(); + final boxConstraintsDto = BoxConstraintsDto.from(constraints); + test('constructor should set the value correctly', () { + final attribute = BoxConstraintsAttribute(boxConstraintsDto); + + expect(attribute.value, isA()); + expect(attribute.value, equals(boxConstraintsDto)); + }); + + test('merge should return the same attribute if other is null', () { + final attribute = BoxConstraintsAttribute(boxConstraintsDto); + final mergedAttribute = attribute.merge(null); + + expect(mergedAttribute, equals(attribute)); + }); + + test('merge should return the same attribute if value types are different', + () { + const constraints1 = BoxConstraintsDto(minWidth: 50, minHeight: 100); + const constraints2 = BoxConstraintsDto(minWidth: 100, minHeight: 200); + const attribute1 = BoxConstraintsAttribute(constraints1); + const attribute2 = BoxConstraintsAttribute(constraints2); + final mergedAttribute = attribute1.merge(attribute2); + + expect(mergedAttribute, equals(attribute2)); + }); + + test('merge should return a new attribute with merged value', () { + final constraints1 = + BoxConstraintsDto.from(RandomGenerator.boxConstraints()); + final constraints2 = + BoxConstraintsDto.from(RandomGenerator.boxConstraints()); + + final attribute1 = BoxConstraintsAttribute(constraints1); + final attribute2 = BoxConstraintsAttribute(constraints2); + final mergedAttribute = attribute1.merge(attribute2); + + expect(mergedAttribute.value, equals(constraints1.merge(constraints2))); + }); + + test('resolve should return the constraints value', () { + const constraints = BoxConstraintsDto( + minWidth: 50, minHeight: 100, maxWidth: 100, maxHeight: 200); + const attribute = BoxConstraintsAttribute(constraints); + + final resolvedConstraints = attribute.resolve(EmptyMixData); + + expect(resolvedConstraints.minWidth, 50); + expect(resolvedConstraints.minHeight, 100); + expect(resolvedConstraints.maxWidth, 100); + expect(resolvedConstraints.maxHeight, 200); + }); + + testWidgets('build', (widgetTester) async { + const constraints = BoxConstraintsDto(minWidth: 50, minHeight: 100); + const attribute = BoxConstraintsAttribute(constraints); + final widget = attribute.build(EmptyMixData, Container()); + + await widgetTester.pumpMaterialApp(widget); + + final finder = find.byWidget(widget); + + final constrainedBox = widgetTester.widget(finder); + + final boxConstraints = constrainedBox.constraints; + + expect(boxConstraints, isA()); + expect(boxConstraints.minWidth, 50); + expect(boxConstraints.minHeight, 100); + }); + }); +} diff --git a/test/src/attributes/constraints/constraints_dto_test.dart b/test/src/attributes/constraints/constraints_dto_test.dart new file mode 100644 index 000000000..c3e5635c5 --- /dev/null +++ b/test/src/attributes/constraints/constraints_dto_test.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/src/attributes/constraints/constraints_dto.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('BoxConstraintsDto', () { + test('from constructor sets all values correctly', () { + const constraints = BoxConstraints( + minWidth: 50, + maxWidth: 150, + minHeight: 100, + maxHeight: 200, + ); + final constraintsDto = BoxConstraintsDto.from(constraints); + + expect(constraints.minWidth, 50); + expect(constraints.maxWidth, 150); + expect(constraints.minHeight, 100); + expect(constraints.maxHeight, 200); + + expect(constraintsDto.minWidth, 50); + expect(constraintsDto.maxWidth, 150); + expect(constraintsDto.minHeight, 100); + expect(constraintsDto.maxHeight, 200); + }); + test('merge returns merged object correctly', () { + const constraints1 = BoxConstraintsDto(minWidth: 50, minHeight: 100); + const constraints2 = BoxConstraintsDto(minWidth: 60, minHeight: 110); + final merged = constraints1.merge(constraints2); + expect(merged.minWidth, 60); + expect(merged.minHeight, 110); + expect(merged.maxWidth, isNull); + expect(merged.maxHeight, isNull); + }); + test('resolve returns correct BoxConstraints with default values', () { + const constraints = BoxConstraintsDto(); + final resolved = constraints.resolve(EmptyMixData); + + expect(constraints, isA()); + expect(resolved, isA()); + + expect(constraints.minWidth, isNull); + expect(constraints.maxWidth, isNull); + expect(constraints.minHeight, isNull); + expect(constraints.maxHeight, isNull); + expect(resolved.minWidth, 0); + expect(resolved.maxWidth, double.infinity); + expect(resolved.minHeight, 0); + expect(resolved.maxHeight, double.infinity); + }); + test('resolve returns correct BoxConstraints with specific values', () { + const constraints = BoxConstraintsDto(minWidth: 50, minHeight: 100); + final resolved = constraints.resolve(EmptyMixData); + + expect(constraints, isA()); + expect(constraints.minWidth, 50); + expect(constraints.maxWidth, isNull); + expect(constraints.minHeight, 100); + + expect(resolved, isA()); + expect(resolved.minWidth, 50); + expect(resolved.maxWidth, double.infinity); + expect(resolved.minHeight, 100); + expect(resolved.maxHeight, double.infinity); + return const Placeholder(); + }); + test('Equality holds when all properties are the same', () { + const constraints1 = BoxConstraintsDto(minWidth: 50, minHeight: 100); + const constraints2 = BoxConstraintsDto(minWidth: 50, minHeight: 100); + expect(constraints1, constraints2); + }); + test('Equality fails when properties are different', () { + const constraints1 = BoxConstraintsDto(minWidth: 50, minHeight: 100); + const constraints2 = BoxConstraintsDto(minWidth: 60, minHeight: 100); + expect(constraints1, isNot(constraints2)); + }); + }); +} diff --git a/test/src/attributes/constraints/constraints_util_test.dart b/test/src/attributes/constraints/constraints_util_test.dart new file mode 100644 index 000000000..14c8479cb --- /dev/null +++ b/test/src/attributes/constraints/constraints_util_test.dart @@ -0,0 +1,56 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; +import 'package:mix/src/attributes/constraints/constraints_dto.dart'; + +void main() { + group('BoxConstraintsUtility', () { + const boxConstraints = BoxConstraintsUtility(BoxConstraintsAttribute.new); + test('boxConstraints()', () { + final result = boxConstraints( + minWidth: 50.0, + maxWidth: 150.0, + minHeight: 100.0, + maxHeight: 200.0, + ); + + expect(result, isA()); + expect(result.value, isA()); + expect(result.value.minWidth, 50.0); + expect(result.value.maxWidth, 150.0); + expect(result.value.minHeight, 100.0); + expect(result.value.maxHeight, 200.0); + }); + + test('minWidth()', () { + final result = minWidth(50.0); + expect(result.value.minWidth, 50.0); + expect(result.value.maxWidth, isNull); + expect(result.value.minHeight, isNull); + expect(result.value.maxHeight, isNull); + }); + + test('maxWidth()', () { + final result = maxWidth(150.0); + expect(result.value.minWidth, isNull); + expect(result.value.maxWidth, 150.0); + expect(result.value.minHeight, isNull); + expect(result.value.maxHeight, isNull); + }); + + test('minHeight()', () { + final result = minHeight(100.0); + expect(result.value.minWidth, isNull); + expect(result.value.maxWidth, isNull); + expect(result.value.minHeight, 100.0); + expect(result.value.maxHeight, isNull); + }); + + test('maxHeight()', () { + final result = maxHeight(200.0); + expect(result.value.minWidth, isNull); + expect(result.value.maxWidth, isNull); + expect(result.value.minHeight, isNull); + expect(result.value.maxHeight, 200.0); + }); + }); +} diff --git a/test/src/attributes/constraints_attribute_test.dart b/test/src/attributes/constraints_attribute_test.dart deleted file mode 100644 index 3ab037112..000000000 --- a/test/src/attributes/constraints_attribute_test.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/attributes/constraints_attribute.dart'; - -import '../../helpers/testing_utils.dart'; - -void main() { - group('BoxConstraintsAttribute', () { - test('from constructor sets all values correctly', () { - const constraints = BoxConstraints( - minWidth: 50, - maxWidth: 150, - minHeight: 100, - maxHeight: 200, - ); - final attr = constraints.toAttribute(); - expect(attr.minWidth, 50); - expect(attr.maxWidth, 150); - expect(attr.minHeight, 100); - expect(attr.maxHeight, 200); - }); - test('merge returns merged object correctly', () { - const attr1 = BoxConstraintsAttribute(minWidth: 50, minHeight: 100); - const attr2 = BoxConstraintsAttribute(minWidth: 60, minHeight: 110); - final merged = attr1.merge(attr2); - expect(merged.minWidth, 60); - expect(merged.minHeight, 110); - expect(merged.maxWidth, isNull); - expect(merged.maxHeight, isNull); - }); - test('resolve returns correct BoxConstraints with default values', () { - const attr = BoxConstraintsAttribute(); - final constraints = attr.resolve(EmptyMixData); - expect(constraints.minWidth, 0); - expect(constraints.maxWidth, double.infinity); - expect(constraints.minHeight, 0); - expect(constraints.maxHeight, double.infinity); - }); - test('resolve returns correct BoxConstraints with specific values', () { - const attr = BoxConstraintsAttribute(minWidth: 50, minHeight: 100); - final constraints = attr.resolve(EmptyMixData); - expect(constraints.minWidth, 50); - expect(constraints.maxWidth, double.infinity); - expect(constraints.minHeight, 100); - expect(constraints.maxHeight, double.infinity); - return const Placeholder(); - }); - test('Equality holds when all properties are the same', () { - const attr1 = BoxConstraintsAttribute(minWidth: 50, minHeight: 100); - const attr2 = BoxConstraintsAttribute(minWidth: 50, minHeight: 100); - expect(attr1, attr2); - }); - test('Equality fails when properties are different', () { - const attr1 = BoxConstraintsAttribute(minWidth: 50, minHeight: 100); - const attr2 = BoxConstraintsAttribute(minWidth: 60, minHeight: 100); - expect(attr1, isNot(attr2)); - }); - }); -} diff --git a/test/src/attributes/decoration/decoration_attribute_test.dart b/test/src/attributes/decoration/decoration_attribute_test.dart new file mode 100644 index 000000000..cc9a5180b --- /dev/null +++ b/test/src/attributes/decoration/decoration_attribute_test.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; +import 'package:mix/src/attributes/decoration/decoration_dto.dart'; + +import '../../../helpers/attribute_generator.dart'; +import '../../../helpers/testing_utils.dart'; + +void main() { + group('DecorationAttribute', () { + final border = RandomGenerator.border(); + final boxDecorationDto = BoxDecorationDto( + color: const ColorDto(Colors.red), + shape: BoxShape.circle, + border: BoxBorderDto.from(border), + ); + test('constructor should set the value correctly', () { + final attribute = DecorationAttribute(boxDecorationDto); + + expect(attribute.value, isA()); + expect(attribute.value, equals(boxDecorationDto)); + }); + + test('merge should return the same attribute if other is null', () { + final attribute = DecorationAttribute(boxDecorationDto); + final mergedAttribute = attribute.merge(null); + + expect(mergedAttribute, equals(attribute)); + }); + + test('merge should return the same attribute if value types are different', + () { + const decoration1 = BoxDecorationDto(color: ColorDto(Colors.red)); + const decoration2 = BoxDecorationDto(color: ColorDto(Colors.blue)); + const attribute1 = DecorationAttribute(decoration1); + const attribute2 = DecorationAttribute(decoration2); + final mergedAttribute = attribute1.merge(attribute2); + + expect(mergedAttribute, equals(attribute2)); + }); + + test('merge should return a new attribute with merged value', () { + final decoration1 = + BoxDecorationDto.from(RandomGenerator.boxDecoration()); + final decoration2 = + BoxDecorationDto.from(RandomGenerator.boxDecoration()); + + final attribute1 = DecorationAttribute(decoration1); + final attribute2 = DecorationAttribute(decoration2); + final mergedAttribute = attribute1.merge(attribute2); + + expect(mergedAttribute.value, equals(decoration1.merge(decoration2))); + }); + + test('resolve should return the decoration value', () { + const decoration = BoxDecorationDto(color: ColorDto(Colors.red)); + const attribute = DecorationAttribute(decoration); + final resolvedDecoration = attribute.resolve(EmptyMixData); + + expect( + resolvedDecoration, + equals( + const BoxDecoration(color: Colors.red), + )); + }); + + testWidgets('build', (widgetTester) async { + const decoration = BoxDecorationDto(color: ColorDto(Colors.red)); + const attribute = DecorationAttribute(decoration); + final widget = attribute.build(EmptyMixData, Container()); + + await widgetTester.pumpWidget(widget); + final decorationBox = + widgetTester.widget(find.byType(DecoratedBox)); + expect( + decorationBox.decoration, equals(decoration.resolve(EmptyMixData))); + }); + }); +} diff --git a/test/src/attributes/decoration/decoration_dto_test.dart b/test/src/attributes/decoration/decoration_dto_test.dart new file mode 100644 index 000000000..31a37fcb3 --- /dev/null +++ b/test/src/attributes/decoration/decoration_dto_test.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; +import 'package:mix/src/attributes/decoration/decoration_dto.dart'; +import 'package:mix/src/attributes/gradient/gradient_dto.dart'; + +import '../../../helpers/attribute_generator.dart'; +import '../../../helpers/testing_utils.dart'; + +void main() { + const linearGradient = LinearGradient( + colors: Colors.accents, + ); + + final linearGradientDto = LinearGradientDto( + colors: Colors.accents.map(ColorDto.new).toList(), + ); + + group('BoxDecorationDto', () { + test('merge returns merged object correctly', () { + final decoration1 = BoxDecorationDto(color: Colors.red.toDto()); + final decoration2 = BoxDecorationDto(gradient: linearGradientDto); + final merged = decoration1.merge(decoration2); + expect(merged.color, decoration1.color); + expect(merged.gradient, decoration2.gradient); + }); + test('resolve returns correct BoxDecoration with default values', () { + const attr = BoxDecorationDto(); + final decoration = attr.resolve(EmptyMixData); + expect(decoration, const BoxDecoration()); + return const Placeholder(); + }); + test('resolve returns correct BoxDecoration with specific values', () { + final attr = BoxDecorationDto( + color: Colors.red.toDto(), gradient: linearGradientDto); + final decoration = attr.resolve(EmptyMixData); + expect(decoration.color, Colors.red); + expect(decoration.gradient, linearGradient); + return const Placeholder(); + }); + test('Equality holds when all properties are the same', () { + final decoration1 = BoxDecorationDto(color: Colors.red.toDto()); + final decoration2 = BoxDecorationDto(color: Colors.red.toDto()); + expect(decoration1, decoration2); + }); + test('Equality fails when properties are different', () { + final decoration1 = BoxDecorationDto(color: Colors.red.toDto()); + final decoration2 = BoxDecorationDto(color: Colors.blue.toDto()); + expect(decoration1, isNot(decoration2)); + }); + }); + + group('ShapeDecorationDto', () { + test('merge returns merged object correctly', () { + final decoration1 = ShapeDecorationDto(color: Colors.red.toDto()); + final decoration2 = ShapeDecorationDto(gradient: linearGradientDto); + final merged = decoration1.merge(decoration2); + expect(merged.color, decoration1.color); + expect(merged.gradient, decoration2.gradient); + }); + test('resolve returns correct ShapeDecoration with default values', () { + const shapeDecoration = ShapeDecoration(shape: CircleBorder()); + const attr = ShapeDecorationDto(shape: CircleBorder()); + + final decoration = attr.resolve(EmptyMixData); + expect(decoration, shapeDecoration); + }); + test('resolve returns correct ShapeDecoration with specific values', () { + final boxShadow = RandomGenerator.boxShadow(); + + final decoration1 = ShapeDecorationDto( + gradient: linearGradientDto, + shape: const CircleBorder(), + shadows: [boxShadow.toDto()], + ); + + final resolvedValue = decoration1.resolve(EmptyMixData); + + expect(decoration1.gradient, linearGradientDto); + expect(decoration1.color, isNull); + expect(decoration1.shape, const CircleBorder()); + expect(decoration1.shadows, [boxShadow.toDto()]); + + expect(resolvedValue.gradient, linearGradient); + expect(resolvedValue.color, isNull); + expect(resolvedValue.shape, const CircleBorder()); + expect(resolvedValue.shadows, [boxShadow]); + }); + test('Equality holds when all properties are the same', () { + final decoration1 = ShapeDecorationDto(color: Colors.red.toDto()); + final decoration2 = ShapeDecorationDto(color: Colors.red.toDto()); + expect(decoration1, decoration2); + }); + test('Equality fails when properties are different', () { + final decoration1 = ShapeDecorationDto(color: Colors.red.toDto()); + final decoration2 = ShapeDecorationDto(color: Colors.blue.toDto()); + expect(decoration1, isNot(decoration2)); + }); + }); +} diff --git a/test/src/attributes/decoration/decoration_util_test.dart b/test/src/attributes/decoration/decoration_util_test.dart new file mode 100644 index 000000000..f2d983f59 --- /dev/null +++ b/test/src/attributes/decoration/decoration_util_test.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; +import 'package:mix/src/attributes/decoration/decoration_dto.dart'; +import 'package:mix/src/attributes/gradient/gradient_dto.dart'; + +import '../../../helpers/attribute_generator.dart'; +import '../../../helpers/testing_utils.dart'; + +void main() { + const boxDecoration = BoxDecorationUtility(UtilityTestAttribute.new); + + group('BoxDecorationUtility', () { + test('call', () { + final refBoxDecoration = RandomGenerator.boxDecoration(); + + final result = boxDecoration( + border: refBoxDecoration.border, + borderRadius: refBoxDecoration.borderRadius, + boxShadow: refBoxDecoration.boxShadow, + color: refBoxDecoration.color, + gradient: refBoxDecoration.gradient, + shape: refBoxDecoration.shape, + ); + + expect(result.value, equals(BoxDecorationDto.from(refBoxDecoration))); + + expect(result.value, isA()); + expect(result.value.border, isA()); + expect(result.value.borderRadius, isA()); + expect(result.value.boxShadow, isA>()); + expect(result.value.color, isA()); + expect(result.value.gradient, isA()); + expect(result.value.shape, isA()); + + expect(result.value.border, + equals(BoxBorderDto.from(refBoxDecoration.border!))); + expect(result.value.borderRadius, + equals(BorderRadiusGeometryDto.from(refBoxDecoration.borderRadius!))); + expect(result.value.boxShadow, + equals(refBoxDecoration.boxShadow?.map((e) => BoxShadowDto.from(e)))); + expect(result.value.color, equals(ColorDto(refBoxDecoration.color!))); + expect(result.value.gradient, + equals(GradientDto.from(refBoxDecoration.gradient!))); + expect(result.value.shape, equals(refBoxDecoration.shape)); + }); + test('color setting', () { + final result = boxDecoration(color: Colors.red); + expect(result.value.color, equals(Colors.red.toDto())); + }); + + test('shape setting', () { + final result = boxDecoration.shape.circle(); + expect(result.value.shape, equals(BoxShape.circle)); + }); + + test('border setting', () { + final result = boxDecoration.border.all(color: Colors.red, width: 2.0); + expect(result.value.border?.resolve(EmptyMixData), + equals(Border.all(color: Colors.red, width: 2.0))); + }); + + test('borderRadius setting', () { + final result = boxDecoration.borderRadius(10.0); + expect(result.value.borderRadius?.resolve(EmptyMixData), + equals(BorderRadius.circular(10.0))); + }); + + test('gradient setting', () { + const gradient = LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [Colors.red, Colors.blue], + ); + final result = boxDecoration.gradient.as(gradient); + expect(result.value.gradient, equals(gradient.toDto())); + }); + + test('boxShadow setting', () { + final boxShadow = [ + const BoxShadow( + color: Colors.black, + blurRadius: 10.0, + offset: Offset(5.0, 5.0), + ) + ]; + final result = boxDecoration.boxShadow(boxShadow); + expect(result.value.boxShadow, equals(boxShadow.toDto())); + }); + + test('elevation setting', () { + final result = boxDecoration.elevation(9); + final boxShadows = + result.value.boxShadow?.map((e) => e.resolve(EmptyMixData)); + expect(boxShadows, equals(kElevationToShadow[9]!)); + }); + }); +} diff --git a/test/src/attributes/decoration_attribute_test.dart b/test/src/attributes/decoration_attribute_test.dart deleted file mode 100644 index 159c40cf7..000000000 --- a/test/src/attributes/decoration_attribute_test.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/mix.dart'; - -import '../../helpers/testing_utils.dart'; - -void main() { - const linearGradient = LinearGradient(colors: Colors.accents); - const gradientAttribute = GradientAttribute(linearGradient); - group('BoxDecorationAttribute', () { - test('merge returns merged object correctly', () { - final attr1 = BoxDecorationAttribute(color: Colors.red.toAttribute()); - const attr2 = BoxDecorationAttribute(gradient: gradientAttribute); - final merged = attr1.merge(attr2); - expect(merged.color, attr1.color); - expect(merged.gradient, attr2.gradient); - }); - test('resolve returns correct BoxDecoration with default values', () { - const attr = BoxDecorationAttribute(); - final decoration = attr.resolve(EmptyMixData); - expect(decoration, const BoxDecoration()); - return const Placeholder(); - }); - test('resolve returns correct BoxDecoration with specific values', () { - final attr = BoxDecorationAttribute( - color: Colors.red.toAttribute(), gradient: gradientAttribute); - final decoration = attr.resolve(EmptyMixData); - expect(decoration.color, Colors.red); - expect(decoration.gradient, linearGradient); - return const Placeholder(); - }); - test('Equality holds when all properties are the same', () { - final attr1 = BoxDecorationAttribute(color: Colors.red.toAttribute()); - final attr2 = BoxDecorationAttribute(color: Colors.red.toAttribute()); - expect(attr1, attr2); - }); - test('Equality fails when properties are different', () { - final attr1 = BoxDecorationAttribute(color: Colors.red.toAttribute()); - final attr2 = BoxDecorationAttribute(color: Colors.blue.toAttribute()); - expect(attr1, isNot(attr2)); - }); - }); - - group('ShapeDecorationAttribute', () { - test('merge returns merged object correctly', () { - final attr1 = ShapeDecorationAttribute(color: Colors.red.toAttribute()); - const attr2 = ShapeDecorationAttribute(gradient: gradientAttribute); - final merged = attr1.merge(attr2); - expect(merged.color, attr1.color); - expect(merged.gradient, attr2.gradient); - }); - test('resolve returns correct ShapeDecoration with default values', () { - const shapeDecoration = ShapeDecoration(shape: CircleBorder()); - const attr = ShapeDecorationAttribute(shape: CircleBorder()); - - final decoration = attr.resolve(EmptyMixData); - expect(decoration, shapeDecoration); - }); - test('resolve returns correct ShapeDecoration with specific values', () { - final attr1 = ShapeDecorationAttribute( - gradient: linearGradient.toAttribute(), - ); - - final attr2 = ShapeDecorationAttribute( - color: Colors.red.toAttribute(), - ); - final decoration1 = attr1.resolve(EmptyMixData); - final decoration2 = attr2.resolve(EmptyMixData); - - expect(decoration1.gradient, linearGradient); - expect(decoration2.color, Colors.red); - }); - test('Equality holds when all properties are the same', () { - final attr1 = ShapeDecorationAttribute(color: Colors.red.toAttribute()); - final attr2 = ShapeDecorationAttribute(color: Colors.red.toAttribute()); - expect(attr1, attr2); - }); - test('Equality fails when properties are different', () { - final attr1 = ShapeDecorationAttribute(color: Colors.red.toAttribute()); - final attr2 = ShapeDecorationAttribute(color: Colors.blue.toAttribute()); - expect(attr1, isNot(attr2)); - }); - }); -} diff --git a/test/src/attributes/edge_insets_attribute_test.dart b/test/src/attributes/edge_insets_attribute_test.dart deleted file mode 100644 index 118f35386..000000000 --- a/test/src/attributes/edge_insets_attribute_test.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/attributes/edge_insets_attribute.dart'; - -import '../../helpers/testing_utils.dart'; - -void main() { - group('EdgeInsetsAttribute', () { - test('resolves to EdgeInsets.only with correct values', () { - const attribute = EdgeInsetsAttribute( - top: 10, - bottom: 20, - left: 30, - right: 40, - ); - - expect( - attribute.resolve(EmptyMixData), - const EdgeInsets.only( - left: 30, - top: 10, - right: 40, - bottom: 20, - )); - }); - - test('merges correctly with another EdgeInsetsAttribute', () { - const attribute1 = EdgeInsetsAttribute( - top: 10, - bottom: 20, - left: 30, - right: 40, - ); - const attribute2 = EdgeInsetsAttribute( - top: 5, - bottom: 15, - left: 25, - right: 35, - ); - final mergedAttribute = attribute1.merge(attribute2); - expect( - mergedAttribute, - const EdgeInsetsAttribute( - top: 5, - bottom: 15, - left: 25, - right: 35, - )); - }); - }); - - group('EdgeInsetsDirectionalAttribute', () { - test('resolves to EdgeInsetsDirectional.only with correct values', () { - const attribute = EdgeInsetsDirectionalAttribute( - top: 10, - bottom: 20, - start: 30, - end: 40, - ); - - expect( - attribute.resolve(EmptyMixData), - const EdgeInsetsDirectional.only( - start: 30, - top: 10, - end: 40, - bottom: 20, - )); - }); - - test('merges correctly with another EdgeInsetsDirectionalAttribute', () { - const attribute1 = EdgeInsetsDirectionalAttribute( - top: 10, - bottom: 20, - start: 30, - end: 40, - ); - const attribute2 = EdgeInsetsDirectionalAttribute( - top: 5, - bottom: 15, - start: 25, - end: 35, - ); - final mergedAttribute = attribute1.merge(attribute2); - expect( - mergedAttribute, - const EdgeInsetsDirectionalAttribute( - top: 5, - bottom: 15, - start: 25, - end: 35, - )); - }); - }); -} diff --git a/test/src/attributes/gradient/gradient_dto_test.dart b/test/src/attributes/gradient/gradient_dto_test.dart new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/test/src/attributes/gradient/gradient_dto_test.dart @@ -0,0 +1 @@ + diff --git a/test/src/attributes/gradient/gradient_util_test.dart b/test/src/attributes/gradient/gradient_util_test.dart new file mode 100644 index 000000000..0fed59b58 --- /dev/null +++ b/test/src/attributes/gradient/gradient_util_test.dart @@ -0,0 +1,568 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; +import 'package:mix/src/attributes/gradient/gradient_dto.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('GradientUtility', () { + const utility = GradientUtility.selfBuilder; + test('GradientUtility.from for RadialGradient', () { + const gradient = RadialGradient(colors: []); + final attribute = utility.as(gradient); + + expect(attribute, isA()); + expect(attribute.value, isA()); + expect(attribute.resolve(EmptyMixData), isA()); + }); + + test('GradientUtility.from for LinearGradient', () { + const gradient = LinearGradient(colors: []); + final attribute = utility.as(gradient); + + expect(attribute, isA()); + expect(attribute.value, isA()); + expect(attribute.resolve(EmptyMixData), isA()); + }); + + test('GradientUtility.from for SweepGradient', () { + const gradient = SweepGradient(colors: []); + final attribute = utility.as(gradient); + + expect(attribute, isA()); + expect(attribute.value, isA()); + expect(attribute.resolve(EmptyMixData), isA()); + }); + }); + + group('RadialGradientUtility', () { + const radialUtility = RadialGradientUtility.selfBuilder; + + test('.from for RadialGradient', () { + const gradient = RadialGradient(colors: []); + final attribute = radialUtility.as(gradient); + + final resolvedGradient = attribute.resolve(EmptyMixData); + + expect(attribute, isA()); + expect(resolvedGradient, isA()); + expect(attribute.value, isA()); + }); + + test('.call', () { + final colors = [Colors.red, Colors.blue]; + final stops = [0.0, 0.5]; + const center = Alignment.center; + const radius = 20.0; + const focal = Alignment.center; + const focalRadius = 10.0; + const tileMode = TileMode.clamp; + const transform = GradientRotation(0.0); + + final attribute = radialUtility( + colors: colors, + stops: stops, + center: center, + radius: radius, + focal: focal, + focalRadius: focalRadius, + tileMode: tileMode, + transform: transform, + ); + + expect(attribute, isA()); + expect(attribute.value, isA()); + expect(attribute.resolve(EmptyMixData), isA()); + }); + + // colors + test('.colors', () { + final colors = [Colors.red, Colors.blue]; + final colorsDto = colors.map(ColorDto.new).toList(); + + final attribute = radialUtility(colors: colors); + + final resolvedGradient = attribute.resolve(EmptyMixData); + final dto = attribute.value as RadialGradientDto; + + expect(attribute, isA()); + expect(dto.colors, colorsDto); + expect(resolvedGradient.colors, colors); + }); + + // stops + test('.stops', () { + final stops = [0.0, 0.5]; + final attribute = radialUtility(stops: stops); + + final resolvedGradient = attribute.resolve(EmptyMixData); + final dto = attribute.value as RadialGradientDto; + + expect(dto.stops, stops); + expect(resolvedGradient.stops, stops); + }); + + // center + test('.center', () { + const center = Alignment.center; + final attribute = radialUtility(center: center); + final attributeFn = radialUtility.center.center(); + + final resolvedGradient = + attribute.resolve(EmptyMixData) as RadialGradient; + final dto = attribute.value as RadialGradientDto; + + expect(attribute, attributeFn); + expect(dto.center, center); + expect(resolvedGradient.center, center); + }); + + // radius + test('.radius', () { + const radius = 20.0; + final attribute = radialUtility(radius: radius); + + final resolvedGradient = + attribute.resolve(EmptyMixData) as RadialGradient; + final dto = attribute.value as RadialGradientDto; + + expect(dto.radius, radius); + expect(resolvedGradient.radius, radius); + }); + + // focal + test('.focal', () { + const focal = Alignment.center; + final attribute = radialUtility.focal.builder(focal); + final attributeFn = radialUtility.focal.center(); + + final resolvedGradient = + attribute.resolve(EmptyMixData) as RadialGradient; + final dto = attribute.value as RadialGradientDto; + + expect(attribute, attributeFn); + expect(dto.focal, focal); + expect(resolvedGradient.focal, focal); + }); + + // focalRadius + test('.focalRadius', () { + const focalRadius = 10.0; + final attribute = radialUtility(focalRadius: focalRadius); + + final resolvedGradient = + attribute.resolve(EmptyMixData) as RadialGradient; + final dto = attribute.value as RadialGradientDto; + + expect(dto.focalRadius, focalRadius); + expect(resolvedGradient.focalRadius, focalRadius); + }); + + // tileMode + + test('.tileMode', () { + const tileMode = TileMode.clamp; + final attribute = radialUtility(tileMode: tileMode); + final attributeFn = radialUtility.tileMode.clamp(); + + final resolvedGradient = + attribute.resolve(EmptyMixData) as RadialGradient; + final dto = attribute.value as RadialGradientDto; + + expect(attribute, attributeFn); + expect(dto.tileMode, tileMode); + expect(resolvedGradient.tileMode, tileMode); + }); + + // transform + test('.transform', () { + const transform = GradientRotation(0.0); + final attribute = radialUtility(transform: transform); + + final resolvedGradient = + attribute.resolve(EmptyMixData) as RadialGradient; + final dto = attribute.value as RadialGradientDto; + + expect(dto.transform, transform); + expect(resolvedGradient.transform, transform); + }); + + // resolve + test('.resolve', () { + final colors = [Colors.red, Colors.blue]; + final stops = [0.0, 0.5]; + const center = Alignment.center; + const radius = 20.0; + const focal = Alignment.center; + const focalRadius = 10.0; + const tileMode = TileMode.clamp; + const transform = GradientRotation(0.0); + + final attribute = radialUtility( + colors: colors, + stops: stops, + center: center, + radius: radius, + focal: focal, + focalRadius: focalRadius, + tileMode: tileMode, + transform: transform, + ); + + final resolvedGradient = + attribute.resolve(EmptyMixData) as RadialGradient; + final dto = attribute.value as RadialGradientDto; + + expect(dto.colors, colors.map(ColorDto.new).toList()); + expect(dto.stops, stops); + expect(dto.center, center); + expect(dto.radius, radius); + expect(dto.focal, focal); + expect(dto.focalRadius, focalRadius); + expect(dto.tileMode, tileMode); + expect(dto.transform, transform); + + expect(resolvedGradient, isA()); + expect(resolvedGradient.colors, colors); + expect(resolvedGradient.stops, stops); + expect(resolvedGradient.center, center); + expect(resolvedGradient.radius, radius); + expect(resolvedGradient.focal, focal); + expect(resolvedGradient.focalRadius, focalRadius); + expect(resolvedGradient.tileMode, tileMode); + expect(resolvedGradient.transform, transform); + }); + }); + + // LinearGradientUtility + group('LinearGradientUtility', () { + const linearUtility = LinearGradientUtility.selfBuilder; + + test('.from for LinearGradient', () { + const gradient = LinearGradient(colors: []); + final attribute = linearUtility.as(gradient); + + final resolvedGradient = attribute.resolve(EmptyMixData); + final dto = attribute.value as LinearGradientDto; + + expect(attribute, isA()); + expect(resolvedGradient, isA()); + expect(dto, isA()); + }); + + test('.call', () { + final colors = [Colors.red, Colors.blue]; + final stops = [0.0, 0.5]; + const begin = Alignment.centerLeft; + const end = Alignment.centerRight; + const tileMode = TileMode.clamp; + const transform = GradientRotation(0.0); + + final attribute = linearUtility( + colors: colors, + stops: stops, + begin: begin, + end: end, + tileMode: tileMode, + transform: transform, + ); + + expect(attribute, isA()); + expect(attribute.value, isA()); + expect(attribute.resolve(EmptyMixData), isA()); + }); + + // colors + test('.colors', () { + final colors = [Colors.red, Colors.blue]; + final colorsDto = colors.map(ColorDto.new).toList(); + + final attribute = linearUtility(colors: colors); + + final resolvedGradient = attribute.resolve(EmptyMixData); + final dto = attribute.value as LinearGradientDto; + + expect(dto.colors, colorsDto); + expect(resolvedGradient.colors, colors); + }); + + // stops + test('.stops', () { + final stops = [0.0, 0.5]; + final attribute = linearUtility(stops: stops); + + final resolvedGradient = attribute.resolve(EmptyMixData); + final dto = attribute.value as LinearGradientDto; + + expect(dto.stops, stops); + expect(resolvedGradient.stops, stops); + }); + + // begin + test('.begin', () { + const begin = Alignment.centerLeft; + final attribute = linearUtility(begin: begin); + final attributeFn = linearUtility.begin.centerLeft(); + + final resolvedGradient = + attribute.resolve(EmptyMixData) as LinearGradient; + final dto = attribute.value as LinearGradientDto; + + expect(attribute, attributeFn); + expect(dto.begin, begin); + expect(resolvedGradient.begin, begin); + }); + + // end + test('.end', () { + const end = Alignment.centerRight; + final attribute = linearUtility(end: end); + final attributeFn = linearUtility.end.centerRight(); + + final resolvedGradient = + attribute.resolve(EmptyMixData) as LinearGradient; + + final dto = attribute.value as LinearGradientDto; + + expect(attribute, attributeFn); + expect(dto.end, end); + expect(resolvedGradient.end, end); + }); + + // tileMode + test('.tileMode', () { + const tileMode = TileMode.clamp; + final attribute = linearUtility(tileMode: tileMode); + final attributeFn = linearUtility.tileMode.clamp(); + + final resolvedGradient = + attribute.resolve(EmptyMixData) as LinearGradient; + final dto = attribute.value as LinearGradientDto; + + expect(attribute, attributeFn); + expect(dto.tileMode, tileMode); + expect(resolvedGradient.tileMode, tileMode); + }); + + // transform + test('.transform', () { + const transform = GradientRotation(0.0); + final attribute = linearUtility(transform: transform); + + final resolvedGradient = + attribute.resolve(EmptyMixData) as LinearGradient; + final dto = attribute.value as LinearGradientDto; + + expect(dto.transform, transform); + expect(resolvedGradient.transform, transform); + }); + + // resolve + test('.resolve', () { + final colors = [Colors.red, Colors.blue]; + final stops = [0.0, 0.5]; + const begin = Alignment.centerLeft; + const end = Alignment.centerRight; + const tileMode = TileMode.clamp; + const transform = GradientRotation(0.0); + + final attribute = linearUtility( + colors: colors, + stops: stops, + begin: begin, + end: end, + tileMode: tileMode, + transform: transform, + ); + + final resolvedGradient = + attribute.resolve(EmptyMixData) as LinearGradient; + final dto = attribute.value as LinearGradientDto; + + expect(dto.colors, colors.map(ColorDto.new).toList()); + expect(dto.stops, stops); + expect(dto.begin, begin); + expect(dto.end, end); + expect(dto.tileMode, tileMode); + expect(dto.transform, transform); + + expect(resolvedGradient, isA()); + expect(resolvedGradient.colors, colors); + expect(resolvedGradient.stops, stops); + expect(resolvedGradient.begin, begin); + expect(resolvedGradient.end, end); + expect(resolvedGradient.tileMode, tileMode); + expect(resolvedGradient.transform, transform); + }); + }); + + // SweepGradientUtility + group('SweepGradientUtility', () { + const sweepUtility = SweepGradientUtility.selfBuilder; + + test('.from for SweepGradient', () { + const gradient = SweepGradient(colors: []); + final attribute = sweepUtility.as(gradient); + + expect(attribute, isA()); + expect(attribute.value, isA()); + expect(attribute.resolve(EmptyMixData), isA()); + }); + + test('.call', () { + final colors = [Colors.red, Colors.blue]; + final stops = [0.0, 0.5]; + const center = Alignment.center; + const startAngle = 0.0; + const endAngle = 0.5; + const tileMode = TileMode.clamp; + const transform = GradientRotation(0.0); + + final attribute = sweepUtility( + colors: colors, + stops: stops, + center: center, + startAngle: startAngle, + endAngle: endAngle, + tileMode: tileMode, + transform: transform, + ); + + expect(attribute, isA()); + expect(attribute.value, isA()); + expect(attribute.resolve(EmptyMixData), isA()); + }); + + // colors + test('.colors', () { + final colors = [Colors.red, Colors.blue]; + final colorsDto = colors.map(ColorDto.new).toList(); + + final attribute = sweepUtility(colors: colors); + + final resolvedGradient = attribute.resolve(EmptyMixData); + final dto = attribute.value as SweepGradientDto; + + expect(dto.colors, colorsDto); + expect(resolvedGradient.colors, colors); + }); + + // stops + test('.stops', () { + final stops = [0.0, 0.5]; + final attribute = sweepUtility(stops: stops); + + final resolvedGradient = attribute.resolve(EmptyMixData); + final dto = attribute.value as SweepGradientDto; + + expect(dto.stops, stops); + expect(resolvedGradient.stops, stops); + }); + + // center + test('.center', () { + const center = Alignment.center; + final attribute = sweepUtility(center: center); + final attributeFn = sweepUtility.center.center(); + + final resolvedGradient = attribute.resolve(EmptyMixData) as SweepGradient; + final dto = attribute.value as SweepGradientDto; + + expect(attribute, attributeFn); + expect(dto.center, center); + expect(resolvedGradient.center, center); + }); + + // startAngle + test('.startAngle', () { + const startAngle = 0.0; + final attribute = sweepUtility(startAngle: startAngle); + + final resolvedGradient = attribute.resolve(EmptyMixData) as SweepGradient; + final dto = attribute.value as SweepGradientDto; + + expect(dto.startAngle, startAngle); + expect(resolvedGradient.startAngle, startAngle); + }); + + // endAngle + test('.endAngle', () { + const endAngle = 0.5; + final attribute = sweepUtility(endAngle: endAngle); + + final resolvedGradient = attribute.resolve(EmptyMixData) as SweepGradient; + final dto = attribute.value as SweepGradientDto; + + expect(dto.endAngle, endAngle); + expect(resolvedGradient.endAngle, endAngle); + }); + + // tileMode + test('.tileMode', () { + const tileMode = TileMode.clamp; + final attribute = sweepUtility(tileMode: tileMode); + final attributeFn = sweepUtility.tileMode.clamp(); + + final resolvedGradient = attribute.resolve(EmptyMixData) as SweepGradient; + final dto = attribute.value as SweepGradientDto; + + expect(attribute, attributeFn); + expect(dto.tileMode, tileMode); + expect(resolvedGradient.tileMode, tileMode); + }); + + // transform + test('.transform', () { + const transform = GradientRotation(0.0); + final attribute = sweepUtility(transform: transform); + + final resolvedGradient = attribute.resolve(EmptyMixData) as SweepGradient; + final dto = attribute.value as SweepGradientDto; + + expect(dto.transform, transform); + expect(resolvedGradient.transform, transform); + }); + + // resolve + test('.resolve', () { + final colors = [Colors.red, Colors.blue]; + final stops = [0.0, 0.5]; + const center = Alignment.center; + const startAngle = 0.0; + const endAngle = 0.5; + const tileMode = TileMode.clamp; + const transform = GradientRotation(0.0); + + final attribute = sweepUtility( + colors: colors, + stops: stops, + center: center, + startAngle: startAngle, + endAngle: endAngle, + tileMode: tileMode, + transform: transform, + ); + + final resolvedGradient = attribute.resolve(EmptyMixData) as SweepGradient; + final dto = attribute.value as SweepGradientDto; + + expect(dto.colors, colors.map(ColorDto.new).toList()); + expect(dto.stops, stops); + expect(dto.center, center); + expect(dto.startAngle, startAngle); + expect(dto.endAngle, endAngle); + expect(dto.tileMode, tileMode); + expect(dto.transform, transform); + + expect(resolvedGradient, isA()); + expect(resolvedGradient.colors, colors); + expect(resolvedGradient.stops, stops); + expect(resolvedGradient.center, center); + expect(resolvedGradient.startAngle, startAngle); + expect(resolvedGradient.endAngle, endAngle); + expect(resolvedGradient.tileMode, tileMode); + expect(resolvedGradient.transform, transform); + }); + }); +} diff --git a/test/src/attributes/scalar_attribute_test.dart b/test/src/attributes/scalar_attribute_test.dart deleted file mode 100644 index 607d436a8..000000000 --- a/test/src/attributes/scalar_attribute_test.dart +++ /dev/null @@ -1,269 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:mix/src/attributes/scalar_attribute.dart'; - -import '../../helpers/testing_utils.dart'; - -void main() { - testScalarAttribute( - 'AxisAttribute', - (value) => AxisAttribute(value), - Axis.values, - ); - - testScalarAttribute( - 'MainAxisAlignmentAttribute', - (value) => MainAxisAlignmentAttribute(value), - MainAxisAlignment.values); - - testScalarAttribute( - 'MainAxisSizeAttribute', - (value) => MainAxisSizeAttribute(value), - MainAxisSize.values, - ); - - testScalarAttribute( - 'CrossAxisAlignmentAttribute', - (value) => CrossAxisAlignmentAttribute(value), - CrossAxisAlignment.values, - ); - - testScalarAttribute( - 'VerticalDirectionAttribute', - (value) => VerticalDirectionAttribute(value), - VerticalDirection.values, - ); - - testScalarAttribute( - 'TextBaselineAttribute', - (value) => TextBaselineAttribute(value), - TextBaseline.values, - ); - - testScalarAttribute( - 'ClipAttribute', - (value) => ClipAttribute(value), - Clip.values, - ); - - testScalarAttribute( - 'ImageAlignmentAttribute', (value) => ImageAlignmentAttribute(value), [ - Alignment.topLeft, - Alignment.topCenter, - Alignment.topRight, - Alignment.centerLeft, - Alignment.center, - Alignment.centerRight, - Alignment.bottomLeft, - Alignment.bottomCenter, - Alignment.bottomRight, - ]); - - testScalarAttribute( - 'GradientAttribute', (value) => GradientAttribute(value), [ - const LinearGradient( - colors: [Colors.red, Colors.blue], - ), - const RadialGradient( - colors: [Colors.red, Colors.blue], - ), - const SweepGradient( - colors: [Colors.red, Colors.blue], - ), - ]); - - testScalarAttribute( - 'ImageFitAttribute', - (value) => ImageFitAttribute(value), - BoxFit.values, - ); - - testScalarAttribute( - 'ImageRepeatAttribute', - (value) => ImageRepeatAttribute(value), - ImageRepeat.values, - ); - - testScalarAttribute( - 'SoftWrapAttribute', (value) => SoftWrapAttribute(value), [ - false, - true, - ]); - - testScalarAttribute( - 'TextOverflowAttribute', - (value) => TextOverflowAttribute(value), - TextOverflow.values, - ); - - testScalarAttribute( - 'MaxLinesAttribute', (value) => MaxLinesAttribute(value), [ - 1, - 2, - ]); - - testScalarAttribute( - 'TextWidthBasisAttribute', - (value) => TextWidthBasisAttribute(value), - TextWidthBasis.values, - ); - - testScalarAttribute( - 'TextHeightBehaviorAttribute', - (value) => TextHeightBehaviorAttribute(value), [ - const TextHeightBehavior( - applyHeightToFirstAscent: true, - applyHeightToLastDescent: true, - leadingDistribution: TextLeadingDistribution.even, - ), - const TextHeightBehavior( - applyHeightToFirstAscent: false, - applyHeightToLastDescent: false, - leadingDistribution: TextLeadingDistribution.proportional, - ), - ]); - - testScalarAttribute( - 'TransformAttribute', (value) => TransformAttribute(value), [ - Matrix4.identity(), - Matrix4.rotationZ(0.1), - Matrix4.rotationY(0.2), - ]); - - testScalarAttribute( - 'TransformAlignmentAttribute', - (value) => TransformAlignmentAttribute(value), [ - Alignment.topLeft, - Alignment.topCenter, - Alignment.topRight, - Alignment.centerLeft, - Alignment.center, - Alignment.centerRight, - Alignment.bottomLeft, - Alignment.bottomCenter, - Alignment.bottomRight, - ]); - - testScalarAttribute( - 'BlendModeAttribute', - (value) => BlendModeAttribute(value), - BlendMode.values, - ); - - testScalarAttribute( - 'ImageWidthAttribute', - (value) => ImageWidthAttribute(value), - [ - 1.0, - 2.0, - ], - ); - - testScalarAttribute( - 'ImageHeightAttribute', - (value) => ImageHeightAttribute(value), - [ - 1.0, - 2.0, - ], - ); - - testScalarAttribute( - 'TextAlignAttribute', - (value) => TextAlignAttribute(value), - TextAlign.values, - ); - - testScalarAttribute( - 'TextDirectionAttribute', - (value) => TextDirectionAttribute(value), - TextDirection.values, - ); - -// class IconSizeAttribute extends ScalarAttribute { -// const IconSizeAttribute(super.value); - -// @override -// IconSizeAttribute create(value) => IconSizeAttribute(value); -// } - -// class BoxFitAttribute extends ScalarAttribute { -// const BoxFitAttribute(super.value); - -// @override -// BoxFitAttribute create(value) => BoxFitAttribute(value); -// } - -// class StackFitAttribute extends ScalarAttribute { -// const StackFitAttribute(super.value); - -// @override -// StackFitAttribute create(value) => StackFitAttribute(value); -// } - -// class FlexFitAttribute extends ScalarAttribute { -// const FlexFitAttribute(super.value); - -// @override -// FlexFitAttribute create(value) => FlexFitAttribute(value); -// } - - testScalarAttribute( - 'IconSizeAttribute', - (value) => IconSizeAttribute(value), - [ - 1.0, - 2.0, - ], - ); - - testScalarAttribute( - 'BoxFitAttribute', - (value) => BoxFitAttribute(value), - BoxFit.values, - ); - - testScalarAttribute( - 'StackFitAttribute', - (value) => StackFitAttribute(value), - StackFit.values, - ); - - testScalarAttribute( - 'FlexFitAttribute', - (value) => FlexFitAttribute(value), - FlexFit.values, - ); - - testScalarAttribute( - 'BoxShapeAttribute', - (value) => BoxShapeAttribute(value), - BoxShape.values, - ); - - testScalarAttribute( - 'VisibleAttribute', - (value) => VisibleAttribute(value), - [ - false, - true, - ], - ); - - testScalarAttribute( - 'ImageScaleAttribute', - (value) => ImageScaleAttribute(value), - [ - 1.0, - 2.0, - ], - ); - - testScalarAttribute( - 'TextScaleFactorAttribute', - (value) => TextScaleFactorAttribute(value), - [ - 1.0, - 2.0, - ], - ); -} diff --git a/test/src/attributes/scalars/scalar_util_test.dart b/test/src/attributes/scalars/scalar_util_test.dart new file mode 100644 index 000000000..c0a5ca510 --- /dev/null +++ b/test/src/attributes/scalars/scalar_util_test.dart @@ -0,0 +1,221 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('StackFitUtility Tests', () { + const utility = StackFitUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.loose().value, isA()); + expect(utility.expand().value, isA()); + expect(utility.passthrough().value, isA()); + expect(utility.loose().value, StackFit.loose); + expect(utility.expand().value, StackFit.expand); + expect(utility.passthrough().value, StackFit.passthrough); + }); + }); + + group('ClipUtility Tests', () { + const utility = ClipUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.none().value, isA()); + expect(utility.hardEdge().value, isA()); + expect(utility.antiAlias().value, isA()); + expect(utility.antiAliasWithSaveLayer().value, isA()); + expect(utility.none().value, Clip.none); + expect(utility.hardEdge().value, Clip.hardEdge); + expect(utility.antiAlias().value, Clip.antiAlias); + expect( + utility.antiAliasWithSaveLayer().value, Clip.antiAliasWithSaveLayer); + }); + }); + + group('VerticalDirectionUtility Tests', () { + const utility = VerticalDirectionUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.up().value, isA()); + expect(utility.down().value, isA()); + expect(utility.up().value, VerticalDirection.up); + expect(utility.down().value, VerticalDirection.down); + }); + }); + + group('TextOverflowUtility Tests', () { + const utility = TextOverflowUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.clip().value, isA()); + expect(utility.ellipsis().value, isA()); + expect(utility.fade().value, isA()); + expect(utility.clip().value, TextOverflow.clip); + expect(utility.ellipsis().value, TextOverflow.ellipsis); + expect(utility.fade().value, TextOverflow.fade); + }); + }); + + group('TextWidthBasisUtility Tests', () { + const utility = TextWidthBasisUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.parent().value, isA()); + expect(utility.longestLine().value, isA()); + expect(utility.parent().value, TextWidthBasis.parent); + expect(utility.longestLine().value, TextWidthBasis.longestLine); + }); + }); + + group('TextAlignUtility Tests', () { + const utility = TextAlignUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.left().value, isA()); + expect(utility.right().value, isA()); + expect(utility.center().value, isA()); + expect(utility.justify().value, isA()); + expect(utility.start().value, isA()); + expect(utility.end().value, isA()); + expect(utility.left().value, TextAlign.left); + expect(utility.right().value, TextAlign.right); + expect(utility.center().value, TextAlign.center); + expect(utility.justify().value, TextAlign.justify); + expect(utility.start().value, TextAlign.start); + expect(utility.end().value, TextAlign.end); + }); + }); + + group('FlexFitUtility Tests', () { + const utility = FlexFitUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.tight().value, isA()); + expect(utility.loose().value, isA()); + expect(utility.tight().value, FlexFit.tight); + expect(utility.loose().value, FlexFit.loose); + }); + }); + + group('AxisUtility Tests', () { + const utility = AxisUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.horizontal().value, isA()); + expect(utility.vertical().value, isA()); + expect(utility.horizontal().value, Axis.horizontal); + expect(utility.vertical().value, Axis.vertical); + }); + }); + + group('MainAxisAlignmentUtility Tests', () { + const utility = MainAxisAlignmentUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.start().value, isA()); + expect(utility.end().value, isA()); + expect(utility.center().value, isA()); + expect(utility.spaceBetween().value, isA()); + expect(utility.spaceAround().value, isA()); + expect(utility.spaceEvenly().value, isA()); + expect(utility.start().value, MainAxisAlignment.start); + expect(utility.end().value, MainAxisAlignment.end); + expect(utility.center().value, MainAxisAlignment.center); + expect(utility.spaceBetween().value, MainAxisAlignment.spaceBetween); + expect(utility.spaceAround().value, MainAxisAlignment.spaceAround); + expect(utility.spaceEvenly().value, MainAxisAlignment.spaceEvenly); + }); + }); + + group('CrossAxisAlignmentUtility Tests', () { + const utility = CrossAxisAlignmentUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.start().value, isA()); + expect(utility.end().value, isA()); + expect(utility.center().value, isA()); + expect(utility.stretch().value, isA()); + expect(utility.baseline().value, isA()); + expect(utility.start().value, CrossAxisAlignment.start); + expect(utility.end().value, CrossAxisAlignment.end); + expect(utility.center().value, CrossAxisAlignment.center); + expect(utility.stretch().value, CrossAxisAlignment.stretch); + expect(utility.baseline().value, CrossAxisAlignment.baseline); + }); + }); + + group('MainAxisSizeUtility Tests', () { + const utility = MainAxisSizeUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.min().value, isA()); + expect(utility.max().value, isA()); + expect(utility.min().value, MainAxisSize.min); + expect(utility.max().value, MainAxisSize.max); + }); + }); + + group('TextBaselineUtility Tests', () { + const utility = TextBaselineUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.alphabetic().value, isA()); + expect(utility.ideographic().value, isA()); + expect(utility.alphabetic().value, TextBaseline.alphabetic); + expect(utility.ideographic().value, TextBaseline.ideographic); + }); + }); + + group('ImageRepeatUtility Tests', () { + const utility = ImageRepeatUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.noRepeat().value, isA()); + expect(utility.repeat().value, isA()); + expect(utility.repeatX().value, isA()); + expect(utility.repeatY().value, isA()); + expect(utility.noRepeat().value, ImageRepeat.noRepeat); + expect(utility.repeat().value, ImageRepeat.repeat); + expect(utility.repeatX().value, ImageRepeat.repeatX); + expect(utility.repeatY().value, ImageRepeat.repeatY); + }); + }); + + group('BoxFitUtility Tests', () { + const utility = BoxFitUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.fill().value, isA()); + expect(utility.contain().value, isA()); + expect(utility.cover().value, isA()); + expect(utility.fitWidth().value, isA()); + expect(utility.fitHeight().value, isA()); + expect(utility.none().value, isA()); + expect(utility.scaleDown().value, isA()); + expect(utility.fill().value, BoxFit.fill); + expect(utility.contain().value, BoxFit.contain); + expect(utility.cover().value, BoxFit.cover); + expect(utility.fitWidth().value, BoxFit.fitWidth); + expect(utility.fitHeight().value, BoxFit.fitHeight); + expect(utility.none().value, BoxFit.none); + expect(utility.scaleDown().value, BoxFit.scaleDown); + }); + }); + + group('BoxShapeUtility Tests', () { + const utility = BoxShapeUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.circle().value, isA()); + expect(utility.rectangle().value, isA()); + expect(utility.circle().value, BoxShape.circle); + expect(utility.rectangle().value, BoxShape.rectangle); + }); + }); + + group('BlendModeUtility Tests', () { + test('Properties are initialized correctly', () { + const utility = BlendModeUtility(UtilityTestAttribute.new); + expect(utility.clear().value, BlendMode.clear); + expect(utility.src().value, BlendMode.src); + expect(utility.dst().value, BlendMode.dst); + expect(utility.srcOver().value, BlendMode.srcOver); + expect(utility.dstOver().value, BlendMode.dstOver); + expect(utility.srcIn().value, BlendMode.srcIn); + expect(utility.dstIn().value, BlendMode.dstIn); + expect(utility.srcOut().value, BlendMode.srcOut); + expect(utility.dstOut().value, BlendMode.dstOut); + expect(utility.srcATop().value, BlendMode.srcATop); + expect(utility.dstATop().value, BlendMode.dstATop); + expect(utility.xor().value, BlendMode.xor); + expect(utility.plus().value, BlendMode.plus); + }); + }); +} diff --git a/test/src/attributes/scalars/scalars_attribute_test.dart b/test/src/attributes/scalars/scalars_attribute_test.dart new file mode 100644 index 000000000..ad5532085 --- /dev/null +++ b/test/src/attributes/scalars/scalars_attribute_test.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('AxisAttribute Tests', () { + test('initializes correctly', () { + const axisAttribute = AxisAttribute(Axis.vertical); + expect(axisAttribute.value, Axis.vertical); + }); + + // maybeFrom + test('maybeFrom returns null if value is null', () { + final axisAttribute = AxisAttribute.maybeFrom(null); + expect(axisAttribute, isNull); + }); + }); + + group('TransformAttribute Tests', () { + test('initializes correctly', () { + final transformAttribute = TransformAttribute(Matrix4.identity()); + expect(transformAttribute.value, Matrix4.identity()); + }); + + test('maybeFrom returns null if value is null', () { + final transformAttribute = TransformAttribute.maybeFrom(null); + expect(transformAttribute, isNull); + }); + + test('build returns a Transform widget', () { + final transformAttribute = TransformAttribute(Matrix4.identity()); + final transform = transformAttribute.build(EmptyMixData, Container()); + expect(transform, isA()); + expect(transform.transform, Matrix4.identity()); + }); + }); + + group('AlignmentGeometryAttribute Tests', () { + test('initializes correctly', () { + const alignmentGeometryAttribute = + AlignmentGeometryAttribute(Alignment.center); + expect(alignmentGeometryAttribute.value, Alignment.center); + }); + + test('maybeFrom returns null if value is null', () { + final alignmentGeometryAttribute = + AlignmentGeometryAttribute.maybeFrom(null); + expect(alignmentGeometryAttribute, isNull); + }); + + test('build returns an Align widget', () { + const alignmentGeometryAttribute = + AlignmentGeometryAttribute(Alignment.topLeft); + final align = alignmentGeometryAttribute.build(EmptyMixData, Container()); + expect(align, isA()); + expect(align.alignment, Alignment.topLeft); + }); + }); + + group('ClipBehaviorAttribute Tests', () { + test('initializes correctly', () { + const clipBehaviorAttribute = ClipBehaviorAttribute(Clip.hardEdge); + expect(clipBehaviorAttribute.value, Clip.hardEdge); + }); + + test('maybeFrom returns null if value is null', () { + final clipBehaviorAttribute = ClipBehaviorAttribute.maybeFrom(null); + expect(clipBehaviorAttribute, isNull); + }); + }); + + group('BackgroundColorAttribute Tests', () { + test('initializes correctly', () { + const backgroundColorAttribute = + BackgroundColorAttribute(ColorDto(Colors.red)); + expect(backgroundColorAttribute.value.value, Colors.red); + }); + + test('maybeFrom returns null if value is null', () { + final backgroundColorAttribute = BackgroundColorAttribute.maybeFrom(null); + expect(backgroundColorAttribute, isNull); + }); + + test('build returns a ColoredBox widget', () { + const backgroundColorAttribute = + BackgroundColorAttribute(ColorDto(Colors.red)); + final coloredBox = + backgroundColorAttribute.build(EmptyMixData, Container()); + expect(coloredBox, isA()); + expect(coloredBox.color, Colors.red); + }); + }); +} diff --git a/test/src/attributes/shadow_attribute_test.dart b/test/src/attributes/shadow/shadow_dto_test.dart similarity index 51% rename from test/src/attributes/shadow_attribute_test.dart rename to test/src/attributes/shadow/shadow_dto_test.dart index 2a444f3df..db47743db 100644 --- a/test/src/attributes/shadow_attribute_test.dart +++ b/test/src/attributes/shadow/shadow_dto_test.dart @@ -1,55 +1,55 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/attributes/color_attribute.dart'; -import 'package:mix/src/attributes/shadow_attribute.dart'; +import 'package:mix/src/attributes/color/color_dto.dart'; +import 'package:mix/src/attributes/shadow/shadow_dto.dart'; -import '../../helpers/testing_utils.dart'; +import '../../../helpers/testing_utils.dart'; void main() { - group('ShadowAttribute and BoxShadowAttribute', () { - test('ShadowAttribute correctly resolves attributes', () { - const shadowAttribute = ShadowAttribute( - color: ColorAttribute(Colors.green), + group('ShadowDto and BoxShadowDto', () { + test('ShadowDto correctly resolves attributes', () { + const shadowDto = ShadowDto( + color: ColorDto(Colors.green), offset: Offset(1, 1), blurRadius: 2.0, ); - final resultShadow = shadowAttribute.resolve(EmptyMixData); + final resultShadow = shadowDto.resolve(EmptyMixData); expect(resultShadow.color, Colors.green); expect(resultShadow.offset, const Offset(1, 1)); expect(resultShadow.blurRadius, 2.0); }); - test('ShadowAttribute correctly merges attributes', () { - const shadowAttribute1 = ShadowAttribute( - color: ColorAttribute(Colors.green), + test('ShadowDto correctly merges attributes', () { + const shadowDto1 = ShadowDto( + color: ColorDto(Colors.green), offset: Offset(1, 1), blurRadius: 2.0, ); - const shadowAttribute2 = ShadowAttribute( - color: ColorAttribute(Colors.blue), + const shadowDto2 = ShadowDto( + color: ColorDto(Colors.blue), offset: Offset(2, 2), blurRadius: 4.0, ); - final resultShadow = shadowAttribute1.merge(shadowAttribute2); + final resultShadow = shadowDto1.merge(shadowDto2); expect(resultShadow.color?.value, Colors.blue); expect(resultShadow.offset, const Offset(2, 2)); expect(resultShadow.blurRadius, 4.0); }); - test('BoxShadowAttribute correctly resolves attributes', () { - const boxShadowAttribute = BoxShadowAttribute( - color: ColorAttribute(Colors.green), + test('BoxShadowDto correctly resolves attributes', () { + const boxShadowDto = BoxShadowDto( + color: ColorDto(Colors.green), offset: Offset(1, 1), blurRadius: 2.0, spreadRadius: 3.0, ); - final resultBoxShadow = boxShadowAttribute.resolve(EmptyMixData); + final resultBoxShadow = boxShadowDto.resolve(EmptyMixData); expect(resultBoxShadow.color, Colors.green, reason: 'color'); expect(resultBoxShadow.offset, const Offset(1, 1), reason: 'offset'); @@ -57,22 +57,22 @@ void main() { expect(resultBoxShadow.spreadRadius, 3.0, reason: 'spreadRadius'); }); - test('BoxShadowAttribute correctly merges attributes', () { - const boxShadowAttribute1 = BoxShadowAttribute( - color: ColorAttribute(Colors.green), + test('BoxShadowDto correctly merges attributes', () { + const boxShadowDto1 = BoxShadowDto( + color: ColorDto(Colors.green), offset: Offset(1, 1), blurRadius: 2.0, spreadRadius: 3.0, ); - const boxShadowAttribute2 = BoxShadowAttribute( - color: ColorAttribute(Colors.blue), + const boxShadowDto2 = BoxShadowDto( + color: ColorDto(Colors.blue), offset: Offset(2, 2), blurRadius: 4.0, spreadRadius: 5.0, ); - final resultBoxShadow = boxShadowAttribute1.merge(boxShadowAttribute2); + final resultBoxShadow = boxShadowDto1.merge(boxShadowDto2); expect(resultBoxShadow.color?.value, Colors.blue); expect(resultBoxShadow.offset, const Offset(2, 2)); diff --git a/test/src/attributes/space_attribute_test.dart b/test/src/attributes/space_attribute_test.dart deleted file mode 100644 index 9ca2fafdd..000000000 --- a/test/src/attributes/space_attribute_test.dart +++ /dev/null @@ -1,274 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/attributes/space_attribute.dart'; - -import '../../helpers/testing_utils.dart'; - -void main() { - group('PaddingAttribute', () { - // Constructor Tests - test('constructs correctly with all properties', () { - const padding = PaddingAttribute(top: 10, bottom: 5, left: 3, right: 7); - expect(padding.top, 10); - expect(padding.bottom, 5); - expect(padding.left, 3); - expect(padding.right, 7); - }); - - test('constructs correctly with no properties', () { - const padding = PaddingAttribute(); - expect(padding.top, null); - expect(padding.bottom, null); - expect(padding.left, null); - expect(padding.right, null); - }); - - // Merge Function Tests - test('merge function merges correctly', () { - const padding1 = PaddingAttribute(top: 10, bottom: 5); - const padding2 = PaddingAttribute(left: 3, right: 7); - - final merged = padding1.merge(padding2); - - expect(merged.top, 10); - expect(merged.bottom, 5); - expect(merged.left, 3); - expect(merged.right, 7); - }); - - test('merge returns itself if other is null', () { - const padding = PaddingAttribute(top: 10, bottom: 5); - final merged = padding.merge(null); - - expect(merged, equals(padding)); - }); - - // Resolve Function Tests - // Assuming a mock for MixData and its resolver - test('resolve function returns correct EdgeInsets', () { - const padding = PaddingAttribute(top: 10, bottom: 5, left: 3, right: 7); - - final resolvedValue = padding.resolve(EmptyMixData); - - expect(resolvedValue, - equals(const EdgeInsets.only(top: 10, bottom: 5, left: 3, right: 7))); - }); - - // Equality Tests - test('equality holds when properties are the same', () { - const padding1 = PaddingAttribute(top: 10, bottom: 5); - const padding2 = PaddingAttribute(top: 10, bottom: 5); - - expect(padding1, equals(padding2)); - }); - - test('equality fails when properties are different', () { - const padding1 = PaddingAttribute(top: 10, bottom: 5); - const padding2 = PaddingAttribute(top: 5, bottom: 10); - - expect(padding1, isNot(equals(padding2))); - }); - }); - - group('PaddingDirectionalAttribute', () { - // Constructor Tests - test('constructs correctly with all properties', () { - const padding = - PaddingDirectionalAttribute(top: 10, bottom: 5, start: 3, end: 7); - expect(padding.top, 10); - expect(padding.bottom, 5); - expect(padding.start, 3); - expect(padding.end, 7); - }); - - test('constructs correctly with no properties', () { - const padding = PaddingDirectionalAttribute(); - expect(padding.top, null); - expect(padding.bottom, null); - expect(padding.start, null); - expect(padding.end, null); - }); - - // Merge Function Tests - test('merge function merges correctly', () { - const padding1 = PaddingDirectionalAttribute(top: 10, bottom: 5); - const padding2 = PaddingDirectionalAttribute(start: 3, end: 7); - - final merged = padding1.merge(padding2); - - expect(merged.top, 10); - expect(merged.bottom, 5); - expect(merged.start, 3); - expect(merged.end, 7); - }); - - test('merge returns itself if other is null', () { - const padding = PaddingDirectionalAttribute(top: 10, bottom: 5); - final merged = padding.merge(null); - - expect(merged, equals(padding)); - }); - - // Resolve Function Tests - // Assuming a mock for MixData and its resolver - test('resolve function returns correct EdgeInsetsDirectional', () { - const padding = - PaddingDirectionalAttribute(top: 10, bottom: 5, start: 3, end: 7); - - final resolvedValue = padding.resolve(EmptyMixData); - - expect( - resolvedValue, - equals(const EdgeInsetsDirectional.only( - top: 10, bottom: 5, start: 3, end: 7))); - }); - - // Equality Tests - test('equality holds when properties are the same', () { - const padding1 = PaddingDirectionalAttribute(top: 10, bottom: 5); - const padding2 = PaddingDirectionalAttribute(top: 10, bottom: 5); - - expect(padding1, equals(padding2)); - }); - - test('equality fails when properties are different', () { - const padding1 = PaddingDirectionalAttribute(top: 10, bottom: 5); - const padding2 = PaddingDirectionalAttribute(top: 5, bottom: 10); - - expect(padding1, isNot(equals(padding2))); - }); - }); - group('MarginAttribute', () { - // Constructor Tests - test('constructs correctly with all properties', () { - const margin = MarginAttribute(top: 10, bottom: 5, left: 3, right: 7); - expect(margin.top, 10); - expect(margin.bottom, 5); - expect(margin.left, 3); - expect(margin.right, 7); - }); - - test('constructs correctly with no properties', () { - const margin = MarginAttribute(); - expect(margin.top, null); - expect(margin.bottom, null); - expect(margin.left, null); - expect(margin.right, null); - }); - - // Merge Function Tests - test('merge function merges correctly', () { - const margin1 = MarginAttribute(top: 10, bottom: 5); - const margin2 = MarginAttribute(left: 3, right: 7); - - final merged = margin1.merge(margin2); - - expect(merged.top, 10); - expect(merged.bottom, 5); - expect(merged.left, 3); - expect(merged.right, 7); - }); - - test('merge returns itself if other is null', () { - const margin = MarginAttribute(top: 10, bottom: 5); - final merged = margin.merge(null); - - expect(merged, equals(margin)); - }); - - // Resolve Function Tests - // Assuming a mock for MixData and its resolver - test('resolve function returns correct EdgeInsets', () { - const margin = MarginAttribute(top: 10, bottom: 5, left: 3, right: 7); - - final resolvedValue = margin.resolve(EmptyMixData); - - expect(resolvedValue, - equals(const EdgeInsets.only(top: 10, bottom: 5, left: 3, right: 7))); - }); - - // Equality Tests - test('equality holds when properties are the same', () { - const margin1 = MarginAttribute(top: 10, bottom: 5); - const margin2 = MarginAttribute(top: 10, bottom: 5); - - expect(margin1, equals(margin2)); - }); - - test('equality fails when properties are different', () { - const margin1 = MarginAttribute(top: 10, bottom: 5); - const margin2 = MarginAttribute(top: 5, bottom: 10); - - expect(margin1, isNot(equals(margin2))); - }); - }); - - group('MarginDirectionalAttribute', () { - // Constructor Tests - test('constructs correctly with all properties', () { - const margin = - MarginDirectionalAttribute(top: 10, bottom: 5, start: 3, end: 7); - expect(margin.top, 10); - expect(margin.bottom, 5); - expect(margin.start, 3); - expect(margin.end, 7); - }); - - test('constructs correctly with no properties', () { - const margin = MarginDirectionalAttribute(); - expect(margin.top, null); - expect(margin.bottom, null); - expect(margin.start, null); - expect(margin.end, null); - }); - - // Merge Function Tests - test('merge function merges correctly', () { - const margin1 = MarginDirectionalAttribute(top: 10, bottom: 5); - const margin2 = MarginDirectionalAttribute(start: 3, end: 7); - - final merged = margin1.merge(margin2); - - expect(merged.top, 10); - expect(merged.bottom, 5); - expect(merged.start, 3); - expect(merged.end, 7); - }); - - test('merge returns itself if other is null', () { - const margin = MarginDirectionalAttribute(top: 10, bottom: 5); - final merged = margin.merge(null); - - expect(merged, equals(margin)); - }); - - // Resolve Function Tests - // Assuming a mock for MixData and its resolver - test('resolve function returns correct EdgeInsetsDirectional', () { - const margin = - MarginDirectionalAttribute(top: 10, bottom: 5, start: 3, end: 7); - - final resolvedValue = margin.resolve(EmptyMixData); - - expect( - resolvedValue, - equals(const EdgeInsetsDirectional.only( - top: 10, bottom: 5, start: 3, end: 7))); - }); - - // Equality Tests - test('equality holds when properties are the same', () { - const margin1 = MarginDirectionalAttribute(top: 10, bottom: 5); - const margin2 = MarginDirectionalAttribute(top: 10, bottom: 5); - - expect(margin1, equals(margin2)); - }); - - test('equality fails when properties are different', () { - const margin1 = MarginDirectionalAttribute(top: 10, bottom: 5); - const margin2 = MarginDirectionalAttribute(top: 5, bottom: 10); - - expect(margin1, isNot(equals(margin2))); - }); - }); -} diff --git a/test/src/attributes/spacing/spacing_attribute_test.dart b/test/src/attributes/spacing/spacing_attribute_test.dart new file mode 100644 index 000000000..a28f7a537 --- /dev/null +++ b/test/src/attributes/spacing/spacing_attribute_test.dart @@ -0,0 +1,235 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; +import 'package:mix/src/attributes/spacing/spacing_dto.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('PaddingAttribute', () { + // Constructor Tests + test('constructs correctly with all properties', () { + const padding = + PaddingAttribute(SpacingDto(top: 10, bottom: 5, left: 3, right: 7)); + + expect(padding.top, 10); + expect(padding.bottom, 5); + expect(padding.left, 3); + expect(padding.right, 7); + expect(padding.start, null); + expect(padding.end, null); + }); + + test('constructs correctly with no properties', () { + const padding = PaddingAttribute(SpacingDto()); + expect(padding.top, null); + expect(padding.bottom, null); + expect(padding.left, null); + expect(padding.right, null); + expect(padding.start, null); + expect(padding.end, null); + }); + + // Merge Function Tests + test('merge function merges correctly', () { + const padding1 = PaddingAttribute(SpacingDto(top: 10, bottom: 5)); + + const padding2 = PaddingAttribute(SpacingDto(left: 3, right: 7)); + + final merged = padding1.merge(padding2); + + expect(merged.top, 10); + expect(merged.bottom, 5); + expect(merged.left, 3); + expect(merged.right, 7); + }); + + test('merge returns itself if other is null', () { + const padding = PaddingAttribute(SpacingDto(top: 10, bottom: 5)); + final merged = padding.merge(null); + + expect(merged, equals(padding)); + }); + + // Equality Tests + test('equality holds when properties are the same', () { + const padding1 = PaddingAttribute(SpacingDto(top: 10, bottom: 5)); + + const padding2 = PaddingAttribute(SpacingDto(top: 10, bottom: 5)); + + expect(padding1, equals(padding2)); + }); + + test('equality fails when properties are different', () { + const padding1 = PaddingAttribute(SpacingDto(top: 10, bottom: 5)); + const padding2 = PaddingAttribute(SpacingDto(top: 5, bottom: 10)); + + expect(padding1, isNot(equals(padding2))); + }); + }); + + group('PaddingAttribute Directional', () { + // Constructor Tests + test('constructs correctly with all properties', () { + const padding = + PaddingAttribute(SpacingDto(top: 10, bottom: 5, start: 3, end: 7)); + + expect(padding.top, 10); + expect(padding.bottom, 5); + expect(padding.start, 3); + expect(padding.end, 7); + }); + test('constructs correctly with no properties', () { + const padding = PaddingAttribute(SpacingDto()); + expect(padding.top, isNull); + expect(padding.bottom, isNull); + expect(padding.start, isNull); + expect(padding.end, isNull); + }); + + // Merge Function Tests + test('merge function merges correctly', () { + const padding1 = PaddingAttribute(SpacingDto(top: 10, bottom: 5)); + + const padding2 = PaddingAttribute(SpacingDto(start: 3, end: 7)); + + final merged = padding1.merge(padding2); + + expect(merged.top, 10); + expect(merged.bottom, 5); + expect(merged.end, 7); + expect(merged.start, 3); + }); + + test('merge returns itself if other is null', () { + const padding = PaddingAttribute(SpacingDto(top: 10, bottom: 5)); + final merged = padding.merge(null); + + expect(merged, equals(padding)); + }); + + // Resolve Function Tests + // Assuming a mock for MixData and its resolver + test('resolve function returns correct EdgeInsetsDirectional', () { + const padding = + PaddingAttribute(SpacingDto(top: 10, bottom: 5, start: 3, end: 7)); + + final resolvedValue = padding.resolve(EmptyMixData); + + expect( + resolvedValue, + equals( + const EdgeInsetsDirectional.only( + top: 10, bottom: 5, start: 3, end: 7), + ), + ); + }); + + // Equality Tests + test('equality holds when properties are the same', () { + const padding1 = PaddingAttribute(SpacingDto(top: 10, bottom: 5)); + const padding2 = PaddingAttribute(SpacingDto(top: 10, bottom: 5)); + + expect(padding1, equals(padding2)); + }); + + test('equality fails when properties are different', () { + const padding1 = PaddingAttribute(SpacingDto(top: 10, bottom: 5)); + + const padding2 = PaddingAttribute(SpacingDto(top: 5, bottom: 10)); + + expect(padding1, isNot(equals(padding2))); + }); + }); + + // MarginAttribute + group('MarginAttribute', () { + // Constructor Tests + test('constructs correctly with all properties', () { + const margin = + MarginAttribute(SpacingDto(top: 10, bottom: 5, left: 3, right: 7)); + + expect(margin.top, 10); + expect(margin.bottom, 5); + expect(margin.left, 3); + expect(margin.right, 7); + expect(margin.start, null); + expect(margin.end, null); + }); + + test('constructs correctly with no properties', () { + const margin = MarginAttribute(SpacingDto()); + expect(margin.top, null); + expect(margin.bottom, null); + expect(margin.left, null); + expect(margin.right, null); + expect(margin.start, null); + expect(margin.end, null); + }); + + // Merge Function Tests + test('merge function merges correctly', () { + const margin1 = MarginAttribute(SpacingDto(top: 10, bottom: 5)); + + const margin2 = MarginAttribute(SpacingDto(left: 3, right: 7)); + + final merged = margin1.merge(margin2); + + expect(merged.top, 10); + expect(merged.bottom, 5); + expect(merged.left, 3); + expect(merged.right, 7); + }); + + test('merge returns itself if other is null', () { + const margin = MarginAttribute(SpacingDto(top: 10, bottom: 5)); + final merged = margin.merge(null); + + expect(merged, equals(margin)); + }); + + // Resolve Function Tests + // Assuming a mock for MixData and its resolver + test('resolve function returns correct EdgeInsets', () { + const margin = + MarginAttribute(SpacingDto(top: 10, bottom: 5, left: 3, right: 7)); + + final resolvedValue = margin.resolve(EmptyMixData); + + expect( + resolvedValue, + equals( + const EdgeInsets.only(top: 10, bottom: 5, left: 3, right: 7), + ), + ); + }); + + // Equality Tests + test('equality holds when properties are the same', () { + const margin1 = MarginAttribute(SpacingDto(top: 10, bottom: 5)); + + const margin2 = MarginAttribute(SpacingDto(top: 10, bottom: 5)); + + expect(margin1, equals(margin2)); + }); + + test(' equality fails when properties are different', () { + const margin1 = MarginAttribute(SpacingDto(top: 10, bottom: 5)); + + const margin2 = MarginAttribute(SpacingDto(top: 5, bottom: 10)); + + expect(margin1, isNot(equals(margin2))); + }); + + // Directional MarginAttribute + test('constructs correctly with all properties', () { + const margin = + MarginAttribute(SpacingDto(top: 10, bottom: 5, start: 3, end: 7)); + + expect(margin.top, 10); + expect(margin.bottom, 5); + expect(margin.start, 3); + expect(margin.end, 7); + }); + }); +} diff --git a/test/src/attributes/spacing/spacing_dto_test.dart b/test/src/attributes/spacing/spacing_dto_test.dart new file mode 100644 index 000000000..c4a63efb6 --- /dev/null +++ b/test/src/attributes/spacing/spacing_dto_test.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/src/attributes/spacing/spacing_dto.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('SpacingDto', () { + test('resolves to EdgeInsets.only with correct values', () { + const spacingDto = SpacingDto( + top: 10, + bottom: 20, + left: 30, + right: 40, + ); + + expect( + spacingDto.resolve(EmptyMixData), + const EdgeInsets.only( + left: 30, + top: 10, + right: 40, + bottom: 20, + ), + ); + }); + + test('merges correctly with another SpacingDto', () { + const spacingDto1 = SpacingDto( + top: 10, + bottom: 20, + left: 30, + right: 40, + ); + const spacingDto2 = SpacingDto( + top: 5, + bottom: 15, + left: 25, + right: 35, + ); + final mergedSpacingDto = spacingDto1.merge(spacingDto2); + expect( + mergedSpacingDto, + const SpacingDto( + top: 5, + bottom: 15, + left: 25, + right: 35, + ), + ); + }); + }); +} diff --git a/test/src/attributes/spacing/spacing_util_test.dart b/test/src/attributes/spacing/spacing_util_test.dart new file mode 100644 index 000000000..e74723344 --- /dev/null +++ b/test/src/attributes/spacing/spacing_util_test.dart @@ -0,0 +1,611 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; +import 'package:mix/src/attributes/spacing/spacing_dto.dart'; + +void main() { + group('Padding Utils', () { + test('padding()', () { + expect( + padding(10), + const PaddingAttribute( + SpacingDto(top: 10, bottom: 10, left: 10, right: 10)), + ); + expect( + padding(10, 20), + const PaddingAttribute( + SpacingDto(top: 10, bottom: 10, left: 20, right: 20)), + ); + expect( + padding(10, 20, 30), + const PaddingAttribute( + SpacingDto(top: 10, bottom: 30, left: 20, right: 20)), + ); + expect( + padding(10, 20, 30, 40), + const PaddingAttribute( + SpacingDto(top: 10, bottom: 30, left: 40, right: 20)), + ); + }); + + test('padding.directional()', () { + expect( + padding.directional(10), + const PaddingAttribute( + SpacingDto(start: 10, end: 10, top: 10, bottom: 10)), + reason: '1', + ); + expect( + padding.directional(10, 20), + const PaddingAttribute( + SpacingDto(top: 10, bottom: 10, start: 20, end: 20)), + reason: '2', + ); + expect( + padding.directional(10, 20, 30), + const PaddingAttribute( + SpacingDto(top: 10, bottom: 30, start: 20, end: 20)), + reason: '3', + ); + expect( + padding.directional(10, 20, 30, 40), + const PaddingAttribute( + SpacingDto(top: 10, end: 20, bottom: 30, start: 40)), + reason: '4', + ); + }); + + test('padding.from', () { + expect( + padding.as(const EdgeInsets.all(10)), + const PaddingAttribute( + SpacingDto(top: 10, bottom: 10, left: 10, right: 10)), + reason: '1', + ); + + expect( + padding.as(const EdgeInsets.only(top: 10)), + const PaddingAttribute( + SpacingDto(top: 10, bottom: 0, left: 0, right: 0)), + reason: '2', + ); + + expect( + padding.as(const EdgeInsets.only(left: 10)), + const PaddingAttribute( + SpacingDto(left: 10, bottom: 0, top: 0, right: 0)), + reason: '3', + ); + + expect( + padding.as(const EdgeInsets.only(right: 10)), + const PaddingAttribute( + SpacingDto(right: 10, bottom: 0, top: 0, left: 0)), + reason: '4', + ); + + expect( + padding.as(const EdgeInsets.only(bottom: 10)), + const PaddingAttribute( + SpacingDto(bottom: 10, top: 0, left: 0, right: 0)), + reason: '5', + ); + + expect( + padding.as(const EdgeInsets.symmetric(horizontal: 10)), + const PaddingAttribute( + SpacingDto(left: 10, right: 10, top: 0, bottom: 0)), + reason: '6', + ); + + expect( + padding.as(const EdgeInsets.symmetric(vertical: 10)), + const PaddingAttribute( + SpacingDto(top: 10, bottom: 10, left: 0, right: 0)), + reason: '7', + ); + + expect( + padding.directional.as(const EdgeInsetsDirectional.only(start: 10)), + const PaddingAttribute( + SpacingDto(start: 10, end: 0, top: 0, bottom: 0)), + reason: '8', + ); + + expect( + padding.directional.as(const EdgeInsetsDirectional.only(end: 10)), + const PaddingAttribute( + SpacingDto(end: 10, start: 0, top: 0, bottom: 0)), + reason: '9', + ); + + expect( + padding.directional.as(const EdgeInsetsDirectional.only(top: 10)), + const PaddingAttribute( + SpacingDto(top: 10, bottom: 0, start: 0, end: 0)), + reason: '10', + ); + + expect( + padding.directional.as(const EdgeInsetsDirectional.only(bottom: 10)), + const PaddingAttribute( + SpacingDto(bottom: 10, top: 0, start: 0, end: 0)), + reason: '11', + ); + + expect( + padding.directional + .as(const EdgeInsetsDirectional.only(start: 10, end: 20)), + const PaddingAttribute( + SpacingDto(start: 10, end: 20, top: 0, bottom: 0)), + reason: '12', + ); + + expect( + padding.directional + .as(const EdgeInsetsDirectional.only(start: 10, end: 20, top: 30)), + const PaddingAttribute( + SpacingDto(start: 10, end: 20, top: 30, bottom: 0)), + reason: '13', + ); + + expect( + padding.directional.as( + const EdgeInsetsDirectional.only( + start: 10, + end: 20, + top: 30, + bottom: 40, + ), + ), + const PaddingAttribute( + SpacingDto(start: 10, end: 20, top: 30, bottom: 40)), + reason: '14', + ); + }); + + // padding.directionalFrom + + test('padding.only', () { + expect( + padding.only(top: 10), + const PaddingAttribute(SpacingDto(top: 10)), + ); + + expect( + padding.only(left: 10), + const PaddingAttribute(SpacingDto(left: 10)), + ); + + expect( + padding.only(right: 10), + const PaddingAttribute(SpacingDto(right: 10)), + ); + + expect( + padding.only(bottom: 10), + const PaddingAttribute(SpacingDto(bottom: 10)), + ); + }); + + test('padding.top', () { + expect( + padding.top(10), + const PaddingAttribute(SpacingDto(top: 10)), + ); + }); + + test('padding.bottom', () { + expect( + padding.bottom(10), + const PaddingAttribute(SpacingDto(bottom: 10)), + ); + }); + + test('padding.left', () { + expect( + padding.left(10), + const PaddingAttribute(SpacingDto(left: 10)), + ); + }); + + test('padding.right', () { + expect( + padding.right(10), + const PaddingAttribute(SpacingDto(right: 10)), + ); + }); + + test('padding.directional.start', () { + expect( + padding.directional.start(10), + const PaddingAttribute(SpacingDto(start: 10)), + ); + }); + + test('padding.directional.end', () { + expect( + padding.directional.end(10), + const PaddingAttribute(SpacingDto(end: 10)), + ); + }); + + test('padding.horizontal', () { + expect( + padding.horizontal(10), + const PaddingAttribute(SpacingDto(left: 10, right: 10)), + ); + }); + + test('padding.vertical', () { + expect( + padding.vertical(10), + const PaddingAttribute(SpacingDto(top: 10, bottom: 10)), + ); + }); + + test('padding.all', () { + expect( + padding.all(10), + const PaddingAttribute( + SpacingDto(top: 10, bottom: 10, left: 10, right: 10)), + ); + }); + + test('padding.directional.only', () { + expect( + padding.directional.only(start: 10), + const PaddingAttribute(SpacingDto(start: 10)), + ); + + expect( + padding.directional.only(end: 10), + const PaddingAttribute(SpacingDto(end: 10)), + ); + + expect( + padding.directional.only(start: 10, end: 20), + const PaddingAttribute(SpacingDto(start: 10, end: 20)), + ); + + expect( + padding.directional.only(start: 10, end: 20, top: 30), + const PaddingAttribute(SpacingDto(start: 10, end: 20, top: 30)), + ); + + expect( + padding.directional.only(start: 10, end: 20, top: 30, bottom: 40), + const PaddingAttribute( + SpacingDto(start: 10, end: 20, top: 30, bottom: 40)), + ); + }); + }); + + group('Margin Utils', () { + test('margin()', () { + expect( + margin(10), + const MarginAttribute( + SpacingDto(top: 10, bottom: 10, left: 10, right: 10), + ), + ); + expect( + margin(10, 20), + const MarginAttribute( + SpacingDto(top: 10, bottom: 10, left: 20, right: 20), + ), + ); + expect( + margin(10, 20, 30), + const MarginAttribute( + SpacingDto(top: 10, bottom: 30, left: 20, right: 20), + ), + ); + expect( + margin(10, 20, 30, 40), + const MarginAttribute( + SpacingDto(top: 10, bottom: 30, left: 40, right: 20), + ), + ); + }); + + // margin.directional() + test('margin.directional()', () { + expect( + margin.directional(10), + const MarginAttribute( + SpacingDto( + start: 10, + end: 10, + top: 10, + bottom: 10, + ), + ), + reason: '1', + ); + expect( + margin.directional(10, 20), + const MarginAttribute( + SpacingDto( + top: 10, + bottom: 10, + start: 20, + end: 20, + ), + ), + reason: '2', + ); + expect( + margin.directional(10, 20, 30), + const MarginAttribute( + SpacingDto( + top: 10, + bottom: 30, + start: 20, + end: 20, + ), + ), + reason: '3', + ); + expect( + margin.directional(10, 20, 30, 40), + const MarginAttribute( + SpacingDto( + top: 10, + end: 20, + bottom: 30, + start: 40, + ), + ), + reason: '4', + ); + }); + + test('margin.only', () { + expect( + margin.only(top: 10), + const MarginAttribute( + SpacingDto(top: 10), + ), + ); + + expect( + margin.only(left: 10), + const MarginAttribute( + SpacingDto(left: 10), + ), + ); + + expect( + margin.only(right: 10), + const MarginAttribute( + SpacingDto(right: 10), + ), + ); + + expect( + margin.only(bottom: 10), + const MarginAttribute( + SpacingDto(bottom: 10), + ), + ); + }); + + test('margin.top', () { + expect( + margin.top(10), + const MarginAttribute( + SpacingDto(top: 10), + ), + ); + }); + + test('margin.bottom', () { + expect( + margin.bottom(10), + const MarginAttribute( + SpacingDto(bottom: 10), + ), + ); + }); + + test('margin.left', () { + expect( + margin.left(10), + const MarginAttribute( + SpacingDto(left: 10), + ), + ); + }); + + test('margin.right', () { + expect( + margin.right(10), + const MarginAttribute( + SpacingDto(right: 10), + ), + ); + }); + + test('margin.directional.start', () { + expect( + margin.directional.start(10), + const MarginAttribute( + SpacingDto(start: 10), + ), + ); + }); + + test('margin.directional.end', () { + expect( + margin.directional.end(10), + const MarginAttribute( + SpacingDto(end: 10), + ), + ); + }); + + test('margin.horizontal', () { + expect( + margin.horizontal(10), + const MarginAttribute( + SpacingDto(left: 10, right: 10), + ), + ); + }); + + test('margin.vertical', () { + expect( + margin.vertical(10), + const MarginAttribute(SpacingDto(top: 10, bottom: 10)), + ); + }); + + test('margin.all', () { + expect( + margin.all(10), + const MarginAttribute( + SpacingDto(top: 10, bottom: 10, left: 10, right: 10), + ), + ); + }); + + test('margin.as', () { + expect( + margin.as(const EdgeInsets.all(10)), + const MarginAttribute( + SpacingDto(top: 10, bottom: 10, left: 10, right: 10), + ), + ); + + expect( + margin.as(const EdgeInsets.only(top: 10)), + const MarginAttribute( + SpacingDto(top: 10, bottom: 0, left: 0, right: 0), + ), + ); + + expect( + margin.as(const EdgeInsets.only(left: 10)), + const MarginAttribute( + SpacingDto(left: 10, bottom: 0, top: 0, right: 0), + ), + ); + + expect( + margin.as(const EdgeInsets.only(right: 10)), + const MarginAttribute( + SpacingDto(right: 10, bottom: 0, top: 0, left: 0), + ), + ); + + expect( + margin.as(const EdgeInsets.only(bottom: 10)), + const MarginAttribute( + SpacingDto(bottom: 10, top: 0, left: 0, right: 0), + ), + ); + + expect( + margin.as(const EdgeInsets.symmetric(horizontal: 10)), + const MarginAttribute( + SpacingDto(left: 10, right: 10, top: 0, bottom: 0), + ), + ); + + expect( + margin.as(const EdgeInsets.symmetric(vertical: 10)), + const MarginAttribute( + SpacingDto(top: 10, bottom: 10, left: 0, right: 0), + ), + ); + + expect( + margin.directional.as(const EdgeInsetsDirectional.only(start: 10)), + const MarginAttribute( + SpacingDto(start: 10, end: 0, top: 0, bottom: 0), + ), + ); + + expect( + margin.directional.as(const EdgeInsetsDirectional.only(end: 10)), + const MarginAttribute( + SpacingDto(end: 10, start: 0, top: 0, bottom: 0), + ), + ); + + expect( + margin.directional.as(const EdgeInsetsDirectional.only(top: 10)), + const MarginAttribute(SpacingDto(top: 10, bottom: 0, start: 0, end: 0)), + ); + + expect( + margin.directional.as(const EdgeInsetsDirectional.only(bottom: 10)), + const MarginAttribute( + SpacingDto(bottom: 10, top: 0, start: 0, end: 0), + ), + ); + + expect( + margin.directional + .as(const EdgeInsetsDirectional.only(start: 10, end: 20)), + const MarginAttribute( + SpacingDto(start: 10, end: 20, top: 0, bottom: 0), + ), + ); + + expect( + margin.directional + .as(const EdgeInsetsDirectional.only(start: 10, end: 20, top: 30)), + const MarginAttribute( + SpacingDto(start: 10, end: 20, top: 30, bottom: 0), + ), + ); + + expect( + margin.directional.as(const EdgeInsetsDirectional.only( + start: 10, end: 20, top: 30, bottom: 40)), + const MarginAttribute( + SpacingDto(start: 10, end: 20, top: 30, bottom: 40), + ), + ); + }); + + test('margin.directional.only', () { + expect( + margin.directional.only(start: 10), + const MarginAttribute( + SpacingDto(start: 10), + ), + ); + + expect( + margin.directional.only(end: 10), + const MarginAttribute( + SpacingDto(end: 10), + ), + ); + + expect( + margin.directional.only(start: 10, end: 20), + const MarginAttribute( + SpacingDto(start: 10, end: 20), + ), + ); + + expect( + margin.directional.only(start: 10, end: 20, top: 30), + const MarginAttribute( + SpacingDto(start: 10, end: 20, top: 30), + ), + ); + + expect( + margin.directional.only(start: 10, end: 20, top: 30, bottom: 40), + const MarginAttribute( + SpacingDto(start: 10, end: 20, top: 30, bottom: 40), + ), + ); + }); + }); +} diff --git a/test/src/attributes/strut_style/strut_style_attribute_test.dart b/test/src/attributes/strut_style/strut_style_attribute_test.dart new file mode 100644 index 000000000..3e79f5aed --- /dev/null +++ b/test/src/attributes/strut_style/strut_style_attribute_test.dart @@ -0,0 +1,71 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/src/attributes/strut_style/strut_style_attribute.dart'; +import 'package:mix/src/attributes/strut_style/strut_style_dto.dart'; + +import '../../../helpers/attribute_generator.dart'; +import '../../../helpers/testing_utils.dart'; + +void main() { + group('StrutStyleAttribute', () { + test('initializes correctly', () { + final strutStyle = RandomGenerator.strutStyle(); + final strutStyleDto = StrutStyleDto.from(strutStyle); + + final attribute = StrutStyleAttribute(strutStyleDto); + + expect(attribute.value, equals(strutStyleDto)); + expect(attribute.value, isA()); + expect(attribute.value.fontFamily, equals(strutStyle.fontFamily)); + expect(attribute.value.fontFamilyFallback, + equals(strutStyle.fontFamilyFallback)); + expect(attribute.value.fontSize, equals(strutStyle.fontSize)); + expect(attribute.value.fontWeight, equals(strutStyle.fontWeight)); + expect(attribute.value.fontStyle, equals(strutStyle.fontStyle)); + expect(attribute.value.height, equals(strutStyle.height)); + expect(attribute.value.leading, equals(strutStyle.leading)); + expect(attribute.value.forceStrutHeight, + equals(strutStyle.forceStrutHeight)); + }); + + test('merge returns the same attribute if other is null', () { + final strutStyle = RandomGenerator.strutStyle(); + final strutStyleDto = StrutStyleDto.from(strutStyle); + + final attribute = StrutStyleAttribute(strutStyleDto); + final mergedAttribute = attribute.merge(null); + + expect(mergedAttribute, equals(attribute)); + }); + + test('merge returns the same attribute if value types are different', () { + const strutStyle1 = StrutStyleDto(fontFamily: 'Roboto'); + const strutStyle2 = StrutStyleDto(fontFamily: 'Arial'); + const attribute1 = StrutStyleAttribute(strutStyle1); + const attribute2 = StrutStyleAttribute(strutStyle2); + final mergedAttribute = attribute1.merge(attribute2); + + expect(mergedAttribute, equals(attribute2)); + }); + + test('merge returns a new attribute with merged value', () { + final strutStyle1 = StrutStyleDto.from(RandomGenerator.strutStyle()); + final strutStyle2 = StrutStyleDto.from(RandomGenerator.strutStyle()); + + final attribute1 = StrutStyleAttribute(strutStyle1); + final attribute2 = StrutStyleAttribute(strutStyle2); + final mergedAttribute = attribute1.merge(attribute2); + + expect(mergedAttribute.value, equals(strutStyle1.merge(strutStyle2))); + }); + + test('resolve returns the correct StrutStyle', () { + final strutStyle = RandomGenerator.strutStyle(); + final strutStyleDto = StrutStyleDto.from(strutStyle); + + final attribute = StrutStyleAttribute(strutStyleDto); + final resolvedStrutStyle = attribute.resolve(EmptyMixData); + + expect(resolvedStrutStyle, equals(strutStyle)); + }); + }); +} diff --git a/test/src/attributes/strut_style_attribute_test.dart b/test/src/attributes/strut_style/strut_style_dto_test.dart similarity index 52% rename from test/src/attributes/strut_style_attribute_test.dart rename to test/src/attributes/strut_style/strut_style_dto_test.dart index 5ff9c79bc..63496a42c 100644 --- a/test/src/attributes/strut_style_attribute_test.dart +++ b/test/src/attributes/strut_style/strut_style_dto_test.dart @@ -1,14 +1,14 @@ import 'dart:ui'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/attributes/strut_style_attribute.dart'; +import 'package:mix/src/attributes/strut_style/strut_style_dto.dart'; -import '../../helpers/testing_utils.dart'; +import '../../../helpers/testing_utils.dart'; void main() { - group('StrutStyleAttribute', () { + group('StrutStyleDto', () { test('from constructor sets all values correctly', () { - const attr = StrutStyleAttribute( + const strutStyle = StrutStyleDto( fontFamily: 'Roboto', fontSize: 24.0, height: 2.0, @@ -18,21 +18,21 @@ void main() { forceStrutHeight: true, ); - expect(attr.fontFamily, 'Roboto'); - expect(attr.fontSize, 24.0); - expect(attr.height, 2.0); - expect(attr.leading, 1.0); - expect(attr.fontWeight, FontWeight.bold); - expect(attr.fontStyle, FontStyle.italic); - expect(attr.forceStrutHeight, true); + expect(strutStyle.fontFamily, 'Roboto'); + expect(strutStyle.fontSize, 24.0); + expect(strutStyle.height, 2.0); + expect(strutStyle.leading, 1.0); + expect(strutStyle.fontWeight, FontWeight.bold); + expect(strutStyle.fontStyle, FontStyle.italic); + expect(strutStyle.forceStrutHeight, true); }); // Test to check if the merge function returns a merged object correctly test('merge returns merged object correctly', () { - const attr1 = StrutStyleAttribute(fontFamily: 'Roboto', fontSize: 24.0); - const attr2 = StrutStyleAttribute( - height: 2.0, leading: 1.0, fontWeight: FontWeight.bold); - final merged = attr1.merge(attr2); + const strutStyle1 = StrutStyleDto(fontFamily: 'Roboto', fontSize: 24.0); + const strutStyle2 = + StrutStyleDto(height: 2.0, leading: 1.0, fontWeight: FontWeight.bold); + final merged = strutStyle1.merge(strutStyle2); expect(merged.fontFamily, 'Roboto'); expect(merged.fontSize, 24.0); @@ -43,7 +43,7 @@ void main() { // Test to check if the resolve function returns the correct StrutStyle test('resolve returns correct StrutStyle', () { - const attr = StrutStyleAttribute( + const strutStyle = StrutStyleDto( fontFamily: 'Roboto', fontSize: 24.0, height: 2.0, @@ -51,30 +51,30 @@ void main() { fontWeight: FontWeight.bold, fontStyle: FontStyle.italic, ); - final strutStyle = attr.resolve(EmptyMixData); + final resolvedValue = strutStyle.resolve(EmptyMixData); - expect(strutStyle.fontFamily, 'Roboto'); - expect(strutStyle.fontSize, 24.0); - expect(strutStyle.height, 2.0); - expect(strutStyle.leading, 1.0); - expect(strutStyle.fontWeight, FontWeight.bold); - expect(strutStyle.fontStyle, FontStyle.italic); + expect(resolvedValue.fontFamily, 'Roboto'); + expect(resolvedValue.fontSize, 24.0); + expect(resolvedValue.height, 2.0); + expect(resolvedValue.leading, 1.0); + expect(resolvedValue.fontWeight, FontWeight.bold); + expect(resolvedValue.fontStyle, FontStyle.italic); }); - // Test to check if two StrutStyleAttributes with the same properties are equal + // Test to check if two StrutStyleDtos with the same properties are equal test('Equality holds when all properties are the same', () { - const attr1 = StrutStyleAttribute(fontFamily: 'Roboto', fontSize: 24.0); - const attr2 = StrutStyleAttribute(fontFamily: 'Roboto', fontSize: 24.0); + const strutStyle1 = StrutStyleDto(fontFamily: 'Roboto', fontSize: 24.0); + const strutStyle2 = StrutStyleDto(fontFamily: 'Roboto', fontSize: 24.0); - expect(attr1, attr2); + expect(strutStyle1, strutStyle2); }); - // Test to check if two StrutStyleAttributes with different properties are not equal + // Test to check if two StrutStyleDtos with different properties are not equal test('Equality fails when properties are different', () { - const attr1 = StrutStyleAttribute(fontFamily: 'Roboto', fontSize: 24.0); - const attr2 = StrutStyleAttribute(fontFamily: 'Lato', fontSize: 24.0); + const strutStyle1 = StrutStyleDto(fontFamily: 'Roboto', fontSize: 24.0); + const strutStyle2 = StrutStyleDto(fontFamily: 'Lato', fontSize: 24.0); - expect(attr1, isNot(attr2)); + expect(strutStyle1, isNot(strutStyle2)); }); }); } diff --git a/test/src/attributes/strut_style/strut_style_util_test.dart b/test/src/attributes/strut_style/strut_style_util_test.dart new file mode 100644 index 000000000..b5bab41e7 --- /dev/null +++ b/test/src/attributes/strut_style/strut_style_util_test.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/src/attributes/strut_style/strut_style_dto.dart'; +import 'package:mix/src/attributes/strut_style/strut_style_util.dart'; + +import '../../../helpers/attribute_generator.dart'; +import '../../../helpers/testing_utils.dart'; + +void main() { + group('StrutStyleUtility', () { + const strutStyleUtility = StrutStyleUtility(UtilityTestAttribute.new); + test('callable', () { + final strutStyle = strutStyleUtility( + fontFamily: 'Roboto', + fontSize: 24.0, + height: 2.0, + leading: 1.0, + fontWeight: FontWeight.bold, + fontStyle: FontStyle.italic, + forceStrutHeight: true, + ); + + expect(strutStyleUtility.call, isA()); + expect(strutStyle.value, isA()); + expect(strutStyle.value.fontFamily, 'Roboto'); + expect(strutStyle.value.fontSize, 24.0); + expect(strutStyle.value.height, 2.0); + expect(strutStyle.value.leading, 1.0); + expect(strutStyle.value.fontWeight, FontWeight.bold); + expect(strutStyle.value.fontStyle, FontStyle.italic); + expect(strutStyle.value.forceStrutHeight, true); + }); + + test('fontFamily', () { + final strutStyle = strutStyleUtility.fontFamily('Roboto'); + + expect(strutStyle.value.fontFamily, 'Roboto'); + }); + + test('fontSize', () { + final strutStyle = strutStyleUtility.fontSize(24.0); + + expect(strutStyle.value.fontSize, 24.0); + }); + + test('height', () { + final strutStyle = strutStyleUtility.height(2.0); + + expect(strutStyle.value.height, 2.0); + }); + + test('leading', () { + final strutStyle = strutStyleUtility.leading(1.0); + + expect(strutStyle.value.leading, 1.0); + }); + + test('fontWeight', () { + final strutStyle = strutStyleUtility.fontWeight(FontWeight.bold); + + expect(strutStyle.value.fontWeight, FontWeight.bold); + }); + + test('fontStyle', () { + final strutStyle = strutStyleUtility.fontStyle(FontStyle.italic); + + expect(strutStyle.value.fontStyle, FontStyle.italic); + }); + + test('forceStrutHeight', () { + final strutStyle = strutStyleUtility.forceStrutHeight(true); + + expect(strutStyle.value.forceStrutHeight, true); + }); + + test('as', () { + final strutStyle = RandomGenerator.strutStyle(); + final attribute = strutStyleUtility.as(strutStyle); + + expect(attribute.value, isA()); + expect(attribute.value, equals(StrutStyleDto.from(strutStyle))); + expect(attribute.value.resolve(EmptyMixData), equals(strutStyle)); + }); + }); +} diff --git a/test/src/attributes/style_mix_attribute_test.dart b/test/src/attributes/style_mix_attribute_test.dart index 35b564f9c..8af42745a 100644 --- a/test/src/attributes/style_mix_attribute_test.dart +++ b/test/src/attributes/style_mix_attribute_test.dart @@ -42,6 +42,7 @@ void main() { const MockIntScalarAttribute(2), ); final styleMix2 = StyleMix(const MockDoubleScalarAttribute(2.0)); + final attribute1 = StyleMixAttribute(styleMix1); final attribute2 = StyleMixAttribute(styleMix2); @@ -69,8 +70,8 @@ void main() { test('should contain all attributes after multiple merges', () { const attr1 = MockIntScalarAttribute(3); - const attr2 = MockIntScalarAttribute(1); - const attr3 = MockIntScalarAttribute(5); + const attr2 = MockDoubleScalarAttribute(1); + const attr3 = MockBooleanScalarAttribute(false); final styleMix1 = StyleMix(attr1); final styleMix2 = StyleMix(attr2); @@ -96,8 +97,8 @@ void main() { test('should handle nested StyleMixAttributes properly', () { const attr1 = MockIntScalarAttribute(3); - const attr2 = MockIntScalarAttribute(1); - const attr3 = MockIntScalarAttribute(5); + const attr2 = MockDoubleScalarAttribute(1); + const attr3 = MockBooleanScalarAttribute(false); final style = StyleMix(attr1, attr2, attr3); @@ -114,12 +115,18 @@ void main() { expect(level3Attribute.value.values, containsAll([attr1, attr2, attr3])); expect( - level1Attribute.value.values.whereType(), isEmpty); + level1Attribute.value.values.whereType(), + isEmpty, + ); expect( - level2Attribute.value.values.whereType(), isEmpty); + level2Attribute.value.values.whereType(), + isEmpty, + ); expect( - level3Attribute.value.values.whereType(), isEmpty); + level3Attribute.value.values.whereType(), + isEmpty, + ); expect(level1Attribute.value.values.length, 3); expect(level2Attribute.value.values.length, 3); diff --git a/test/src/attributes/text_directives_util_test.dart b/test/src/attributes/text_directives_util_test.dart new file mode 100644 index 000000000..2a6a546cc --- /dev/null +++ b/test/src/attributes/text_directives_util_test.dart @@ -0,0 +1,84 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +void main() { + group('TextDirectiveAttribute', () { + String uppercaseFn(value) => value.toUpperCase(); + String lowercaseFn(value) => value.toLowerCase(); + test('merge returns merged object correctly', () { + final attr1 = uppercase(); + final attr2 = capitalize(); + final merged = attr1.merge(attr2); + expect(merged.directives?.length, 2); + }); + test('resolve returns correct TextDirective with default values', () { + const attr = TextMixAttribute(directives: []); + + expect(attr.directives, []); + }); + + test('Equality holds when all properties are the same', () { + final attr1 = TextMixAttribute(directives: [TextDirective(uppercaseFn)]); + final attr2 = TextMixAttribute(directives: [TextDirective(uppercaseFn)]); + expect(attr1, attr2); + }); + test('Equality fails when properties are different', () { + final attr1 = TextMixAttribute(directives: [TextDirective(uppercaseFn)]); + final attr2 = TextMixAttribute(directives: [TextDirective(lowercaseFn)]); + expect(attr1, isNot(attr2)); + }); + + group('UppercaseDirective', () { + test('modify returns correct value', () { + final attribute = uppercase(); + final modified = attribute.directives!.first('hello'); + expect(modified, 'HELLO'); + }); + }); + + group('CapitalizeDirective', () { + test('modify returns correct value', () { + final attribute = capitalize(); + final modified = attribute.directives!.first('hello'); + expect(modified, 'Hello'); + }); + }); + + group('LowercaseDirective', () { + test('modify returns correct value', () { + final attribute = lowercase(); + final modified = attribute.directives!.first('HELLO'); + expect(modified, 'hello'); + }); + }); + + group('SentenceCaseDirective', () { + test('modify returns correct value', () { + final attribute = sentenceCase(); + final modified = attribute.directives!.first('hello'); + expect(modified, 'Hello'); + }); + }); + + group('TitleCaseDirective', () { + test('modify returns correct value', () { + final attribute = titleCase(); + final modified = attribute.directives!.first('hello'); + expect(modified, 'Hello'); + }); + }); + + group('TextDirective', () { + test('Equality holds when all properties are the same', () { + final attr1 = uppercase(); + final attr2 = uppercase(); + expect(attr1, attr2); + }); + test('Equality fails when properties are different', () { + final attr1 = uppercase(); + final attr2 = lowercase(); + expect(attr1, isNot(attr2)); + }); + }); + }); +} diff --git a/test/src/attributes/text_style_attribute_test.dart b/test/src/attributes/text_style/text_style_dto_test.dart similarity index 72% rename from test/src/attributes/text_style_attribute_test.dart rename to test/src/attributes/text_style/text_style_dto_test.dart index c32b41ad8..7b0b795aa 100644 --- a/test/src/attributes/text_style_attribute_test.dart +++ b/test/src/attributes/text_style/text_style_dto_test.dart @@ -1,21 +1,22 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/attributes/text_style_attribute.dart'; +import 'package:mix/src/attributes/text_style/text_style_dto.dart'; -import '../../helpers/testing_utils.dart'; +import '../../../helpers/testing_utils.dart'; void main() { - group('TextStyleAttribute', () { + group('TextStyleDto', () { test('from constructor sets all values correctly', () { - final attr = TextStyleAttribute(color: Colors.red.toAttribute()); - expect(attr.color?.value, Colors.red); + final attr = TextStyleDto.only(color: Colors.red.toDto()); + final result = attr.resolve(EmptyMixData); + expect(result.color, Colors.red); }); test('merge returns merged object correctly', () { - final attr1 = TextStyleAttribute( - color: Colors.red.toAttribute(), + final attr1 = TextStyleDto.only( + color: Colors.red.toDto(), fontSize: 24.0, decoration: TextDecoration.underline, - decorationColor: Colors.blue.toAttribute(), + decorationColor: Colors.blue.toDto(), decorationStyle: TextDecorationStyle.dashed, fontWeight: FontWeight.bold, fontStyle: FontStyle.italic, @@ -26,11 +27,11 @@ void main() { textBaseline: TextBaseline.ideographic, ); - final attr2 = TextStyleAttribute( - color: Colors.blue.toAttribute(), + final attr2 = TextStyleDto.only( + color: Colors.blue.toDto(), fontSize: 30.0, decoration: TextDecoration.lineThrough, - decorationColor: Colors.red.toAttribute(), + decorationColor: Colors.red.toDto(), decorationStyle: TextDecorationStyle.dotted, fontWeight: FontWeight.w100, fontStyle: FontStyle.normal, @@ -41,12 +42,12 @@ void main() { textBaseline: TextBaseline.alphabetic, ); - final merged = attr1.merge(attr2); + final merged = attr1.merge(attr2).resolve(EmptyMixData); - expect(merged.color?.value, Colors.blue); + expect(merged.color, Colors.blue); expect(merged.fontSize, 30.0); expect(merged.decoration, TextDecoration.lineThrough); - expect(merged.decorationColor?.value, Colors.red); + expect(merged.decorationColor, Colors.red); expect(merged.decorationStyle, TextDecorationStyle.dotted); expect(merged.fontWeight, FontWeight.w100); expect(merged.fontStyle, FontStyle.normal); @@ -57,11 +58,11 @@ void main() { expect(merged.textBaseline, TextBaseline.alphabetic); }); test('resolve returns correct TextStyle with specific values', () { - final attr = TextStyleAttribute( - color: Colors.red.toAttribute(), + final attr = TextStyleDto.only( + color: Colors.red.toDto(), fontSize: 24.0, decoration: TextDecoration.underline, - decorationColor: Colors.blue.toAttribute(), + decorationColor: Colors.blue.toDto(), decorationStyle: TextDecorationStyle.dashed, fontWeight: FontWeight.bold, fontStyle: FontStyle.italic, @@ -90,13 +91,13 @@ void main() { return const Placeholder(); }); test('Equality holds when all attributes are the same', () { - final attr1 = TextStyleAttribute(color: Colors.red.toAttribute()); - final attr2 = TextStyleAttribute(color: Colors.red.toAttribute()); + final attr1 = TextStyleDto.only(color: Colors.red.toDto()); + final attr2 = TextStyleDto.only(color: Colors.red.toDto()); expect(attr1, attr2); }); test('Equality fails when attributes are different', () { - final attr1 = TextStyleAttribute(color: Colors.red.toAttribute()); - final attr2 = TextStyleAttribute(color: Colors.blue.toAttribute()); + final attr1 = TextStyleDto.only(color: Colors.red.toDto()); + final attr2 = TextStyleDto.only(color: Colors.blue.toDto()); expect(attr1, isNot(attr2)); }); }); diff --git a/test/src/attributes/text_style/text_style_util_test.dart b/test/src/attributes/text_style/text_style_util_test.dart new file mode 100644 index 000000000..fbb40a7fd --- /dev/null +++ b/test/src/attributes/text_style/text_style_util_test.dart @@ -0,0 +1,230 @@ +// Import necessary packages +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('TextStyleUtility', () { + test('call() creates TextStyleAttribute correctly', () { + final yellowPaint = Paint()..color = Colors.yellow; + final purplePaint = Paint()..color = Colors.purple; + final attr = text.style( + fontFamily: 'Roboto', + fontWeight: FontWeight.bold, + fontStyle: FontStyle.italic, + fontSize: 16.0, + letterSpacing: 1.0, + wordSpacing: 2.0, + debugLabel: 'debugLabel', + textBaseline: TextBaseline.ideographic, + shadows: [ + const Shadow( + blurRadius: 1.0, + color: Colors.black, + offset: Offset(1.0, 1.0), + ), + ], + color: Colors.red, + backgroundColor: Colors.blue, + fontFeatures: [const FontFeature.alternative(4)], + decoration: TextDecoration.underline, + decorationColor: Colors.green, + decorationStyle: TextDecorationStyle.dashed, + locale: const Locale('en', 'US'), + height: 2.0, + ); + + final attrWithPaint = text.style( + background: purplePaint, + foreground: yellowPaint, + ); + + final resolvedValue = attr.resolve(EmptyMixData); + final resolvedWithPaint = attrWithPaint.resolve(EmptyMixData); + + expect(resolvedValue.style?.fontWeight, FontWeight.bold); + expect(resolvedValue.style?.fontStyle, FontStyle.italic); + expect(resolvedValue.style?.fontSize, 16.0); + expect(resolvedValue.style?.letterSpacing, 1.0); + expect(resolvedValue.style?.wordSpacing, 2.0); + expect(resolvedValue.style?.textBaseline, TextBaseline.ideographic); + expect(resolvedValue.style?.shadows?.length, 1); + expect(resolvedValue.style?.shadows?.first.blurRadius, 1.0); + expect( + resolvedValue.style?.shadows?.first.color, + Colors.black, + ); + expect( + resolvedValue.style?.shadows?.first.offset, const Offset(1.0, 1.0)); + expect(resolvedValue.style?.color, Colors.red); + expect(resolvedValue.style?.backgroundColor, Colors.blue); + expect(resolvedValue.style?.fontFeatures?.length, 1); + expect( + resolvedValue.style?.fontFeatures?.first, + const FontFeature.alternative(4), + ); + expect( + resolvedValue.style?.decoration, + TextDecoration.underline, + ); + expect( + resolvedValue.style?.decorationColor, + Colors.green, + ); + expect(resolvedValue.style?.decorationStyle, TextDecorationStyle.dashed); + // expect(textStyleAttribute.resolve(EmptyMixData).foreground, yellowPaint); + // expect(textStyleAttribute.resolve(EmptyMixData).background, purplePaint); + expect(resolvedValue.style?.debugLabel, 'debugLabel'); + expect( + resolvedValue.style?.locale, + const Locale('en', 'US'), + ); + expect(resolvedValue.style?.height, 2.0); + expect(resolvedWithPaint.style?.foreground, yellowPaint); + expect(resolvedWithPaint.style?.background, purplePaint); + }); + + test('color() creates TextStyleAttribute correctly', () { + final textStyleAttribute = text.style(color: Colors.red); + final resolvedValue = textStyleAttribute.resolve(EmptyMixData); + + expect(resolvedValue.style?.color, Colors.red); + }); + + test('backgroundColor() creates TextStyleAttribute correctly', () { + final textStyleAttribute = text.style(backgroundColor: Colors.blue); + final resolvedValue = textStyleAttribute.resolve(EmptyMixData); + + expect(resolvedValue.style?.backgroundColor, Colors.blue); + }); + + test('fontFamily() creates TextStyleAttribute correctly', () { + final textStyleAttribute = text.style(fontFamily: 'Roboto'); + final resolvedValue = textStyleAttribute.resolve(EmptyMixData); + + expect(resolvedValue.style?.fontFamily, 'Roboto'); + }); + + test('fontSize() creates TextStyleAttribute correctly', () { + final textStyleAttribute = text.style(fontSize: 16.0); + final resolvedValue = textStyleAttribute.resolve(EmptyMixData); + + expect(resolvedValue.style?.fontSize, 16.0); + }); + + test('fontWeight() creates TextStyleAttribute correctly', () { + final textStyleAttribute = text.style.fontWeight.bold(); + final resolvedValue = textStyleAttribute.resolve(EmptyMixData); + + expect(resolvedValue.style?.fontWeight, FontWeight.bold); + }); + + test('fontStyle() creates TextStyleAttribute correctly', () { + final textStyleAttribute = text.style.fontStyle.italic(); + final resolvedValue = textStyleAttribute.resolve(EmptyMixData); + + expect(resolvedValue.style?.fontStyle, FontStyle.italic); + }); + + test('letterSpacing() creates TextStyleAttribute correctly', () { + final textStyleAttribute = text.style(letterSpacing: 1.0); + final resolvedValue = textStyleAttribute.resolve(EmptyMixData); + + expect(resolvedValue.style?.letterSpacing, 1.0); + }); + + test('wordSpacing() creates TextStyleAttribute correctly', () { + final textStyleAttribute = text.style(wordSpacing: 2.0); + final resolvedValue = textStyleAttribute.resolve(EmptyMixData); + + expect(resolvedValue.style?.wordSpacing, 2.0); + }); + + test('textBaseline() creates TextStyleAttribute correctly', () { + final textStyleAttribute = text.style.textBaseline.ideographic(); + final resolvedValue = textStyleAttribute.resolve(EmptyMixData); + + expect(resolvedValue.style?.textBaseline, TextBaseline.ideographic); + }); + + test('shadows() creates TextStyleAttribute correctly', () { + const shadow = Shadow( + blurRadius: 1.0, + color: Colors.black, + offset: Offset(1.0, 1.0), + ); + final attribute = text.style.shadow( + blurRadius: 1.0, + color: Colors.black, + offset: const Offset(1.0, 1.0), + ); + + final resolved = attribute.resolve(EmptyMixData); + + expect(resolved.style?.shadows?.length, 1); + expect(resolved.style?.shadows?.first.blurRadius, 1.0); + expect(resolved.style?.shadows?.first.color, Colors.black); + expect(resolved.style?.shadows?.first.offset, const Offset(1.0, 1.0)); + expect(resolved.style?.shadows?.first, shadow); + }); + + test('fontFeatures() creates TextStyleAttribute correctly', () { + final textStyleAttribute = text.style.fontFeatures([ + const FontFeature.alternative(4), + ]); + + final resolvedValue = textStyleAttribute.resolve(EmptyMixData); + + expect(resolvedValue.style?.fontFeatures?.length, 1); + expect( + resolvedValue.style?.fontFeatures?.first, + const FontFeature.alternative(4), + ); + }); + + test('decoration() creates TextStyleAttribute correctly', () { + final textStyleAttribute = text.style.decoration.underline(); + + final resolvedValue = textStyleAttribute.resolve(EmptyMixData); + + expect(resolvedValue.style?.decoration, TextDecoration.underline); + }); + + test('decorationColor() creates TextStyleAttribute correctly', () { + final textStyleAttribute = text.style(decorationColor: Colors.green); + final resolvedValue = textStyleAttribute.resolve(EmptyMixData); + + expect(resolvedValue.style?.decorationColor, Colors.green); + }); + + test('decorationStyle() creates TextStyleAttribute correctly', () { + final textStyleAttribute = text.style.decorationStyle.dashed(); + + final resolvedValue = textStyleAttribute.resolve(EmptyMixData); + + expect(resolvedValue.style?.decorationStyle, TextDecorationStyle.dashed); + }); + + test('foreground() creates TextStyleAttribute correctly', () { + final yellowPaint = Paint()..color = Colors.yellow; + final textStyleAttribute = text.style.foreground(yellowPaint); + + final resolvedValue = textStyleAttribute.resolve(EmptyMixData); + + expect(resolvedValue.style?.foreground, yellowPaint); + }); + + test('background() creates TextStyleAttribute correctly', () { + final purplePaint = Paint()..color = Colors.purple; + final textStyleAttribute = text.style.background(purplePaint); + + final resolvedValue = textStyleAttribute.resolve(EmptyMixData); + + expect(resolvedValue.style?.background, purplePaint); + }); + }); +} diff --git a/test/src/helpers/extensions/string_ext_test.dart b/test/src/core/extensions/string_ext_test.dart similarity index 98% rename from test/src/helpers/extensions/string_ext_test.dart rename to test/src/core/extensions/string_ext_test.dart index 52edf2f84..9bd78c746 100644 --- a/test/src/helpers/extensions/string_ext_test.dart +++ b/test/src/core/extensions/string_ext_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/helpers/extensions/string_ext.dart'; +import 'package:mix/src/helpers/string_ext.dart'; void main() { group('StringExt', () { diff --git a/test/src/core/extensions/values_ext_test.dart b/test/src/core/extensions/values_ext_test.dart new file mode 100644 index 000000000..87b0d5b81 --- /dev/null +++ b/test/src/core/extensions/values_ext_test.dart @@ -0,0 +1,248 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; +import 'package:mix/src/attributes/constraints/constraints_dto.dart'; +import 'package:mix/src/attributes/decoration/decoration_dto.dart'; +import 'package:mix/src/attributes/strut_style/strut_style_dto.dart'; +import 'package:mix/src/attributes/text_style/text_style_dto.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('Extensions', () { + test('StrutStyle', () { + const value = StrutStyle( + fontFamily: 'Roboto', + fontFamilyFallback: ['Arial', 'Helvetica'], + fontSize: 14.0, + fontWeight: FontWeight.bold, + fontStyle: FontStyle.italic, + height: 1.2, + leading: 5.0, + forceStrutHeight: true, + ); + + final dto = StrutStyleDto.from(value); + final attribute = StrutStyleAttribute(dto); + + expect(value.toAttribute(), isA()); + expect(value.toAttribute(), attribute); + + expect(attribute.value, dto); + + // Resolves correctly + expect(attribute.resolve(EmptyMixData), value); + }); + + test('AlignmentGeometry', () { + const alignment = Alignment.topCenter; + + const attribute = AlignmentGeometryAttribute(alignment); + + expect(alignment.toAttribute(), attribute); + + expect(attribute.value, alignment); + }); + + test('ShapeDecoration', () { + final value = ShapeDecoration( + shape: Border.all(), + gradient: const RadialGradient(colors: [Colors.red, Colors.blue]), + shadows: const [BoxShadow(blurRadius: 5.0)], + ); + + final dto = ShapeDecorationDto.from(value); + final attribute = DecorationAttribute(dto); + + expect(value.toAttribute(), isA()); + expect(value.toAttribute(), attribute); + expect(value.toDto(), isA()); + expect(value.toDto(), dto); + + expect(attribute.value, dto); + + // Resolves correctly + expect(attribute.resolve(EmptyMixData), value); + }); + + test('BoxConstraints toAttribute', () { + const value = BoxConstraints( + minWidth: 100.0, + maxWidth: 200.0, + minHeight: 150.0, + maxHeight: 250.0, + ); + + final dto = BoxConstraintsDto.from(value); + final attribute = BoxConstraintsAttribute(dto); + + expect(value.toAttribute(), isA()); + expect(value.toAttribute(), attribute); + expect(value.toDto(), isA()); + expect(value.toDto(), dto); + + expect(attribute.value, dto); + + // Resolves correctly + expect(attribute.resolve(EmptyMixData), value); + }); + + test('Axis toAttribute', () { + const value = Axis.horizontal; + + const attribute = AxisAttribute(value); + + expect(attribute.value, Axis.horizontal); + }); + + test('BoxDecoration toAttribute', () { + final value = BoxDecoration( + color: Colors.blue, + border: Border.all(), + borderRadius: BorderRadius.circular(10.0), + gradient: const LinearGradient(colors: [Colors.red, Colors.blue]), + boxShadow: const [BoxShadow(blurRadius: 5.0)], + ); + + final dto = BoxDecorationDto.from(value); + final attribute = DecorationAttribute(dto); + + expect(value.toAttribute(), isA()); + expect(value.toAttribute(), attribute); + expect(value.toDto(), isA()); + expect(value.toDto(), dto); + + expect(attribute.value, dto); + + // Resolves correctly + expect(attribute.resolve(EmptyMixData), value); + }); + + test('BorderRadiusGeometry', () { + final value = BorderRadius.circular(10.0); + + final dto = BorderRadiusGeometryDto.from(value); + final attribute = BorderRadiusGeometryAttribute(dto); + + expect(value.toAttribute(), isA()); + expect(value.toAttribute(), attribute); + expect(value.toDto(), isA()); + expect(value.toDto(), dto); + + expect(attribute.value, dto); + + // Resolves correctly + expect(attribute.resolve(EmptyMixData), value); + }); + + test('Matrix4 toAttribute', () { + final matrix4 = Matrix4.identity(); + final attribute = matrix4.toAttribute(); + expect(attribute.value, Matrix4.identity()); + }); + + test('BorderSide', () { + const value = BorderSide( + color: Colors.blue, + width: 2.0, + style: BorderStyle.solid, + ); + + final dto = BorderSideDto.from(value); + + expect(value.toDto(), dto); + expect(dto.color, const ColorDto(Colors.blue)); + expect(dto.width, 2.0); + expect(dto.style, BorderStyle.solid); + + // Resolves correctly + expect(dto.resolve(EmptyMixData), value); + }); + + test('BoxBorder', () { + // Border + const value = Border( + top: BorderSide(color: Colors.red), + bottom: BorderSide(color: Colors.blue), + ); + + final dto = BoxBorderDto.from(value); + final attribute = BoxBorderAttribute(dto); + + expect(value.toAttribute(), isA()); + expect(value.toAttribute(), attribute); + expect(value.toDto(), isA()); + + expect(attribute.value, dto); + + // Resolves correctly + expect(attribute.resolve(EmptyMixData), value); + + // BorderDirectional + const value2 = BorderDirectional( + top: BorderSide(color: Colors.red), + bottom: BorderSide(color: Colors.blue), + ); + + final dto2 = BoxBorderDto.from(value2); + final attribute2 = BoxBorderAttribute(dto2); + + expect(value2.toAttribute(), isA()); + expect(value2.toAttribute(), attribute2); + expect(value2.toDto(), isA()); + + expect(attribute2.value, dto2); + + // Resolves correctly + expect(attribute2.resolve(EmptyMixData), value2); + }); + + test('Shadow', () { + const value = BoxShadow(blurRadius: 10.0, color: Colors.black); + + final dto = BoxShadowDto.from(value); + + expect(value.toDto(), dto); + + expect(dto.blurRadius, 10.0); + expect(dto.color, const ColorDto(Colors.black)); + + // Resolves correctly + expect(dto.resolve(EmptyMixData), value); + }); + + test('BoxShadow', () { + const value = BoxShadow(blurRadius: 5.0, color: Colors.grey); + + final dto = BoxShadowDto.from(value); + + expect(value.toDto(), dto); + expect(dto.blurRadius, 5.0); + expect(dto.color, const ColorDto(Colors.grey)); + + // Resolves correctly + expect(dto.resolve(EmptyMixData), value); + }); + + test('TextStyle ', () { + const value = TextStyle( + color: Colors.black, + fontSize: 16.0, + fontWeight: FontWeight.bold, + ); + + final dto = TextStyleDto.from(value); + final attribute = TextStyleAttribute(dto); + + expect(value.toAttribute(), isA()); + expect(value.toAttribute(), attribute); + expect(value.toDto(), isA()); + expect(value.toDto(), dto); + + expect(attribute.value, dto); + + // Resolves correctly + expect(attribute.resolve(EmptyMixData), value); + }); + }); +} diff --git a/test/src/decorators/clip_decorator_test.dart b/test/src/decorators/clip_decorator_test.dart index f1a9cc6bc..33e712663 100644 --- a/test/src/decorators/clip_decorator_test.dart +++ b/test/src/decorators/clip_decorator_test.dart @@ -4,8 +4,8 @@ import 'package:mix/src/decorators/clip_decorator.dart'; import '../../helpers/testing_utils.dart'; -class CustomRectClipper extends CustomClipper { - const CustomRectClipper(); +class _CustomRectClipper extends CustomClipper { + const _CustomRectClipper(); @override Rect getClip(Size size) => Rect.fromLTWH(0, 0, size.width, size.height); @@ -13,8 +13,8 @@ class CustomRectClipper extends CustomClipper { bool shouldReclip(CustomClipper oldClipper) => false; } -class CustomRRectClipper extends CustomClipper { - const CustomRRectClipper(); +class _CustomRRectClipper extends CustomClipper { + const _CustomRRectClipper(); @override RRect getClip(Size size) => RRect.fromLTRBR(0, 0, size.width, size.height, const Radius.circular(10)); @@ -23,138 +23,24 @@ class CustomRRectClipper extends CustomClipper { bool shouldReclip(CustomClipper oldClipper) => false; } -void main() { - group('ClipDecoratorData', () { - // equality - test('equality', () { - const decoratorData = ClipDecoratorData( - clipBehavior: Clip.antiAlias, - clipper: null, - ); - const otherDecoratorData = ClipDecoratorData( - clipBehavior: Clip.antiAlias, - clipper: null, - ); - expect(decoratorData, otherDecoratorData); - }); - - test('copyWith', () { - const decoratorData = ClipDecoratorData( - clipBehavior: Clip.antiAlias, - clipper: null, - ); - - final copied = decoratorData.copyWith( - clipBehavior: Clip.hardEdge, - clipper: const CustomRRectClipper(), - ); - - expect(decoratorData, isNotNull); - expect(decoratorData.clipBehavior, Clip.antiAlias); - expect(decoratorData.clipper, isNull); - expect(copied, isNotNull); - expect(copied.clipBehavior, Clip.hardEdge); - expect(copied.clipper, isA()); - }); - - test('lerp', () { - const clipDecorator = ClipDecoratorData( - clipBehavior: Clip.antiAlias, - clipper: null, - ); - const other = ClipDecoratorData( - clipBehavior: Clip.hardEdge, - clipper: CustomRRectClipper(), - ); - - final lerped = clipDecorator.lerp(other, 0.4); - - expect(lerped, isNotNull); - expect(lerped.clipBehavior, Clip.antiAlias); - expect(lerped.clipper, isNull); - }); - }); - - group('ClipRRectDecoratorData', () { - // equality - test('equality', () { - const decoratorData = ClipRRectDecoratorData( - clipBehavior: Clip.antiAlias, - clipper: null, - borderRadius: BorderRadius.zero, - ); - const otherDecoratorData = ClipRRectDecoratorData( - clipBehavior: Clip.antiAlias, - clipper: null, - borderRadius: BorderRadius.zero, - ); - expect(decoratorData, otherDecoratorData); - }); - - test('copyWith', () { - const decoratorData = ClipRRectDecoratorData( - clipBehavior: Clip.antiAlias, - clipper: null, - borderRadius: BorderRadius.zero, - ); - - final copied = decoratorData.copyWith( - clipBehavior: Clip.hardEdge, - clipper: const CustomRRectClipper(), - borderRadius: BorderRadius.circular(10.0), - ); - - expect(decoratorData, isNotNull); - expect(decoratorData.clipBehavior, Clip.antiAlias); - expect(decoratorData.clipper, isNull); - expect(decoratorData.borderRadius, BorderRadius.zero); - expect(copied, isNotNull); - expect(copied.clipBehavior, Clip.hardEdge); - expect(copied.clipper, isA()); - expect(copied.borderRadius, BorderRadius.circular(10.0)); - }); - - test('lerp', () { - const clipDecorator = ClipRRectDecoratorData( - clipBehavior: Clip.antiAlias, - clipper: null, - borderRadius: BorderRadius.zero, - ); - final other = ClipRRectDecoratorData( - clipBehavior: Clip.hardEdge, - clipper: const CustomRRectClipper(), - borderRadius: BorderRadius.circular(10.0), - ); - - final lerped = clipDecorator.lerp(other, 0.4); +class _CustomPathClipper extends CustomClipper { + const _CustomPathClipper(); + @override + Path getClip(Size size) { + final path = Path(); + path.moveTo(size.width / 2, 0.0); + path.lineTo(size.width, size.height); + path.lineTo(0.0, size.height); + path.close(); - expect(lerped, isNotNull); - expect(lerped.clipBehavior, Clip.antiAlias); - expect(lerped.clipper, isNull); - expect( - lerped.borderRadius, - BorderRadius.lerp( - BorderRadius.zero, - BorderRadius.circular(10.0), - 0.4, - ), - ); + return path; + } - final lerped2 = clipDecorator.lerp(other, 0.6); + @override + bool shouldReclip(CustomClipper oldClipper) => false; +} - expect(lerped2, isNotNull); - expect(lerped2.clipBehavior, Clip.hardEdge); - expect(lerped2.clipper, isA()); - expect( - lerped2.borderRadius, - BorderRadius.lerp( - BorderRadius.zero, - BorderRadius.circular(10.0), - 0.6, - ), - ); - }); - }); +void main() { group('ClipOvalDecorator', () { testWidgets( 'renders', @@ -165,7 +51,7 @@ void main() { // Build our app and trigger a frame await tester.pumpWidget( MaterialApp( - home: clipDecorator.render( + home: clipDecorator.build( Container(color: Colors.blue), EmptyMixData, ), @@ -185,95 +71,69 @@ void main() { test('merge', () { const clipDecorator = ClipOvalDecorator(clipBehavior: Clip.antiAlias); - const other = ClipOvalDecorator(clipper: CustomRectClipper()); + const other = ClipOvalDecorator(clipper: _CustomRectClipper()); final merged = clipDecorator.merge(other); expect(merged, isNotNull); expect(merged.clipBehavior, Clip.antiAlias); - expect(merged.clipper, isA()); - }); - - test('resolve', () { - const clipDecorator = ClipOvalDecorator( - clipBehavior: Clip.hardEdge, clipper: CustomRectClipper()); - final resolved = clipDecorator.resolve(EmptyMixData); - expect(resolved, isNotNull); - expect(resolved.clipBehavior, Clip.hardEdge); - expect(resolved.clipper, isA()); + expect(merged.clipper, isA<_CustomRectClipper>()); }); }); - group('ClipRRectDecorator', () { + + // ClipPathDecorator + group('ClipPathDecorator', () { testWidgets( 'renders', (WidgetTester tester) async { - // Define the radius you want to test - final testRadius = BorderRadius.circular(10.0); - // Create a ClipDecorator - final clipDecorator = ClipRRectDecorator(borderRadius: testRadius); + const clipDecorator = ClipPathDecorator( + clipper: _CustomPathClipper(), + ); // Build our app and trigger a frame await tester.pumpWidget( MaterialApp( - home: clipDecorator.render( + home: clipDecorator.build( Container(color: Colors.blue), EmptyMixData, ), ), ); - final finder = find.byType(ClipRRect); + final finder = find.byType(ClipPath); final context = tester.element(finder); - final clipRRectWidget = context.widget as ClipRRect; + final clipPathWidget = context.widget as ClipPath; - // Verify that the ClipRRect widget is in the tree and has the correct radius + // Verify that the ClipPath widget is in the tree expect(finder, findsOneWidget); - expect(clipRRectWidget.borderRadius, testRadius); - expect(clipRRectWidget, isA()); + expect(clipPathWidget, isNotNull); + expect(clipPathWidget, isA()); }, ); test('merge', () { - const clipDecorator = ClipRRectDecorator( - clipBehavior: Clip.antiAlias, - borderRadius: BorderRadius.zero, - ); - final other = ClipRRectDecorator( - clipper: const CustomRRectClipper(), - clipBehavior: Clip.hardEdge, - borderRadius: BorderRadius.circular(10.0), - ); + const clipDecorator = ClipPathDecorator(clipBehavior: Clip.antiAlias); + const other = ClipPathDecorator(clipper: _CustomPathClipper()); final merged = clipDecorator.merge(other); expect(merged, isNotNull); - expect(merged.clipBehavior, Clip.hardEdge); - expect(merged.borderRadius, BorderRadius.circular(10.0)); - expect(merged.clipper, isA()); - }); - - test('resolve', () { - final clipDecorator = ClipRRectDecorator( - clipper: const CustomRRectClipper(), - clipBehavior: Clip.hardEdge, - borderRadius: BorderRadius.circular(10.0), - ); - final resolved = clipDecorator.resolve(EmptyMixData); - expect(resolved, isNotNull); - expect(resolved.clipBehavior, Clip.hardEdge); - expect(resolved.borderRadius, BorderRadius.circular(10.0)); - expect(resolved.clipper, isA()); + expect(merged.clipBehavior, Clip.antiAlias); + expect(merged.clipper, isA<_CustomPathClipper>()); }); }); - group('ClipRectDecorator test', () { + // ClipRectDecorator + group('ClipRectDecorator', () { testWidgets( 'renders', (WidgetTester tester) async { // Create a ClipDecorator - const clipDecorator = ClipRectDecorator(); + const clipDecorator = ClipRectDecorator( + clipper: _CustomRectClipper(), + ); // Build our app and trigger a frame await tester.pumpWidget( MaterialApp( - home: clipDecorator.render( + home: clipDecorator.build( Container(color: Colors.blue), EmptyMixData, ), @@ -293,70 +153,52 @@ void main() { test('merge', () { const clipDecorator = ClipRectDecorator(clipBehavior: Clip.antiAlias); - const other = ClipRectDecorator(clipper: CustomRectClipper()); + const other = ClipRectDecorator(clipper: _CustomRectClipper()); final merged = clipDecorator.merge(other); expect(merged, isNotNull); expect(merged.clipBehavior, Clip.antiAlias); - expect(merged.clipper, isA()); - }); - - test('resolve', () { - const clipDecorator = ClipRectDecorator( - clipBehavior: Clip.hardEdge, clipper: CustomRectClipper()); - final resolved = clipDecorator.resolve(EmptyMixData); - expect(resolved, isNotNull); - expect(resolved.clipBehavior, Clip.hardEdge); - expect(resolved.clipper, isA()); + expect(merged.clipper, isA<_CustomRectClipper>()); }); }); - group('ClipPathDecorator', () { + // ClipRRectDecorator + group('ClipRRectDecorator', () { testWidgets( 'renders', (WidgetTester tester) async { - const clipPathDecorator = ClipPathDecorator(clipper: TriangleClipper()); + // Create a ClipDecorator + const clipDecorator = ClipRRectDecorator( + clipper: _CustomRRectClipper(), + ); + // Build our app and trigger a frame await tester.pumpWidget( MaterialApp( - home: clipPathDecorator.render( + home: clipDecorator.build( Container(color: Colors.blue), EmptyMixData, ), ), ); - final finder = find.byType(ClipPath); + final finder = find.byType(ClipRRect); final context = tester.element(finder); - final clipPathWidget = context.widget as ClipPath; + final clipRRectWidget = context.widget as ClipRRect; - // Verify that the ClipPath widget is in the tree and has the correct path + // Verify that the ClipRRect widget is in the tree expect(finder, findsOneWidget); - expect(clipPathWidget.clipper, isA()); - expect(clipPathWidget, isA()); + expect(clipRRectWidget, isNotNull); + expect(clipRRectWidget, isA()); }, ); test('merge', () { - const clipPathDecorator = ClipPathDecorator( - clipper: TriangleClipper(), - clipBehavior: Clip.antiAlias, - ); - const other = ClipPathDecorator(clipper: TriangleClipper()); - final merged = clipPathDecorator.merge(other); + const clipDecorator = ClipRRectDecorator(clipBehavior: Clip.antiAlias); + const other = ClipRRectDecorator(clipper: _CustomRRectClipper()); + final merged = clipDecorator.merge(other); expect(merged, isNotNull); expect(merged.clipBehavior, Clip.antiAlias); - expect(merged.clipper, isA()); - }); - - test('resolve', () { - const clipPathDecorator = ClipPathDecorator( - clipBehavior: Clip.hardEdge, - clipper: TriangleClipper(), - ); - final resolved = clipPathDecorator.resolve(EmptyMixData); - expect(resolved, isNotNull); - expect(resolved.clipBehavior, Clip.hardEdge); - expect(resolved.clipper, isA()); + expect(merged.clipper, isA<_CustomRRectClipper>()); }); }); } diff --git a/test/src/decorators/default_decorators_test.dart b/test/src/decorators/default_decorators_test.dart index 890ea5a8d..986a36047 100644 --- a/test/src/decorators/default_decorators_test.dart +++ b/test/src/decorators/default_decorators_test.dart @@ -20,7 +20,7 @@ void main() { // Build our app and trigger a frame await tester.pumpWidget( MaterialApp( - home: aspectRatioDecorator.render( + home: aspectRatioDecorator.build( Container(color: Colors.blue), EmptyMixData, ), @@ -56,7 +56,7 @@ void main() { // Build a widget with the merged decorator and trigger a frame await tester.pumpWidget( MaterialApp( - home: mergedDecorator.render( + home: mergedDecorator.build( Container(color: Colors.red), EmptyMixData, ), @@ -90,7 +90,7 @@ void main() { // Build our app and trigger a frame await tester.pumpWidget( MaterialApp( - home: opacityDecorator.render( + home: opacityDecorator.build( Container(color: Colors.blue), EmptyMixData, ), @@ -124,7 +124,7 @@ void main() { // Build a widget with the merged decorator and trigger a frame await tester.pumpWidget( MaterialApp( - home: mergedDecorator.render( + home: mergedDecorator.build( Container(color: Colors.red), EmptyMixData, ), @@ -158,7 +158,7 @@ void main() { // Build our app and trigger a frame await tester.pumpWidget( MaterialApp( - home: rotateDecorator.render( + home: rotateDecorator.build( Container(color: Colors.blue), EmptyMixData, ), @@ -192,7 +192,7 @@ void main() { // Build a widget with the merged decorator and trigger a frame await tester.pumpWidget( MaterialApp( - home: mergedDecorator.render( + home: mergedDecorator.build( Container(color: Colors.red), EmptyMixData, ), @@ -226,7 +226,7 @@ void main() { // Build our app and trigger a frame await tester.pumpWidget( MaterialApp( - home: scaleDecorator.render( + home: scaleDecorator.build( Container(color: Colors.blue), EmptyMixData, ), @@ -261,7 +261,7 @@ void main() { // Build a widget with the merged decorator and trigger a frame await tester.pumpWidget( MaterialApp( - home: mergedDecorator.render( + home: mergedDecorator.build( Container(color: Colors.red), EmptyMixData, ), @@ -301,7 +301,7 @@ void main() { MaterialApp( home: Column( children: [ - flexibleDecorator.render( + flexibleDecorator.build( Container(color: Colors.blue), EmptyMixData, ), @@ -339,7 +339,7 @@ void main() { MaterialApp( home: Column( children: [ - mergedDecorator.render( + mergedDecorator.build( Container(color: Colors.red), EmptyMixData, ), diff --git a/test/src/directives/text_directive_test.dart b/test/src/directives/text_directive_test.dart deleted file mode 100644 index d71b6f8d7..000000000 --- a/test/src/directives/text_directive_test.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/mix.dart'; - -import '../../helpers/testing_utils.dart'; - -void main() { - group('TextDirectiveAttribute', () { - test('merge returns merged object correctly', () { - const attr1 = TextDirectiveAttribute([UppercaseDirective()]); - const attr2 = TextDirectiveAttribute([CapitalizeDirective()]); - final merged = attr1.merge(attr2); - expect(merged.value, - [const UppercaseDirective(), const CapitalizeDirective()]); - }); - test('resolve returns correct TextDirective with default values', () { - const attr = TextDirectiveAttribute([]); - final directive = attr.resolve(EmptyMixData); - expect(directive, []); - }); - test('resolve returns correct TextDirective with specific values', () { - const attr = - TextDirectiveAttribute([UppercaseDirective(), CapitalizeDirective()]); - final directive = attr.resolve(EmptyMixData); - expect( - directive, [const UppercaseDirective(), const CapitalizeDirective()]); - }); - test('Equality holds when all properties are the same', () { - const attr1 = TextDirectiveAttribute([UppercaseDirective()]); - const attr2 = TextDirectiveAttribute([UppercaseDirective()]); - expect(attr1, attr2); - }); - test('Equality fails when properties are different', () { - const attr1 = TextDirectiveAttribute([UppercaseDirective()]); - const attr2 = TextDirectiveAttribute([CapitalizeDirective()]); - expect(attr1, isNot(attr2)); - }); - - group('UppercaseDirective', () { - test('modify returns correct value', () { - const directive = UppercaseDirective(); - final modified = directive.modify('hello'); - expect(modified, 'HELLO'); - }); - }); - - group('CapitalizeDirective', () { - test('modify returns correct value', () { - const directive = CapitalizeDirective(); - final modified = directive.modify('hello'); - expect(modified, 'Hello'); - }); - }); - - group('LowercaseDirective', () { - test('modify returns correct value', () { - const directive = LowercaseDirective(); - final modified = directive.modify('HELLO'); - expect(modified, 'hello'); - }); - }); - - group('SentenceCaseDirective', () { - test('modify returns correct value', () { - const directive = SentenceCaseDirective(); - final modified = directive.modify('hello'); - expect(modified, 'Hello'); - }); - }); - - group('TitleCaseDirective', () { - test('modify returns correct value', () { - const directive = TitleCaseDirective(); - final modified = directive.modify('hello'); - expect(modified, 'Hello'); - }); - }); - - group('TextDirective', () { - test('Equality holds when all properties are the same', () { - const attr1 = TextDirectiveAttribute([UppercaseDirective()]); - const attr2 = TextDirectiveAttribute([UppercaseDirective()]); - expect(attr1, attr2); - }); - test('Equality fails when properties are different', () { - const attr1 = TextDirectiveAttribute([UppercaseDirective()]); - const attr2 = TextDirectiveAttribute([CapitalizeDirective()]); - expect(attr1, isNot(attr2)); - }); - }); - }); -} diff --git a/test/src/factory/mix_provider_data_test.dart b/test/src/factory/mix_provider_data_test.dart index 290dfddb6..cabbed2ee 100644 --- a/test/src/factory/mix_provider_data_test.dart +++ b/test/src/factory/mix_provider_data_test.dart @@ -27,21 +27,21 @@ void main() { // Add any other additional assertions that are specific to your use case. // If you become able to access properties _attributes and _resolver you would assert: - expect(mixData.attributes, isInstanceOf()); - expect(mixData.resolver, isInstanceOf()); + expect(mixData.attributes, isInstanceOf()); + expect(mixData.tokens, isInstanceOf()); expect(mixData.attributes.length, 4); - expect(mixData.attributeOfType(), + expect(mixData.attributeOf(), isInstanceOf()); - expect(mixData.attributeOfType(), + expect(mixData.attributeOf(), const MockStringScalarAttribute('test')); - expect(mixData.attributeOfType(), + expect(mixData.attributeOf(), isInstanceOf()); - expect(mixData.attributeOfType(), + expect(mixData.attributeOf(), const MockDoubleScalarAttribute(2.0)); expect(mixData.decoratorOfType(), - isInstanceOf>()); - expect(mixData.attributeOfType(), + isInstanceOf>()); + expect(mixData.attributeOf(), const MockDoubleDecoratorAttribute(0.5)); }); @@ -72,20 +72,20 @@ void main() { expect(mergedMixData, isInstanceOf()); expect(mergedMixData.attributes.length, 4); - expect(mergedMixData.attributeOfType(), + expect(mergedMixData.attributeOf(), isInstanceOf()); - expect(mergedMixData.attributeOfType(), + expect(mergedMixData.attributeOf(), isInstanceOf()); - expect(mergedMixData.attributeOfType(), + expect(mergedMixData.attributeOf(), isInstanceOf()); expect(mergedMixData.decoratorOfType(), - isInstanceOf>()); + isInstanceOf>()); - expect(mergedMixData.attributeOfType(), + expect(mergedMixData.attributeOf(), const MockIntScalarAttribute(1)); - expect(mergedMixData.attributeOfType(), + expect(mergedMixData.attributeOf(), const MockStringScalarAttribute('test')); - expect(mergedMixData.attributeOfType(), + expect(mergedMixData.attributeOf(), const MockDoubleScalarAttribute(4.0)); expect(mergedMixData.decoratorOfType().first, const MockDoubleDecoratorAttribute(0.6)); diff --git a/test/src/helpers/extensions/style_mix_ext_test.dart b/test/src/factory/style_mix_ext_test.dart similarity index 92% rename from test/src/helpers/extensions/style_mix_ext_test.dart rename to test/src/factory/style_mix_ext_test.dart index 93fc00976..d1528ed50 100644 --- a/test/src/helpers/extensions/style_mix_ext_test.dart +++ b/test/src/factory/style_mix_ext_test.dart @@ -8,7 +8,13 @@ void main() { final keyThree = UniqueKey(); testWidgets('StyleMix.container matches StyledContainer(style:StyleMix)', (tester) async { - final style = StyleMix(boxDecoration(border: border(color: Colors.red))); + final style = StyleMix( + box.decoration( + border: Border.all( + color: Colors.red, + ), + ), + ); await tester.pumpWidget( MaterialApp( @@ -42,8 +48,8 @@ void main() { testWidgets('StyleMix.box matches StyledContainer(style:StyleMix)', (tester) async { final style = StyleMix( - boxDecoration( - border: border(color: Colors.red), + box.decoration( + border: Border.all(color: Colors.red), ), ); @@ -77,7 +83,8 @@ void main() { }); testWidgets('StyleMix.hbox matches HBox(style:StyleMix)', (tester) async { - final style = StyleMix(boxDecoration(border: border(color: Colors.red))); + final style = + StyleMix(box.decoration(border: Border.all(color: Colors.red))); await tester.pumpWidget( MaterialApp( @@ -109,7 +116,8 @@ void main() { }); testWidgets('StyleMix.row matches StyledRow(style:StyleMix)', (tester) async { - final style = StyleMix(boxDecoration(border: border(color: Colors.red))); + final style = + StyleMix(box.decoration(border: Border.all(color: Colors.red))); await tester.pumpWidget( MaterialApp( @@ -174,7 +182,8 @@ void main() { }); testWidgets('StyleMix.vbox matches VBox(style:StyleMix)', (tester) async { - final style = StyleMix(boxDecoration(border: border(color: Colors.red))); + final style = + StyleMix(box.decoration(border: Border.all(color: Colors.red))); await tester.pumpWidget( MaterialApp( @@ -207,7 +216,8 @@ void main() { testWidgets('StyleMix.column matches StyledColumn(style:StyleMix)', (tester) async { - final style = StyleMix(boxDecoration(border: border(color: Colors.red))); + final style = + StyleMix(box.decoration(border: Border.all(color: Colors.red))); await tester.pumpWidget( MaterialApp( @@ -240,7 +250,7 @@ void main() { testWidgets('StyleMix.icon matches StyledIcon(style:StyleMix)', (tester) async { - final style = StyleMix(iconColor(Colors.black)); + final style = StyleMix(icon(color: Colors.black)); await tester.pumpWidget( MaterialApp( diff --git a/test/src/factory/style_mix_test.dart b/test/src/factory/style_mix_test.dart index 83c0e5e02..72b68d4b8 100644 --- a/test/src/factory/style_mix_test.dart +++ b/test/src/factory/style_mix_test.dart @@ -1,6 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; -import 'package:mix/src/variants/multi_variant.dart'; import '../../helpers/testing_utils.dart'; @@ -22,7 +21,7 @@ void main() { final mix = StyleMix(null, attribute1, null); expect(mix.styles.length, 1); expect(mix.variants.isEmpty, true); - expect(mix.styles[0], attribute1); + expect(mix.styles.values[0], attribute1); }); test('Initialization with All Non-Null ScalarAttributes', () { @@ -88,7 +87,7 @@ void main() { attribute4, const MockInvalidAttribute(), ]; - expect(() => StyleMix.create(attributes), throwsAssertionError); + expect(() => StyleMix.create(attributes), throwsUnsupportedError); }); }); @@ -171,7 +170,7 @@ void main() { final mix = StyleMix.chooser(true, trueStyle, falseStyle); expect(mix.styles.length, 1); - expect(mix.styles[0], trueAttribute); + expect(mix.styles.values[0], trueAttribute); }); test('Condition is False', () { @@ -184,7 +183,7 @@ void main() { final mix = StyleMix.chooser(false, trueStyle, falseStyle); expect(mix.styles.length, 1); - expect(mix.styles[0], falseAttribute); + expect(mix.styles.values[0], falseAttribute); }); test('Both ifTrue and ifFalse Are Same', () { @@ -196,7 +195,7 @@ void main() { final style = StyleMix.chooser(true, sameStyle, otherStyle); expect(style.styles.length, 1); - expect(style.styles[0], sameAttribute); + expect(style.styles.values[0], sameAttribute); expect(sameStyle, style); }); }); @@ -219,7 +218,7 @@ void main() { }); test('with matching multi variant', () { - final multiVariant = MultiVariant(const [variantAttr1, variantAttr2]); + final multiVariant = MultiVariant.and(const [variantAttr1, variantAttr2]); final style = StyleMix(attr1, attr2, multiVariant(attr3)); final firstStyle = style.selectVariant(variantAttr1); final secondStyle = firstStyle.selectVariant(variantAttr2); @@ -265,7 +264,7 @@ void main() { }); test('with matching multi variant', () { - final multiVariant = MultiVariant(const [variantAttr1, variantAttr2]); + final multiVariant = MultiVariant.and(const [variantAttr1, variantAttr2]); final style = StyleMix(attr1, attr2, multiVariant(attr3)); final thirdStyle = style.selectVariantList([variantAttr1, variantAttr2]); @@ -343,7 +342,7 @@ void main() { final style1 = StyleMix(attribute1, attribute2); final style2 = StyleMix(attribute1, attribute2); - expect(style1.hashCode, isNot(style2.hashCode)); + expect(style1.hashCode, equals(style2.hashCode)); }); test('should return different hashcode for different attributes', () { diff --git a/test/src/helpers/extensions/build_context_ext_test.dart b/test/src/helpers/build_context_ext_test.dart similarity index 97% rename from test/src/helpers/extensions/build_context_ext_test.dart rename to test/src/helpers/build_context_ext_test.dart index 63fef4057..129faca68 100644 --- a/test/src/helpers/extensions/build_context_ext_test.dart +++ b/test/src/helpers/build_context_ext_test.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; +import 'package:mix/src/helpers/build_context_ext.dart'; // extension BuildContextExt on BuildContext { // MixData? get mix => Mix.maybeOf(this); diff --git a/test/src/helpers/deep_collection_equality_test.dart b/test/src/helpers/deep_collection_equality_test.dart index b984ae96b..dc945eb37 100644 --- a/test/src/helpers/deep_collection_equality_test.dart +++ b/test/src/helpers/deep_collection_equality_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/core/equality/deep_collection_equality.dart'; +import 'package:mix/src/helpers/deep_collection_equality.dart'; void main() { group('DeepEqualityChecker hash code', () { @@ -32,6 +32,7 @@ void main() { expect(deepEquality.equals({1, 2}, {1, 2, 3}), isFalse); expect(deepEquality.equals({3, 2, 1}, {1, 2, 3}), isTrue); // Order should not matter in sets + // ignore: equal_elements_in_set expect(deepEquality.equals({1}, {1, 1, 1}), isTrue); // Duplicate elements in a set }); diff --git a/test/src/helpers/extensions/values_ext_test.dart b/test/src/helpers/extensions/values_ext_test.dart deleted file mode 100644 index 893810245..000000000 --- a/test/src/helpers/extensions/values_ext_test.dart +++ /dev/null @@ -1,338 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import '../../../helpers/testing_utils.dart'; - -void main() { - group('Extensions', () { - test('StrutStyle toAttribute', () { - const strutStyle = StrutStyle( - fontFamily: 'Roboto', - fontFamilyFallback: ['Arial', 'Helvetica'], - fontSize: 14.0, - fontWeight: FontWeight.bold, - fontStyle: FontStyle.italic, - height: 1.2, - leading: 5.0, - forceStrutHeight: true, - ); - - final attribute = strutStyle.toAttribute(); - - expect(attribute.fontFamily, 'Roboto'); - expect(attribute.fontFamilyFallback, ['Arial', 'Helvetica']); - expect(attribute.fontSize, 14.0); - expect(attribute.fontWeight, FontWeight.bold); - expect(attribute.fontStyle, FontStyle.italic); - expect(attribute.height, 1.2); - expect(attribute.leading, 5.0); - expect(attribute.forceStrutHeight, true); - }); - - test('StrutStyle merge', () { - const strutStyle1 = StrutStyle( - fontFamily: 'Roboto', - fontSize: 14.0, - ); - - const strutStyle2 = StrutStyle( - fontFamilyFallback: ['Arial', 'Helvetica'], - fontWeight: FontWeight.bold, - forceStrutHeight: true, - ); - - final mergedStyle = strutStyle1.merge(strutStyle2); - - expect(mergedStyle.fontFamily, 'Roboto'); - expect(mergedStyle.fontFamilyFallback, ['Arial', 'Helvetica']); - expect(mergedStyle.fontSize, 14.0); - expect(mergedStyle.fontWeight, FontWeight.bold); - expect(mergedStyle.forceStrutHeight, true); - }); - - test('TextAlign toAttribute', () { - const align = TextAlign.center; - final attribute = align.toAttribute(); - expect(attribute.value, TextAlign.center); - }); - - test('Gradient toAttribute', () { - const gradient = LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: [Colors.red, Colors.blue], - ); - - final attribute = gradient.toAttribute(); - - expect(attribute.value, gradient); - }); - - test('AlignmentGeometry toAttribute', () { - const alignment = Alignment.center; - final attribute = alignment.toAttribute(); - expect(attribute.resolve(EmptyMixData), Alignment.center); - }); - - test('ShapeDecoration toAttribute', () { - final shapeDecoration = ShapeDecoration( - shape: Border.all(), - gradient: const RadialGradient(colors: [Colors.red, Colors.blue]), - shadows: const [BoxShadow(blurRadius: 5.0)], - ); - - final attribute = shapeDecoration.toAttribute(); - - expect(attribute.shape, Border.all()); - expect(attribute.gradient?.value, - const RadialGradient(colors: [Colors.red, Colors.blue])); - expect(attribute.boxShadow?.map((e) => e.resolve(EmptyMixData)), - [const BoxShadow(blurRadius: 5.0)]); - }); - - test('Alignment toAttribute', () { - const alignment = Alignment(0.5, 0.5); - final attribute = alignment.toAttribute(); - expect(attribute.x, 0.5); - expect(attribute.y, 0.5); - }); - - test('AlignmentDirectional toAttribute', () { - const alignmentDirectional = AlignmentDirectional(0.5, 0.5); - final attribute = alignmentDirectional.toAttribute(); - expect(attribute.start, 0.5); - expect(attribute.y, 0.5); - }); - - test('BoxConstraints toAttribute', () { - const boxConstraints = BoxConstraints( - minWidth: 100.0, - maxWidth: 200.0, - minHeight: 150.0, - maxHeight: 250.0, - ); - final attribute = boxConstraints.toAttribute(); - expect(attribute.minWidth, 100.0); - expect(attribute.maxWidth, 200.0); - expect(attribute.minHeight, 150.0); - expect(attribute.maxHeight, 250.0); - }); - - test('MainAxisAlignment toAttribute', () { - const mainAxisAlignment = MainAxisAlignment.center; - final attribute = mainAxisAlignment.toAttribute(); - expect(attribute.value, MainAxisAlignment.center); - }); - - test('CrossAxisAlignment toAttribute', () { - const crossAxisAlignment = CrossAxisAlignment.center; - final attribute = crossAxisAlignment.toAttribute(); - expect(attribute.value, CrossAxisAlignment.center); - }); - - test('MainAxisSize toAttribute', () { - const mainAxisSize = MainAxisSize.max; - final attribute = mainAxisSize.toAttribute(); - expect(attribute.value, MainAxisSize.max); - }); - - test('TextOverflow toAttribute', () { - const textOverflow = TextOverflow.ellipsis; - final attribute = textOverflow.toAttribute(); - expect(attribute.value, TextOverflow.ellipsis); - }); - - test('VerticalDirection toAttribute', () { - const verticalDirection = VerticalDirection.up; - final attribute = verticalDirection.toAttribute(); - expect(attribute.value, VerticalDirection.up); - }); - - test('Clip toAttribute', () { - const clip = Clip.hardEdge; - final attribute = clip.toAttribute(); - expect(attribute.value, Clip.hardEdge); - }); - - test('TextWidthBasis toAttribute', () { - const textWidthBasis = TextWidthBasis.longestLine; - final attribute = textWidthBasis.toAttribute(); - expect(attribute.value, TextWidthBasis.longestLine); - }); - - test('TextHeightBehavior toAttribute', () { - const textHeightBehavior = - TextHeightBehavior(applyHeightToFirstAscent: true); - final attribute = textHeightBehavior.toAttribute(); - expect(attribute.value, - const TextHeightBehavior(applyHeightToFirstAscent: true)); - }); - - test('TextDirection toAttribute', () { - const textDirection = TextDirection.ltr; - final attribute = textDirection.toAttribute(); - expect(attribute.value, TextDirection.ltr); - }); - - test('ImageRepeat toAttribute', () { - const imageRepeat = ImageRepeat.repeat; - final attribute = imageRepeat.toAttribute(); - expect(attribute.value, ImageRepeat.repeat); - }); - - test('Axis toAttribute', () { - const axis = Axis.horizontal; - final attribute = axis.toAttribute(); - expect(attribute.value, Axis.horizontal); - }); - - test('BlendMode toAttribute', () { - const blendMode = BlendMode.srcOver; - final attribute = blendMode.toAttribute(); - expect(attribute.value, BlendMode.srcOver); - }); - - test('BoxFit toAttribute', () { - const boxFit = BoxFit.cover; - final attribute = boxFit.toAttribute(); - expect(attribute.value, BoxFit.cover); - }); - - test('BoxDecoration toAttribute', () { - final boxDecoration = BoxDecoration( - color: Colors.blue, - border: Border.all(), - borderRadius: BorderRadius.circular(10.0), - gradient: const LinearGradient(colors: [Colors.red, Colors.blue]), - boxShadow: const [BoxShadow(blurRadius: 5.0)], - ); - - final attribute = boxDecoration.toAttribute(); - - expect(attribute.color?.value, Colors.blue); - expect(attribute.border?.resolve(EmptyMixData), Border.all()); - expect(attribute.borderRadius?.resolve(EmptyMixData), - BorderRadius.circular(10.0)); - expect(attribute.gradient?.value, - const LinearGradient(colors: [Colors.red, Colors.blue])); - expect( - attribute.boxShadow?.map( - (e) => e.resolve(EmptyMixData), - ), - [const BoxShadow(blurRadius: 5.0)]); - }); - - test('BorderRadiusGeometry toAttribute', () { - const borderRadius = BorderRadius.all(Radius.circular(10.0)); - final attribute = borderRadius.toAttribute(); - expect(attribute.resolve(EmptyMixData), borderRadius); - }); - - test('BorderRadiusDirectional toAttribute', () { - const borderRadiusDirectional = - BorderRadiusDirectional.all(Radius.circular(10.0)); - final attribute = borderRadiusDirectional.toAttribute(); - expect(attribute.resolve(EmptyMixData), borderRadiusDirectional); - }); - - test('BorderRadius toAttribute', () { - const borderRadius = BorderRadius.all(Radius.circular(10.0)); - final attribute = borderRadius.toAttribute(); - expect(attribute.resolve(EmptyMixData), borderRadius); - }); - - test('TextBaseline toAttribute', () { - const textBaseline = TextBaseline.alphabetic; - final attribute = textBaseline.toAttribute(); - expect(attribute.value, TextBaseline.alphabetic); - }); - - test('Matrix4 toAttribute', () { - final matrix4 = Matrix4.identity(); - final attribute = matrix4.toAttribute(); - expect(attribute.value, Matrix4.identity()); - }); - - test('BorderSide toAttribute', () { - const borderSide = BorderSide( - color: Colors.blue, - width: 2.0, - style: BorderStyle.solid, - ); - final attribute = borderSide.toAttribute(); - expect(attribute.color?.resolve(EmptyMixData), Colors.blue); - expect(attribute.width, 2.0); - expect(attribute.style, BorderStyle.solid); - }); - - test('BoxBorder toAttribute', () { - final boxBorder = Border.all( - style: BorderStyle.solid, width: 1.0, color: const Color(0xff000000)); - final attribute = boxBorder.toAttribute(); - expect( - attribute.top?.resolve(EmptyMixData), - const BorderSide( - style: BorderStyle.solid, width: 1.0, color: Color(0xff000000))); - expect( - attribute.bottom?.resolve(EmptyMixData), - const BorderSide( - style: BorderStyle.solid, width: 1.0, color: Color(0xff000000))); - expect( - attribute.left?.resolve(EmptyMixData), - const BorderSide( - style: BorderStyle.solid, width: 1.0, color: Color(0xff000000))); - expect( - attribute.right?.resolve(EmptyMixData), - const BorderSide( - style: BorderStyle.solid, width: 1.0, color: Color(0xff000000))); - }); - - test('Shadow toAttribute', () { - const shadow = BoxShadow(blurRadius: 10.0, color: Colors.black); - final attribute = shadow.toAttribute(); - expect(attribute.blurRadius, 10.0); - expect(attribute.color?.resolve(EmptyMixData), Colors.black); - }); - - test('BoxShadow toAttribute', () { - const boxShadow = BoxShadow(blurRadius: 5.0, color: Colors.grey); - final attribute = boxShadow.toAttribute(); - expect(attribute.blurRadius, 5.0); - expect(attribute.color?.resolve(EmptyMixData), Colors.grey); - }); - - test('TextStyle toAttribute', () { - const textStyle = TextStyle( - color: Colors.black, - fontSize: 16.0, - fontWeight: FontWeight.bold, - ); - final attribute = textStyle.toAttribute(); - expect(attribute.color?.resolve(EmptyMixData), Colors.black); - expect(attribute.fontSize, 16.0); - expect(attribute.fontWeight, FontWeight.bold); - }); - - test('Border toAttribute', () { - const border = Border( - top: BorderSide(color: Colors.red), - bottom: BorderSide(color: Colors.blue)); - final attribute = border.toAttribute(); - expect(attribute.top?.resolve(EmptyMixData), - const BorderSide(color: Colors.red)); - expect(attribute.bottom?.resolve(EmptyMixData), - const BorderSide(color: Colors.blue)); - }); - - test('BorderDirectional toAttribute', () { - const borderDirectional = BorderDirectional( - top: BorderSide(color: Colors.red), - bottom: BorderSide(color: Colors.blue)); - final attribute = borderDirectional.toAttribute(); - expect(attribute.top?.resolve(EmptyMixData), - const BorderSide(color: Colors.red)); - expect(attribute.bottom?.resolve(EmptyMixData), - const BorderSide(color: Colors.blue)); - }); - }); -} diff --git a/test/src/helpers/extensions/iterable_ext_test.dart b/test/src/helpers/iterable_ext_test.dart similarity index 92% rename from test/src/helpers/extensions/iterable_ext_test.dart rename to test/src/helpers/iterable_ext_test.dart index 4bf26ba2a..345e1ad45 100644 --- a/test/src/helpers/extensions/iterable_ext_test.dart +++ b/test/src/helpers/iterable_ext_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/mix.dart'; +import 'package:mix/src/core/extensions/iterable_ext.dart'; void main() { test('Mock Iterable extension methods', () { diff --git a/test/src/helpers/lerp_helpers_test.dart b/test/src/helpers/lerp_helpers_test.dart new file mode 100644 index 000000000..5f9d63eb0 --- /dev/null +++ b/test/src/helpers/lerp_helpers_test.dart @@ -0,0 +1,20 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/src/helpers/lerp_helpers.dart'; + +void main() { + test('Linearly interpolates between two integers', () { + int a = 10; + int b = 20; + double t = 0.3; + int result = lerpInt(a, b, t); + expect(result, 13); + }); + + test('Snaps between two values based on a threshold', () { + String from = 'Hello'; + String to = 'World'; + double t = 0.8; + String result = lerpSnap(from, to, t); + expect(result, 'World'); + }); +} diff --git a/test/src/helpers/string_ext_test.dart b/test/src/helpers/string_ext_test.dart new file mode 100644 index 000000000..9bd78c746 --- /dev/null +++ b/test/src/helpers/string_ext_test.dart @@ -0,0 +1,150 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/src/helpers/string_ext.dart'; + +void main() { + group('StringExt', () { + test('words work properly', () { + String input = 'helloWorld'; + + expect(input.words, ['hello', 'World']); + + input = 'hello_world'; + expect(input.words, ['hello', 'world']); + + input = 'hello-world'; + expect(input.words, ['hello', 'world']); + + input = 'hello world'; + expect(input.words, ['hello', 'world']); + + input = 'HelloWorld'; + expect(input.words, ['Hello', 'World']); + + input = 'HELLO_WORLD'; + expect(input.words, ['HELLO', 'WORLD']); + + input = 'HELLO-WORLD'; + expect(input.words, ['HELLO', 'WORLD']); + + input = 'HELLO WORLD'; + expect(input.words, ['HELLO', 'WORLD']); + }); + + test('isUpperCase work properly', () { + String input = 'HELLO'; + expect(input.isUpperCase, true); + + input = 'hello'; + expect(input.isUpperCase, false); + + input = 'Hello'; + expect(input.isUpperCase, false); + }); + + test('isLowerCase work properly', () { + String input = 'hello'; + expect(input.isLowerCase, true); + + input = 'HELLO'; + expect(input.isLowerCase, false); + + input = 'Hello'; + expect(input.isLowerCase, false); + }); + + test('camelCase work properly', () { + String input = 'hello_world'; + expect(input.camelCase, 'helloWorld'); + + input = 'hello-world'; + expect(input.camelCase, 'helloWorld'); + + input = 'Hello World'; + expect(input.camelCase, 'helloWorld'); + }); + + test('pascalCase work properly', () { + String input = 'hello_world'; + expect(input.pascalCase, 'HelloWorld'); + + input = 'hello-world'; + expect(input.pascalCase, 'HelloWorld'); + + input = 'Hello World'; + expect(input.pascalCase, 'HelloWorld'); + }); + + test('capitalize work properly', () { + String input = 'hello'; + expect(input.capitalize, 'Hello'); + + input = 'HELLO'; + expect(input.capitalize, 'Hello'); + + input = 'hello world'; + expect(input.capitalize, 'Hello world'); + }); + + test('constantCase work properly', () { + String input = 'hello_world'; + expect(input.constantCase, 'HELLO_WORLD'); + + input = 'hello-world'; + expect(input.constantCase, 'HELLO_WORLD'); + + input = 'Hello World'; + expect(input.constantCase, 'HELLO_WORLD'); + }); + + test('snakeCase work properly', () { + String input = 'HelloWorld'; + expect(input.snakeCase, 'hello_world'); + + input = 'hello-world'; + expect(input.snakeCase, 'hello_world'); + + input = 'Hello World'; + expect(input.snakeCase, 'hello_world'); + }); + + test('paramCase work properly', () { + String input = 'HelloWorld'; + expect(input.paramCase, 'hello-world'); + + input = 'hello_world'; + expect(input.paramCase, 'hello-world'); + + input = 'Hello World'; + expect(input.paramCase, 'hello-world'); + }); + + test('titleCase work properly', () { + String input = 'HELLO_WORLD'; + expect(input.titleCase, 'Hello World'); + + input = 'hello-world'; + expect(input.titleCase, 'Hello World'); + + input = 'hello world'; + expect(input.titleCase, 'Hello World'); + }); + }); + + group('ListStringExt', () { + test('lowercase work properly', () { + var input = ['HELLO', 'WORLD']; + expect(input.lowercase, ['hello', 'world']); + + input = ['Hello', 'World']; + expect(input.lowercase, ['hello', 'world']); + }); + + test('uppercase work properly', () { + var input = ['hello', 'world']; + expect(input.uppercase, ['HELLO', 'WORLD']); + + input = ['Hello', 'World']; + expect(input.uppercase, ['HELLO', 'WORLD']); + }); + }); +} diff --git a/test/src/specs/container_spec_test.dart b/test/src/recipes/container/container_spec_test.dart similarity index 68% rename from test/src/specs/container_spec_test.dart rename to test/src/recipes/container/container_spec_test.dart index 3f57c260d..7abe90058 100644 --- a/test/src/specs/container_spec_test.dart +++ b/test/src/recipes/container/container_spec_test.dart @@ -1,8 +1,13 @@ +import 'dart:ui'; + import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; +import 'package:mix/src/attributes/constraints/constraints_dto.dart'; +import 'package:mix/src/attributes/decoration/decoration_dto.dart'; +import 'package:mix/src/attributes/spacing/spacing_dto.dart'; -import '../../helpers/testing_utils.dart'; +import '../../../helpers/testing_utils.dart'; void main() { group('ContainerSpec', () { @@ -10,32 +15,39 @@ void main() { final mix = MixData.create( MockBuildContext(), StyleMix( - const AlignmentAttribute(x: 0.0, y: 0.0), - const PaddingAttribute(top: 8, bottom: 16), - const MarginAttribute(top: 10.0, bottom: 12.0), - const BoxConstraintsAttribute(maxWidth: 300.0, minHeight: 200.0), - const BoxDecorationAttribute(color: ColorAttribute(Colors.blue)), + const AlignmentGeometryAttribute(Alignment.center), + const PaddingAttribute(SpacingDto(top: 8, bottom: 16)), + const MarginAttribute(SpacingDto(top: 10.0, bottom: 12.0)), + const BoxConstraintsAttribute( + BoxConstraintsDto(maxWidth: 300.0, minHeight: 200.0), + ), + const DecorationAttribute( + BoxDecorationDto(color: ColorDto(Colors.blue)), + ), TransformAttribute(Matrix4.translationValues(10.0, 10.0, 0.0)), - const ClipAttribute(Clip.antiAlias), + const ClipBehaviorAttribute(Clip.antiAlias), ), ); - final spec = ContainerSpec.resolve(mix); + final mixture = ContainerSpecAttribute.of(mix).resolve(mix); - expect(spec.alignment, Alignment.center); - expect(spec.padding, const EdgeInsets.only(bottom: 16.0, top: 8.0)); - expect(spec.margin, const EdgeInsets.only(top: 10.0, bottom: 12.0)); - expect(spec.constraints, + expect(mixture.alignment, Alignment.center); + expect(mixture.padding, const EdgeInsets.only(bottom: 16.0, top: 8.0)); + expect(mixture.margin, const EdgeInsets.only(top: 10.0, bottom: 12.0)); + expect(mixture.constraints, const BoxConstraints(maxWidth: 300.0, minHeight: 200.0)); - expect(spec.decoration, const BoxDecoration(color: Colors.blue)); + expect(mixture.decoration, const BoxDecoration(color: Colors.blue)); - expect(spec.transform, Matrix4.translationValues(10.0, 10.0, 0.0)); - expect(spec.clipBehavior, Clip.antiAlias); + expect(mixture.transform, Matrix4.translationValues(10.0, 10.0, 0.0)); + expect(mixture.clipBehavior, Clip.antiAlias); }); test('copyWith', () { final spec = ContainerSpec( alignment: Alignment.center, + color: null, + width: 300, + height: 200, padding: const EdgeInsets.all(16.0), margin: const EdgeInsets.only(top: 8.0, bottom: 8.0), constraints: const BoxConstraints(maxWidth: 300.0, minHeight: 200.0), @@ -60,6 +72,9 @@ void main() { test('lerp', () { final spec1 = ContainerSpec( alignment: Alignment.topLeft, + color: null, + width: 300, + height: 200, padding: const EdgeInsets.all(8.0), margin: const EdgeInsets.only(top: 4.0), constraints: const BoxConstraints(maxWidth: 200.0), @@ -70,6 +85,9 @@ void main() { final spec2 = ContainerSpec( alignment: Alignment.bottomRight, + color: null, + width: 400, + height: 300, padding: const EdgeInsets.all(16.0), margin: const EdgeInsets.only(top: 8.0), constraints: const BoxConstraints(maxWidth: 400.0), @@ -102,6 +120,9 @@ void main() { end: const BoxDecoration(color: Colors.blue)) .lerp(t)); + expect(lerpedSpec.width, lerpDouble(300, 400, t)); + expect(lerpedSpec.height, lerpDouble(200, 300, t)); + expect( lerpedSpec.transform, Matrix4Tween(begin: Matrix4.identity(), end: Matrix4.rotationZ(0.5)) diff --git a/test/src/recipes/container/container_widget_test.dart b/test/src/recipes/container/container_widget_test.dart new file mode 100644 index 000000000..098b9d634 --- /dev/null +++ b/test/src/recipes/container/container_widget_test.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + testWidgets('StyledContainer', (WidgetTester tester) async { + final paddingAttr = padding(10); + final marginAttr = margin(15); + final alignmentAttr = alignment.center(); + final clipAttr = clipBehavior.hardEdge(); + + final boxDecorationAttr = box.decoration( + border: Border.all(color: Colors.red, width: 1, style: BorderStyle.solid), + borderRadius: BorderRadius.circular(10), + color: Colors.red, + ); + + await tester.pumpStyledWidget( + StyledContainer( + style: StyleMix( + paddingAttr, + marginAttr, + alignmentAttr, + clipAttr, + boxDecorationAttr, + ), + ), + ); + + final containerFinder = find.byType(Container); + final containerWidget = tester.widget(containerFinder); + + expect(containerWidget.padding, const EdgeInsets.all(10)); + expect(containerWidget.margin, const EdgeInsets.all(15)); + expect(containerWidget.alignment, Alignment.center); + expect(containerWidget.clipBehavior, Clip.hardEdge); + expect( + containerWidget.decoration, + BoxDecoration( + border: + Border.all(color: Colors.red, width: 1, style: BorderStyle.solid), + borderRadius: BorderRadius.circular(10), + color: Colors.red, + )); + }); +} diff --git a/test/src/specs/flex_spec_test.dart b/test/src/recipes/flex/flex_spec_test.dart similarity index 83% rename from test/src/specs/flex_spec_test.dart rename to test/src/recipes/flex/flex_spec_test.dart index 1e593490b..01412268e 100644 --- a/test/src/specs/flex_spec_test.dart +++ b/test/src/recipes/flex/flex_spec_test.dart @@ -1,8 +1,10 @@ +import 'dart:ui'; + import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; -import '../../helpers/testing_utils.dart'; +import '../../../helpers/testing_utils.dart'; void main() { group('FlexSpec', () { @@ -10,14 +12,17 @@ void main() { final mix = MixData.create( MockBuildContext(), StyleMix( - const CrossAxisAlignmentAttribute(CrossAxisAlignment.center), - const MainAxisAlignmentAttribute(MainAxisAlignment.center), - const MainAxisSizeAttribute(MainAxisSize.min), - const VerticalDirectionAttribute(VerticalDirection.down), - const AxisAttribute(Axis.horizontal), - const TextDirectionAttribute(TextDirection.ltr), - const TextBaselineAttribute(TextBaseline.alphabetic), - const ClipAttribute(Clip.antiAlias), + const FlexMixAttribute( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + verticalDirection: VerticalDirection.down, + direction: Axis.horizontal, + textDirection: TextDirection.ltr, + textBaseline: TextBaseline.alphabetic, + clipBehavior: Clip.antiAlias, + gap: 10, + ), ), ); @@ -31,6 +36,7 @@ void main() { expect(spec.textDirection, TextDirection.ltr); expect(spec.textBaseline, TextBaseline.alphabetic); expect(spec.clipBehavior, Clip.antiAlias); + expect(spec.gap, 10); }); test('copyWith', () { @@ -43,6 +49,7 @@ void main() { textDirection: TextDirection.ltr, textBaseline: TextBaseline.alphabetic, clipBehavior: Clip.antiAlias, + gap: 10, ); final copiedSpec = spec.copyWith( @@ -54,6 +61,7 @@ void main() { textDirection: TextDirection.rtl, textBaseline: TextBaseline.ideographic, clipBehavior: Clip.none, + gap: 20, ); expect(copiedSpec.crossAxisAlignment, CrossAxisAlignment.start); @@ -64,6 +72,7 @@ void main() { expect(copiedSpec.textDirection, TextDirection.rtl); expect(copiedSpec.textBaseline, TextBaseline.ideographic); expect(copiedSpec.clipBehavior, Clip.none); + expect(copiedSpec.gap, 20); expect(copiedSpec, isNot(spec)); }); @@ -78,6 +87,7 @@ void main() { textDirection: TextDirection.ltr, textBaseline: TextBaseline.alphabetic, clipBehavior: Clip.none, + gap: 10, ); const spec2 = FlexSpec( @@ -89,6 +99,7 @@ void main() { textDirection: TextDirection.rtl, textBaseline: TextBaseline.ideographic, clipBehavior: Clip.antiAlias, + gap: 20, ); const t = 0.5; @@ -111,6 +122,7 @@ void main() { expect(lerpedSpec.textDirection, TextDirection.rtl); expect(lerpedSpec.textBaseline, TextBaseline.ideographic); expect(lerpedSpec.clipBehavior, Clip.antiAlias); + expect(lerpedSpec.gap, lerpDouble(spec1.gap, spec2.gap, t)); expect(lerpedSpec, isNot(spec1)); }); diff --git a/test/src/recipes/icon/icon_spec_test.dart b/test/src/recipes/icon/icon_spec_test.dart new file mode 100644 index 000000000..dbeb40bab --- /dev/null +++ b/test/src/recipes/icon/icon_spec_test.dart @@ -0,0 +1,55 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('IconMix', () { + test('resolve', () { + final mix = MixData.create( + MockBuildContext(), + StyleMix( + IconMixAttribute(color: Colors.red.toDto(), size: 20.0), + ), + ); + + final spec = IconSpec.resolve(mix); + + expect(spec.color, Colors.red); + expect(spec.size, 20.0); + }); + + test('copyWith', () { + const spec = IconSpec( + color: Colors.red, + size: 20.0, + ); + + final copiedSpec = spec.copyWith(color: Colors.blue, size: 30.0); + + expect(copiedSpec.color, Colors.blue); + expect(copiedSpec.size, 30.0); + }); + + test('lerp', () { + const spec1 = IconSpec( + color: Colors.red, + size: 20.0, + ); + + const spec2 = IconSpec( + color: Colors.blue, + size: 30.0, + ); + + const t = 0.5; + final lerpedSpec = spec1.lerp(spec2, t); + + expect(lerpedSpec.color, Color.lerp(Colors.red, Colors.blue, t)); + expect(lerpedSpec.size, lerpDouble(20.0, 30.0, t)); + }); + }); +} diff --git a/test/src/specs/icon_spec_test.dart b/test/src/recipes/icon_spec_test.dart similarity index 70% rename from test/src/specs/icon_spec_test.dart rename to test/src/recipes/icon_spec_test.dart index 0c1952762..899353419 100644 --- a/test/src/specs/icon_spec_test.dart +++ b/test/src/recipes/icon_spec_test.dart @@ -7,14 +7,12 @@ import 'package:mix/mix.dart'; import '../../helpers/testing_utils.dart'; void main() { - group('IconSpec', () { + group('IconMix', () { test('resolve', () { final mix = MixData.create( MockBuildContext(), StyleMix( - const IconColorAttribute(Colors.red), - const IconSizeAttribute(20.0), - const TextDirectionAttribute(TextDirection.ltr), + IconMixAttribute(color: Colors.red.toDto(), size: 20.0), ), ); @@ -22,34 +20,29 @@ void main() { expect(spec.color, Colors.red); expect(spec.size, 20.0); - expect(spec.textDirection, TextDirection.ltr); }); test('copyWith', () { const spec = IconSpec( color: Colors.red, size: 20.0, - textDirection: TextDirection.ltr, ); final copiedSpec = spec.copyWith(color: Colors.blue, size: 30.0); expect(copiedSpec.color, Colors.blue); expect(copiedSpec.size, 30.0); - expect(copiedSpec.textDirection, TextDirection.ltr); }); test('lerp', () { const spec1 = IconSpec( color: Colors.red, size: 20.0, - textDirection: TextDirection.ltr, ); const spec2 = IconSpec( color: Colors.blue, size: 30.0, - textDirection: TextDirection.rtl, ); const t = 0.5; @@ -57,7 +50,6 @@ void main() { expect(lerpedSpec.color, Color.lerp(Colors.red, Colors.blue, t)); expect(lerpedSpec.size, lerpDouble(20.0, 30.0, t)); - expect(lerpedSpec.textDirection, TextDirection.rtl); }); }); } diff --git a/test/src/specs/stack_spec_test.dart b/test/src/recipes/stack/stack_spec_test.dart similarity index 76% rename from test/src/specs/stack_spec_test.dart rename to test/src/recipes/stack/stack_spec_test.dart index e67b316db..8b634b59a 100644 --- a/test/src/specs/stack_spec_test.dart +++ b/test/src/recipes/stack/stack_spec_test.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; -import '../../helpers/testing_utils.dart'; +import '../../../helpers/testing_utils.dart'; void main() { group('StackSpec', () { @@ -10,19 +10,21 @@ void main() { final mix = MixData.create( MockBuildContext(), StyleMix( - const AlignmentAttribute(x: 0.0, y: 0.0), - const StackFitAttribute(StackFit.expand), - const TextDirectionAttribute(TextDirection.ltr), - const ClipAttribute(Clip.antiAlias), + const StackMixAttribute( + fit: StackFit.expand, + clipBehavior: Clip.antiAlias, + alignment: Alignment.center, + textDirection: TextDirection.ltr, + ), ), ); - final spec = StackSpec.resolve(mix); + final mixture = StackMixAttribute.of(mix).resolve(mix); - expect(spec.alignment, Alignment.center); - expect(spec.fit, StackFit.expand); - expect(spec.textDirection, TextDirection.ltr); - expect(spec.clipBehavior, Clip.antiAlias); + expect(mixture.alignment, Alignment.center); + expect(mixture.fit, StackFit.expand); + expect(mixture.textDirection, TextDirection.ltr); + expect(mixture.clipBehavior, Clip.antiAlias); }); test('copyWith', () { diff --git a/test/src/recipes/stack/stack_widget_test.dart b/test/src/recipes/stack/stack_widget_test.dart new file mode 100644 index 000000000..eb25e46e9 --- /dev/null +++ b/test/src/recipes/stack/stack_widget_test.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + testWidgets('Stack', (tester) async { + final style = StyleMix( + stack.fit.expand(), + stack.alignment.topCenter(), + stack.clipBehavior.antiAlias(), + stack.textDirection.ltr(), + ); + await tester.pumpMaterialApp( + StyledStack( + style: style, + children: [ + Container( + height: 100, + width: 100, + color: const Color(0xFF000000), + ), + Container( + height: 50, + width: 50, + color: const Color(0xFF0000FF), + ), + ], + ), + ); + + final stackWidget = tester.widget(find.byType(Stack)); + + expect(find.byType(Stack), findsOneWidget); + expect(find.byType(Container), findsNWidgets(2)); + expect(stackWidget.alignment, Alignment.topCenter); + expect(stackWidget.fit, StackFit.expand); + expect(stackWidget.clipBehavior, Clip.antiAlias); + expect(stackWidget.textDirection, TextDirection.ltr); + }); + + testWidgets('ZBox', (tester) async { + final style = StyleMix( + stack.fit.expand(), + stack.alignment.topCenter(), + stack.textDirection.ltr(), + stack.clipBehavior.antiAlias(), + backgroundColor(Colors.red), + ); + + await tester.pumpMaterialApp( + ZBox( + style: style, + children: const [], + ), + ); + + final stackWidget = tester.widget(find.byType(Stack)); + final container = tester.widget(find.byType(Container)); + + expect(find.byType(Stack), findsOneWidget); + + expect(find.byType(Container), findsOneWidget); + + expect(container.color, Colors.red); + + expect(stackWidget.alignment, Alignment.topCenter); + expect(stackWidget.fit, StackFit.expand); + expect(stackWidget.clipBehavior, Clip.antiAlias); + expect(stackWidget.textDirection, TextDirection.ltr); + }); +} diff --git a/test/src/specs/text_spec_test.dart b/test/src/recipes/text/text_spec_test.dart similarity index 67% rename from test/src/specs/text_spec_test.dart rename to test/src/recipes/text/text_spec_test.dart index e62335b9d..4ffabefaa 100644 --- a/test/src/specs/text_spec_test.dart +++ b/test/src/recipes/text/text_spec_test.dart @@ -1,48 +1,57 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; +import 'package:mix/src/attributes/strut_style/strut_style_dto.dart'; +import 'package:mix/src/attributes/text_style/text_style_dto.dart'; -import '../../helpers/testing_utils.dart'; +import '../../../helpers/testing_utils.dart'; void main() { group('TextSpec', () { + const uppercaseDirective = TextDirective(TextModifiers.uppercase); + const lowercaseDirective = TextDirective(TextModifiers.lowercase); test('resolve', () { final mix = MixData.create( MockBuildContext(), StyleMix( - const TextOverflowAttribute(TextOverflow.ellipsis), - const StrutStyleAttribute(fontSize: 20.0), - const TextAlignAttribute(TextAlign.center), - const TextScaleFactorAttribute(1.0), - const MaxLinesAttribute(2), - const TextStyleAttribute(color: ColorAttribute(Colors.red)), - const TextWidthBasisAttribute(TextWidthBasis.longestLine), - const TextHeightBehaviorAttribute( - TextHeightBehavior(applyHeightToFirstAscent: true)), - const TextDirectionAttribute(TextDirection.ltr), - const SoftWrapAttribute(true), - const TextDirectiveAttribute([UppercaseDirective()]), + const StrutStyleAttribute(StrutStyleDto(fontSize: 20.0)), + TextStyleAttribute( + TextStyleDto.only(color: const ColorDto(Colors.red))), + const TextMixAttribute( + overflow: TextOverflow.ellipsis, + textDirection: TextDirection.ltr, + textAlign: TextAlign.center, + textScaleFactor: 1.0, + maxLines: 2, + textWidthBasis: TextWidthBasis.longestLine, + textHeightBehavior: TextHeightBehavior( + applyHeightToFirstAscent: true, + applyHeightToLastDescent: true, + ), + softWrap: true, + directives: [uppercaseDirective], + ), ), ); - final spec = TextSpec.resolve(mix); + final mixture = TextMixAttribute.of(mix).resolve(mix); - expect(spec.overflow, TextOverflow.ellipsis); - expect(spec.strutStyle, const StrutStyle(fontSize: 20.0)); - expect(spec.textAlign, TextAlign.center); - expect(spec.textScaleFactor, 1.0); - expect(spec.maxLines, 2); - expect(spec.style, const TextStyle(color: Colors.red)); - expect(spec.textWidthBasis, TextWidthBasis.longestLine); + expect(mixture.overflow, TextOverflow.ellipsis); + expect(mixture.strutStyle, const StrutStyle(fontSize: 20.0)); + expect(mixture.textAlign, TextAlign.center); + expect(mixture.textScaleFactor, 1.0); + expect(mixture.maxLines, 2); + expect(mixture.style, const TextStyle(color: Colors.red)); + expect(mixture.textWidthBasis, TextWidthBasis.longestLine); expect( - spec.textHeightBehavior, + mixture.textHeightBehavior, const TextHeightBehavior( applyHeightToFirstAscent: true, applyHeightToLastDescent: true)); - expect(spec.textDirection, TextDirection.ltr); - expect(spec.softWrap, true); - expect(spec.directives, [const UppercaseDirective()]); + expect(mixture.textDirection, TextDirection.ltr); + expect(mixture.softWrap, true); + expect(mixture.directives, [uppercaseDirective]); - expect(spec.applyTextDirectives('hello'), 'HELLO'); + expect(mixture.applyTextDirectives('hello'), 'HELLO'); }); test('copyWith', () { @@ -58,7 +67,7 @@ void main() { applyHeightToFirstAscent: true, applyHeightToLastDescent: true), textDirection: TextDirection.ltr, softWrap: true, - directives: [UppercaseDirective()], + directives: [uppercaseDirective], ); final copiedSpec = spec.copyWith( @@ -73,7 +82,7 @@ void main() { applyHeightToFirstAscent: false, applyHeightToLastDescent: false), textDirection: TextDirection.rtl, softWrap: false, - directives: [const LowercaseDirective()], + directives: [lowercaseDirective], ); expect(copiedSpec.overflow, TextOverflow.fade); @@ -91,7 +100,7 @@ void main() { expect(copiedSpec.textDirection, TextDirection.rtl); expect(copiedSpec.softWrap, false); - expect(copiedSpec.directives, [const LowercaseDirective()]); + expect(copiedSpec.directives, [lowercaseDirective]); }); test('lerp', () { @@ -109,7 +118,7 @@ void main() { ), textDirection: TextDirection.ltr, softWrap: true, - directives: [UppercaseDirective()], + directives: [uppercaseDirective], ); const spec2 = TextSpec( @@ -126,7 +135,7 @@ void main() { ), textDirection: TextDirection.rtl, softWrap: false, - directives: [LowercaseDirective()], + directives: [lowercaseDirective], ); const t = 0.5; @@ -152,7 +161,7 @@ void main() { )); expect(lerpedSpec.textDirection, TextDirection.rtl); expect(lerpedSpec.softWrap, false); - expect(lerpedSpec.directives, [const LowercaseDirective()]); + expect(lerpedSpec.directives, [lowercaseDirective]); expect(lerpedSpec, isNot(spec1)); }); diff --git a/test/src/theme/mix_theme_test.dart b/test/src/theme/mix_theme_test.dart new file mode 100644 index 000000000..826194349 --- /dev/null +++ b/test/src/theme/mix_theme_test.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +import '../../helpers/testing_utils.dart'; + +void main() { + const primaryColor = ColorToken('primary', Colors.red); + group('MixTheme', () { + testWidgets('MixTheme.of', (tester) async { + final theme = MixThemeData( + colors: StyledTokens({ + primaryColor: (_) => Colors.blue, + }), + ); + + await tester.pumpWithMixTheme(Container(), theme: theme); + + final context = tester.element(find.byType(Container)); + + expect(MixTheme.of(context), theme); + expect(MixTheme.maybeOf(context), theme); + }); + + // maybeOf + testWidgets('MixTheme.maybeOf', (tester) async { + await tester.pumpMaterialApp(Container()); + + final context = tester.element(find.byType(Container)); + + expect(MixTheme.maybeOf(context), null); + expect(MixTheme.of(context), MixThemeData()); + }); + }); +} diff --git a/test/src/theme/tokens/breakpoints_test.dart b/test/src/theme/tokens/breakpoints_test.dart index a39f81f5a..c96638131 100644 --- a/test/src/theme/tokens/breakpoints_test.dart +++ b/test/src/theme/tokens/breakpoints_test.dart @@ -8,10 +8,10 @@ void main() { test('MixBreakpointsTokens', () { final breakpoints = MixThemeData().breakpoints; final context = MockBuildContext(); - final large = breakpoints[BreakpointToken.large]!(context); - final medium = breakpoints[BreakpointToken.medium]!(context); - final small = breakpoints[BreakpointToken.small]!(context); - final xsmall = breakpoints[BreakpointToken.xsmall]!(context); + final large = breakpoints(BreakpointToken.large, context); + final medium = breakpoints(BreakpointToken.medium, context); + final small = breakpoints(BreakpointToken.small, context); + final xsmall = breakpoints(BreakpointToken.xsmall, context); expect( large, @@ -34,7 +34,7 @@ void main() { test('MixBreakpointsTokens large matches correctly', () { final breakpoints = MixThemeData().breakpoints; final context = MockBuildContext(); - final large = breakpoints[BreakpointToken.large]!(context); + final large = breakpoints(BreakpointToken.large, context); expect( large.matches(const Size(1440, 1024)), @@ -47,44 +47,51 @@ void main() { ); }); - test('Test orientation for Breakpoint tokens', () { - const portraitBreakpoint = BreakpointToken('--custom-breakpoint'); - const landscapeBreakpoint = BreakpointToken('--another-breakpoint'); - final breakpoints = MixThemeData( - breakpoints: { - portraitBreakpoint: (context) => const BreakpointConstraint( - orientation: BreakpointOrientation.portrait, - ), - landscapeBreakpoint: (context) => const BreakpointConstraint( - orientation: BreakpointOrientation.landscape, - ) - }, - ).breakpoints; + test('MixBreakpointsTokens medium matches correctly', () { + final breakpoints = MixThemeData().breakpoints; final context = MockBuildContext(); - final portrait = breakpoints[portraitBreakpoint]!(context); - final landscape = breakpoints[landscapeBreakpoint]!(context); - - const portraitSize = Size(2000, 3000); - const landscapeSize = Size(3000, 2000); + final medium = breakpoints(BreakpointToken.medium, context); expect( - portrait.matches(portraitSize), + medium.matches(const Size(1024, 1024)), true, ); expect( - landscape.matches(portraitSize), + medium.matches(const Size(1023, 1024)), false, ); + }); + + test('MixBreakpointsTokens small matches correctly', () { + final breakpoints = MixThemeData().breakpoints; + final context = MockBuildContext(); + final small = breakpoints(BreakpointToken.small, context); + + expect( + small.matches(const Size(600, 1024)), + true, + ); expect( - portrait.matches(landscapeSize), + small.matches(const Size(599, 1024)), false, ); + }); + + test('MixBreakpointsTokens xsmall matches correctly', () { + final breakpoints = MixThemeData().breakpoints; + final context = MockBuildContext(); + final xsmall = breakpoints(BreakpointToken.xsmall, context); expect( - landscape.matches(landscapeSize), + xsmall.matches(const Size(0, 1024)), true, ); + + expect( + xsmall.matches(const Size(-1, 1024)), + false, + ); }); } diff --git a/test/src/theme/tokens/color_token_test.dart b/test/src/theme/tokens/color_token_test.dart index e8a8f39b9..2326ca2fe 100644 --- a/test/src/theme/tokens/color_token_test.dart +++ b/test/src/theme/tokens/color_token_test.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; -import 'package:mix/src/theme/tokens/color_token.dart'; import '../../../helpers/testing_utils.dart'; @@ -9,40 +8,44 @@ void main() { group('ColorToken Tests', () { // Constructor Test test('Constructor assigns name correctly', () { - const colorToken = ColorToken('testName'); + const colorToken = ColorToken('testName', Colors.black); expect(colorToken.name, 'testName'); + expect(colorToken.value, Colors.black); }); // Equality Operator Test test('Equality operator works correctly', () { - const colorToken1 = ColorToken('testName'); - const colorToken2 = ColorToken('testName'); - const colorToken3 = ColorToken('differentName'); + const colorToken1 = ColorToken('testName', Colors.black); + const colorToken2 = ColorToken('testName', Colors.black); + const colorToken3 = ColorToken('testName', Colors.black12); + const colorToken4 = ColorToken('differentName', Colors.black); expect(colorToken1 == colorToken2, isTrue); expect(colorToken1 == colorToken3, isFalse); + expect(colorToken1 == colorToken4, isFalse); expect(colorToken1 == Object(), isFalse); }); // HashCode Test test('hashCode is consistent with name', () { - const colorToken1 = ColorToken('testName'); - const colorToken2 = ColorToken('testName'); - const colorToken3 = ColorToken('differentName'); + const colorToken1 = ColorToken('testName', Colors.black); + const colorToken2 = ColorToken('testName', Colors.black); + const colorToken3 = ColorToken('differentName', Colors.black); expect(colorToken1.hashCode, colorToken2.hashCode); expect(colorToken1.hashCode, isNot(colorToken3.hashCode)); }); testWidgets('Test it resolves correctly', (tester) async { - const redcolorToken = ColorToken('red'); - const greencolorToken = ColorToken('green'); - const bluecolorToken = ColorToken('blue'); - final theme = MixThemeData(colors: { - redcolorToken: (_) => Colors.red, - greencolorToken: (_) => Colors.green, - bluecolorToken: (_) => Colors.blue, - }); + const redcolorToken = ColorToken('red', Colors.red); + const greencolorToken = ColorToken('green', Colors.green); + const bluecolorToken = ColorToken('blue', Colors.blue); + final theme = MixThemeData.tokenMap( + colors: { + redcolorToken: (_) => Colors.redAccent, + bluecolorToken: (_) => Colors.blueAccent, + }, + ); await tester.pumpWidget(createWithMixTheme(theme)); @@ -50,24 +53,24 @@ void main() { final mixData = MixData.create(context, StyleMix.empty); - expect(mixData.resolver.colorToken(redcolorToken), Colors.red); - expect(mixData.resolver.colorToken(greencolorToken), Colors.green); - expect(mixData.resolver.colorToken(bluecolorToken), Colors.blue); + expect(mixData.tokens.colorToken(redcolorToken), Colors.redAccent); + expect(mixData.tokens.colorToken(greencolorToken), Colors.green); + expect(mixData.tokens.colorToken(bluecolorToken), Colors.blueAccent); }); }); - group('ColorResolvableToken', () { + group('ColorToken.resolvable', () { test('Constructor assigns name correctly', () { - final colorToken = ColorTokenResolver('testName', (_) => Colors.red); + final colorToken = ColorToken.resolvable('testName', (_) => Colors.red); expect(colorToken.name, 'testName'); }); // Equality Operator Test test('Equality operator works correctly', () { - final colorToken1 = ColorTokenResolver('testName', (_) => Colors.red); - final colorToken2 = ColorTokenResolver('testName', (_) => Colors.red); + final colorToken1 = ColorToken.resolvable('testName', (_) => Colors.red); + final colorToken2 = ColorToken.resolvable('testName', (_) => Colors.red); final colorToken3 = - ColorTokenResolver('differentName', (_) => Colors.red); + ColorToken.resolvable('differentName', (_) => Colors.red); expect(colorToken1 == colorToken2, isTrue); expect(colorToken1 == colorToken3, isFalse); @@ -76,19 +79,20 @@ void main() { // HashCode Test test('hashCode is consistent with name', () { - final colorToken1 = ColorTokenResolver('testName', (_) => Colors.red); - final colorToken2 = ColorTokenResolver('testName', (_) => Colors.red); + final colorToken1 = ColorToken.resolvable('testName', (_) => Colors.red); + final colorToken2 = ColorToken.resolvable('testName', (_) => Colors.red); final colorToken3 = - ColorTokenResolver('differentName', (_) => Colors.red); + ColorToken.resolvable('differentName', (_) => Colors.red); expect(colorToken1.hashCode, colorToken2.hashCode); expect(colorToken1.hashCode, isNot(colorToken3.hashCode)); }); testWidgets('Test it resolves correctly', (tester) async { - final redcolorToken = ColorTokenResolver('red', (_) => Colors.red); - final greencolorToken = ColorTokenResolver('green', (_) => Colors.green); - final bluecolorToken = ColorTokenResolver('blue', (_) => Colors.blue); + final redcolorToken = ColorToken.resolvable('red', (_) => Colors.red); + final greencolorToken = + ColorToken.resolvable('green', (_) => Colors.green); + final bluecolorToken = ColorToken.resolvable('blue', (_) => Colors.blue); await tester.pumpMaterialApp(Container()); @@ -96,9 +100,9 @@ void main() { final mixData = MixData.create(context, StyleMix.empty); - expect(mixData.resolver.colorToken(redcolorToken), Colors.red); - expect(mixData.resolver.colorToken(greencolorToken), Colors.green); - expect(mixData.resolver.colorToken(bluecolorToken), Colors.blue); + expect(mixData.tokens.colorToken(redcolorToken), Colors.red); + expect(mixData.tokens.colorToken(greencolorToken), Colors.green); + expect(mixData.tokens.colorToken(bluecolorToken), Colors.blue); }); }); } diff --git a/test/src/theme/tokens/material_tokens_test.dart b/test/src/theme/tokens/material_tokens_test.dart index e3da5e5f6..ada26d628 100644 --- a/test/src/theme/tokens/material_tokens_test.dart +++ b/test/src/theme/tokens/material_tokens_test.dart @@ -1,209 +1,9 @@ -// ignore_for_file: deprecated_member_use, avoid-non-null-assertion +// ignore_for_file: deprecated_member_use import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; -// import '../../helpers/extensions/build_context_ext.dart'; -// import 'mix_token.dart'; -// import 'refs.dart'; - -// class MaterialColorToken extends MixToken { -// const MaterialColorToken(super.name); -// } - -// class _MaterialDesignColors { -// final primary = ResolvableColorToken( -// '--md-color-primary', -// (context) => context.colorScheme.primary, -// ); - -// final secondary = ResolvableColorToken( -// '--md-color-secondary', -// (context) => context.colorScheme.secondary, -// ); - -// final tertiary = ResolvableColorToken( -// '--md-color-tertiary', -// (context) => context.colorScheme.tertiary, -// ); - -// final surface = ResolvableColorToken( -// '--md-color-surface', -// (context) => context.colorScheme.surface, -// ); - -// final background = ResolvableColorToken( -// '--md-color-background', -// (context) => context.colorScheme.background, -// ); - -// final error = ResolvableColorToken( -// '--md-color-error', -// (context) => context.colorScheme.error, -// ); - -// final onPrimary = ResolvableColorToken( -// '--md-color-on-primary', -// (context) => context.colorScheme.onPrimary, -// ); - -// final onSecondary = ResolvableColorToken( -// '--md-color-on-secondary', -// (context) => context.colorScheme.onSecondary, -// ); - -// final onTertiary = ResolvableColorToken( -// '--md-color-on-tertiary', -// (context) => context.colorScheme.onTertiary, -// ); - -// final onSurface = ResolvableColorToken( -// '--md-color-on-surface', -// (context) => context.colorScheme.onSurface, -// ); - -// final onBackground = ResolvableColorToken( -// '--md-color-on-background', -// (context) => context.colorScheme.onBackground, -// ); - -// final onError = ResolvableColorToken( -// '--md-color-on-error', -// (context) => context.colorScheme.onError, -// ); - -// _MaterialDesignColors(); -// } - -// // Material 3 TextTheme Tokens. -// class MaterialTextStyles { -// // Material 3 text styles -// final displayLarge = ResolvableTextStyleToken( -// '--md3-displa-large', -// (context) => context.textTheme.displayLarge!, -// ); -// final displayMedium = ResolvableTextStyleToken( -// '--md3-displaymedium', -// (context) => context.textTheme.displayMedium!, -// ); -// final displaySmall = ResolvableTextStyleToken( -// '--md3-display-small', -// (context) => context.textTheme.displaySmall!, -// ); -// final headlineLarge = ResolvableTextStyleToken( -// '--md3-headline-large', -// (context) => context.textTheme.headlineLarge!, -// ); -// final headlineMedium = ResolvableTextStyleToken( -// '--md3-headline-medium', -// (context) => context.textTheme.headlineMedium!, -// ); -// final headlineSmall = ResolvableTextStyleToken( -// '--md3-headline-small', -// (context) => context.textTheme.headlineSmall!, -// ); -// final titleLarge = ResolvableTextStyleToken( -// '--md3-title-large', -// (context) => context.textTheme.titleLarge!, -// ); -// final titleMedium = ResolvableTextStyleToken( -// '--md3-title-medium', -// (context) => context.textTheme.titleMedium!, -// ); -// final titleSmall = ResolvableTextStyleToken( -// '--md3-title-small', -// (context) => context.textTheme.titleSmall!, -// ); -// final bodyLarge = ResolvableTextStyleToken( -// '--md3-body-large', -// (context) => context.textTheme.bodyLarge!, -// ); -// final bodyMedium = ResolvableTextStyleToken( -// '--md3-body-medium', -// (context) => context.textTheme.bodyMedium!, -// ); -// final bodySmall = ResolvableTextStyleToken( -// '--md3-body-small', -// (context) => context.textTheme.bodySmall!, -// ); -// final labelLarge = ResolvableTextStyleToken( -// '--md3-label-large', -// (context) => context.textTheme.labelLarge!, -// ); -// final labelMedium = ResolvableTextStyleToken( -// '--md3-label-medium', -// (context) => context.textTheme.labelMedium!, -// ); -// final labelSmall = ResolvableTextStyleToken( -// '--md3-label-small', -// (context) => context.textTheme.labelSmall!, -// ); -// // Material 2 text styles -// final headline1 = ResolvableTextStyleToken( -// '--md2-text-style-headline1', -// (context) => context.textTheme.headline1!, -// ); -// final headline2 = ResolvableTextStyleToken( -// '--md2-text-style-headline2', -// (context) => context.textTheme.headline2!, -// ); -// final headline3 = ResolvableTextStyleToken( -// '--md2-text-style-headline3', -// (context) => context.textTheme.headline3!, -// ); -// final headline4 = ResolvableTextStyleToken( -// '--md2-text-style-headline4', -// (context) => context.textTheme.headline4!, -// ); -// final headline5 = ResolvableTextStyleToken( -// '--md2-text-style-headline5', -// (context) => context.textTheme.headline5!, -// ); -// final headline6 = ResolvableTextStyleToken( -// '--md2-text-style-headline6', -// (context) => context.textTheme.headline6!, -// ); -// final subtitle1 = ResolvableTextStyleToken( -// '--md2-text-style-subtitle1', -// (context) => context.textTheme.subtitle1!, -// ); -// final subtitle2 = ResolvableTextStyleToken( -// '--md2-text-style-subtitle2', -// (context) => context.textTheme.subtitle2!, -// ); -// final bodyText1 = ResolvableTextStyleToken( -// '--md2-text-style-bodyText1', -// (context) => context.textTheme.bodyText1!, -// ); -// final bodyText2 = ResolvableTextStyleToken( -// '--md2-text-style-bodyText2', -// (context) => context.textTheme.bodyText2!, -// ); -// final caption = ResolvableTextStyleToken( -// '--md2-text-style-caption', -// (context) => context.textTheme.caption!, -// ); -// final button = ResolvableTextStyleToken( -// '--md2-text-style-button', -// (context) => context.textTheme.button!, -// ); -// final overline = ResolvableTextStyleToken( -// '--md2-text-style-overline', -// (context) => context.textTheme.overline!, -// ); - -// MaterialTextStyles(); -// } - -// final $md = _MaterialTokens(); - -// class _MaterialTokens { -// final colors = _MaterialDesignColors(); -// final textStyles = MaterialTextStyles(); -// _MaterialTokens(); -// } - void main() { // Create a test that checks if all the values of these tokens match the ThemeData from the MaterialApp group('Material tokens', () { @@ -214,22 +14,32 @@ void main() { ); final context = tester.element(find.byType(Container)); final colors = MaterialTokens().colors; - expect(colors.primary.resolve(context), theme.colorScheme.primary); - expect(colors.secondary.resolve(context), theme.colorScheme.secondary); - expect(colors.tertiary.resolve(context), theme.colorScheme.tertiary); - expect(colors.surface.resolve(context), theme.colorScheme.surface); - expect(colors.background.resolve(context), theme.colorScheme.background); - expect(colors.error.resolve(context), theme.colorScheme.error); - expect(colors.onPrimary.resolve(context), theme.colorScheme.onPrimary); - expect( - colors.onSecondary.resolve(context), theme.colorScheme.onSecondary); - expect(colors.onTertiary.resolve(context), theme.colorScheme.onTertiary); - expect(colors.onSurface.resolve(context), theme.colorScheme.onSurface); + expect((colors.primary() as ColorRef).resolve(context), + theme.colorScheme.primary); + expect((colors.secondary() as ColorRef).resolve(context), + theme.colorScheme.secondary); + expect((colors.tertiary() as ColorRef).resolve(context), + theme.colorScheme.tertiary); + expect((colors.surface() as ColorRef).resolve(context), + theme.colorScheme.surface); + expect((colors.background() as ColorRef).resolve(context), + theme.colorScheme.background); + expect((colors.error() as ColorRef).resolve(context), + theme.colorScheme.error); + expect((colors.onPrimary() as ColorRef).resolve(context), + theme.colorScheme.onPrimary); + expect((colors.onSecondary() as ColorRef).resolve(context), + theme.colorScheme.onSecondary); + expect((colors.onTertiary() as ColorRef).resolve(context), + theme.colorScheme.onTertiary); + expect((colors.onSurface() as ColorRef).resolve(context), + theme.colorScheme.onSurface); expect( - colors.onBackground.resolve(context), + (colors.onBackground() as ColorRef).resolve(context), theme.colorScheme.onBackground, ); - expect(colors.onError.resolve(context), theme.colorScheme.onError); + expect((colors.onError() as ColorRef).resolve(context), + theme.colorScheme.onError); }); testWidgets('Material 3 textStyles', (tester) async { @@ -242,34 +52,36 @@ void main() { final theme = Theme.of(context); final textStyles = MaterialTokens().textStyles; - expect(textStyles.displayLarge.resolve(context), + expect((textStyles.displayLarge() as TextStyleRef).resolve(context), theme.textTheme.displayLarge); - expect(textStyles.displayMedium.resolve(context), + expect((textStyles.displayMedium() as TextStyleRef).resolve(context), theme.textTheme.displayMedium); - expect(textStyles.displaySmall.resolve(context), + expect((textStyles.displaySmall() as TextStyleRef).resolve(context), theme.textTheme.displaySmall); - expect(textStyles.headlineLarge.resolve(context), + expect((textStyles.headlineLarge() as TextStyleRef).resolve(context), theme.textTheme.headlineLarge); - expect(textStyles.headlineMedium.resolve(context), + expect((textStyles.headlineMedium() as TextStyleRef).resolve(context), theme.textTheme.headlineMedium); - expect(textStyles.headlineSmall.resolve(context), + expect((textStyles.headlineSmall() as TextStyleRef).resolve(context), theme.textTheme.headlineSmall); - expect( - textStyles.titleLarge.resolve(context), theme.textTheme.titleLarge); - expect( - textStyles.titleMedium.resolve(context), theme.textTheme.titleMedium); - expect( - textStyles.titleSmall.resolve(context), theme.textTheme.titleSmall); - expect(textStyles.bodyLarge.resolve(context), theme.textTheme.bodyLarge); - expect( - textStyles.bodyMedium.resolve(context), theme.textTheme.bodyMedium); - expect(textStyles.bodySmall.resolve(context), theme.textTheme.bodySmall); - expect( - textStyles.labelLarge.resolve(context), theme.textTheme.labelLarge); - expect( - textStyles.labelMedium.resolve(context), theme.textTheme.labelMedium); - expect( - textStyles.labelSmall.resolve(context), theme.textTheme.labelSmall); + expect((textStyles.titleLarge() as TextStyleRef).resolve(context), + theme.textTheme.titleLarge); + expect((textStyles.titleMedium() as TextStyleRef).resolve(context), + theme.textTheme.titleMedium); + expect((textStyles.titleSmall() as TextStyleRef).resolve(context), + theme.textTheme.titleSmall); + expect((textStyles.bodyLarge() as TextStyleRef).resolve(context), + theme.textTheme.bodyLarge); + expect((textStyles.bodyMedium() as TextStyleRef).resolve(context), + theme.textTheme.bodyMedium); + expect((textStyles.bodySmall() as TextStyleRef).resolve(context), + theme.textTheme.bodySmall); + expect((textStyles.labelLarge() as TextStyleRef).resolve(context), + theme.textTheme.labelLarge); + expect((textStyles.labelMedium() as TextStyleRef).resolve(context), + theme.textTheme.labelMedium); + expect((textStyles.labelSmall() as TextStyleRef).resolve(context), + theme.textTheme.labelSmall); }); testWidgets('Material 2 text styles', (tester) async { @@ -281,19 +93,32 @@ void main() { final theme = Theme.of(context); final textStyles = MaterialTokens().textStyles; - expect(textStyles.headline1.resolve(context), theme.textTheme.headline1); - expect(textStyles.headline2.resolve(context), theme.textTheme.headline2); - expect(textStyles.headline3.resolve(context), theme.textTheme.headline3); - expect(textStyles.headline4.resolve(context), theme.textTheme.headline4); - expect(textStyles.headline5.resolve(context), theme.textTheme.headline5); - expect(textStyles.headline6.resolve(context), theme.textTheme.headline6); - expect(textStyles.subtitle1.resolve(context), theme.textTheme.subtitle1); - expect(textStyles.subtitle2.resolve(context), theme.textTheme.subtitle2); - expect(textStyles.bodyText1.resolve(context), theme.textTheme.bodyText1); - expect(textStyles.bodyText2.resolve(context), theme.textTheme.bodyText2); - expect(textStyles.caption.resolve(context), theme.textTheme.caption); - expect(textStyles.button.resolve(context), theme.textTheme.button); - expect(textStyles.overline.resolve(context), theme.textTheme.overline); + expect((textStyles.headline1() as TextStyleRef).resolve(context), + theme.textTheme.headline1); + expect((textStyles.headline2() as TextStyleRef).resolve(context), + theme.textTheme.headline2); + expect((textStyles.headline3() as TextStyleRef).resolve(context), + theme.textTheme.headline3); + expect((textStyles.headline4() as TextStyleRef).resolve(context), + theme.textTheme.headline4); + expect((textStyles.headline5() as TextStyleRef).resolve(context), + theme.textTheme.headline5); + expect((textStyles.headline6() as TextStyleRef).resolve(context), + theme.textTheme.headline6); + expect((textStyles.subtitle1() as TextStyleRef).resolve(context), + theme.textTheme.subtitle1); + expect((textStyles.subtitle2() as TextStyleRef).resolve(context), + theme.textTheme.subtitle2); + expect((textStyles.bodyText1() as TextStyleRef).resolve(context), + theme.textTheme.bodyText1); + expect((textStyles.bodyText2() as TextStyleRef).resolve(context), + theme.textTheme.bodyText2); + expect((textStyles.caption() as TextStyleRef).resolve(context), + theme.textTheme.caption); + expect((textStyles.button() as TextStyleRef).resolve(context), + theme.textTheme.button); + expect((textStyles.overline() as TextStyleRef).resolve(context), + theme.textTheme.overline); }); }); } diff --git a/test/src/theme/tokens/radius_token_test.dart b/test/src/theme/tokens/radius_token_test.dart index c910f3562..76678c6f4 100644 --- a/test/src/theme/tokens/radius_token_test.dart +++ b/test/src/theme/tokens/radius_token_test.dart @@ -1,21 +1,20 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; -import 'package:mix/src/theme/tokens/radius_token.dart'; import '../../../helpers/testing_utils.dart'; void main() { group('RadiusToken', () { test('Constructor assigns name correctly', () { - const radiusRef = RadiusToken('testName'); + const radiusRef = RadiusToken.name('testName'); expect(radiusRef.name, 'testName'); }); test('Equality operator works correctly', () { - const radiusRef1 = RadiusToken('testName'); - const radiusRef2 = RadiusToken('testName'); - const radiusRef3 = RadiusToken('differentName'); + const radiusRef1 = RadiusToken.name('testName'); + const radiusRef2 = RadiusToken.name('testName'); + const radiusRef3 = RadiusToken.name('differentName'); expect(radiusRef1 == radiusRef2, isTrue); expect(radiusRef1 == radiusRef3, isFalse); @@ -23,23 +22,25 @@ void main() { }); test('hashCode is consistent with name', () { - const radiusRef1 = RadiusToken('testName'); - const radiusRef2 = RadiusToken('testName'); - const radiusRef3 = RadiusToken('differentName'); + const radiusRef1 = RadiusToken.name('testName'); + const radiusRef2 = RadiusToken.name('testName'); + const radiusRef3 = RadiusToken.name('differentName'); expect(radiusRef1.hashCode, radiusRef2.hashCode); expect(radiusRef1.hashCode, isNot(radiusRef3.hashCode)); }); testWidgets('Test it resolves correctly', (tester) async { - const redRadiusRef = RadiusToken('red'); - const greenRadiusRef = RadiusToken('green'); - const blueRadiusRef = RadiusToken('blue'); - final theme = MixThemeData(radii: { - redRadiusRef: (_) => const Radius.circular(1), - greenRadiusRef: (_) => const Radius.circular(2), - blueRadiusRef: (_) => const Radius.circular(3), - }); + const redRadiusRef = RadiusToken.name('red'); + const greenRadiusRef = RadiusToken.name('green'); + const blueRadiusRef = RadiusToken.name('blue'); + final theme = MixThemeData.tokenMap( + radii: { + redRadiusRef: (_) => const Radius.circular(1), + greenRadiusRef: (_) => const Radius.circular(2), + blueRadiusRef: (_) => const Radius.circular(3), + }, + ); await tester.pumpWidget(createWithMixTheme(theme)); @@ -47,28 +48,27 @@ void main() { final mixData = MixData.create(context, StyleMix.empty); + expect(mixData.tokens.radiiToken(redRadiusRef), const Radius.circular(1)); expect( - mixData.resolver.radiiToken(redRadiusRef), const Radius.circular(1)); - expect(mixData.resolver.radiiToken(greenRadiusRef), - const Radius.circular(2)); + mixData.tokens.radiiToken(greenRadiusRef), const Radius.circular(2)); expect( - mixData.resolver.radiiToken(blueRadiusRef), const Radius.circular(3)); + mixData.tokens.radiiToken(blueRadiusRef), const Radius.circular(3)); }); }); - group('RadiusTokenResolver', () { + group('RadiusToken.resolvable', () { test('Constructor assigns name correctly', () { - final radiusRef = RadiusTokenResolver('testName', (_) => Radius.zero); + final radiusRef = RadiusToken.resolvable('testName', (_) => Radius.zero); expect(radiusRef.name, 'testName'); }); test('Equality operator works correctly', () { final radiusRef1 = - RadiusTokenResolver('testName', (_) => const Radius.circular(1)); + RadiusToken.resolvable('testName', (_) => const Radius.circular(1)); final radiusRef2 = - RadiusTokenResolver('testName', (_) => const Radius.circular(1)); - final radiusRef3 = - RadiusTokenResolver('differentName', (_) => const Radius.circular(1)); + RadiusToken.resolvable('testName', (_) => const Radius.circular(1)); + final radiusRef3 = RadiusToken.resolvable( + 'differentName', (_) => const Radius.circular(1)); expect(radiusRef1 == radiusRef2, isTrue); expect(radiusRef1 == radiusRef3, isFalse); @@ -77,11 +77,11 @@ void main() { test('hashCode is consistent with name', () { final radiusRef1 = - RadiusTokenResolver('testName', (_) => const Radius.circular(1)); + RadiusToken.resolvable('testName', (_) => const Radius.circular(1)); final radiusRef2 = - RadiusTokenResolver('testName', (_) => const Radius.circular(1)); - final radiusRef3 = - RadiusTokenResolver('differentName', (_) => const Radius.circular(1)); + RadiusToken.resolvable('testName', (_) => const Radius.circular(1)); + final radiusRef3 = RadiusToken.resolvable( + 'differentName', (_) => const Radius.circular(1)); expect(radiusRef1.hashCode, radiusRef2.hashCode); expect(radiusRef1.hashCode, isNot(radiusRef3.hashCode)); @@ -89,11 +89,11 @@ void main() { testWidgets('Test it resolves correctly', (tester) async { final redRadiusRef = - RadiusTokenResolver('red', (_) => const Radius.circular(1)); + RadiusToken.resolvable('red', (_) => const Radius.circular(1)); final greenRadiusRef = - RadiusTokenResolver('green', (_) => const Radius.circular(2)); + RadiusToken.resolvable('green', (_) => const Radius.circular(2)); final blueRadiusRef = - RadiusTokenResolver('blue', (_) => const Radius.circular(3)); + RadiusToken.resolvable('blue', (_) => const Radius.circular(3)); await tester.pumpMaterialApp(Container()); @@ -101,26 +101,25 @@ void main() { final mixData = MixData.create(context, StyleMix.empty); + expect(mixData.tokens.radiiToken(redRadiusRef), const Radius.circular(1)); expect( - mixData.resolver.radiiToken(redRadiusRef), const Radius.circular(1)); - expect(mixData.resolver.radiiToken(greenRadiusRef), - const Radius.circular(2)); + mixData.tokens.radiiToken(greenRadiusRef), const Radius.circular(2)); expect( - mixData.resolver.radiiToken(blueRadiusRef), const Radius.circular(3)); + mixData.tokens.radiiToken(blueRadiusRef), const Radius.circular(3)); }); }); group('RadiiTokenUtil', () { test('small returns correct value', () { - expect(const RadiiTokenUtil().small, RadiusToken.small); + expect(RadiiTokenUtil().small, RadiusToken.small()); }); test('medium returns correct value', () { - expect(const RadiiTokenUtil().medium, RadiusToken.medium); + expect(RadiiTokenUtil().medium, RadiusToken.medium()); }); test('large returns correct value', () { - expect(const RadiiTokenUtil().large, RadiusToken.large); + expect(RadiiTokenUtil().large, RadiusToken.large()); }); }); @@ -132,9 +131,9 @@ void main() { () { final radiiTokenUtil = UtilityWithRadiusTokens((value) => value); - expect(radiiTokenUtil.small, RadiusToken.small); - expect(radiiTokenUtil.medium, RadiusToken.medium); - expect(radiiTokenUtil.large, RadiusToken.large); + expect(radiiTokenUtil.small(), RadiusToken.small()); + expect(radiiTokenUtil.medium(), RadiusToken.medium()); + expect(radiiTokenUtil.large(), RadiusToken.large()); }, ); }, diff --git a/test/src/theme/tokens/space_token_test.dart b/test/src/theme/tokens/space_token_test.dart index 009f50c98..3754df1e0 100644 --- a/test/src/theme/tokens/space_token_test.dart +++ b/test/src/theme/tokens/space_token_test.dart @@ -1,13 +1,15 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; +import '../../../helpers/testing_utils.dart'; + void main() { group('SpaceToken tests', () { test('SpaceToken.xsmall() returns correct value', () { expect(SpaceToken.xsmall(), SpaceToken.xsmall()); - expect(const SpaceToken('--mix-space-xsmall'), SpaceToken.xsmall); - expect('--mix-space-xsmall', SpaceToken.xsmall.name); - expect('--mix-space-xsmall'.hashCode, SpaceToken.xsmall.name.hashCode); + expect(const SpaceToken('mix.space.xsmall', 4.0), SpaceToken.xsmall); + expect('mix.space.xsmall', SpaceToken.xsmall.name); + expect('mix.space.xsmall'.hashCode, SpaceToken.xsmall.name.hashCode); expect(SpaceToken.xsmall(), lessThan(0)); expect(SpaceToken.xsmall(), -1.0 * SpaceToken.xsmall.hashCode); @@ -15,9 +17,9 @@ void main() { test('SpaceToken.small() returns correct value', () { expect(SpaceToken.small(), SpaceToken.small()); - expect(const SpaceToken('--mix-space-small'), SpaceToken.small); - expect('--mix-space-small', SpaceToken.small.name); - expect('--mix-space-small'.hashCode, SpaceToken.small.name.hashCode); + expect(const SpaceToken('mix.space.small', 8.0), SpaceToken.small); + expect('mix.space.small', SpaceToken.small.name); + expect('mix.space.small'.hashCode, SpaceToken.small.name.hashCode); expect(SpaceToken.small(), lessThan(0)); expect(SpaceToken.small(), -1.0 * SpaceToken.small.hashCode); @@ -25,9 +27,9 @@ void main() { test('SpaceToken.medium() returns correct value', () { expect(SpaceToken.medium(), SpaceToken.medium()); - expect(const SpaceToken('--mix-space-medium'), SpaceToken.medium); - expect('--mix-space-medium', SpaceToken.medium.name); - expect('--mix-space-medium'.hashCode, SpaceToken.medium.name.hashCode); + expect(const SpaceToken('mix.space.medium', 16.0), SpaceToken.medium); + expect('mix.space.medium', SpaceToken.medium.name); + expect('mix.space.medium'.hashCode, SpaceToken.medium.name.hashCode); expect(SpaceToken.medium(), lessThan(0)); expect(SpaceToken.medium(), -1.0 * SpaceToken.medium.hashCode); @@ -35,16 +37,16 @@ void main() { test('SpaceToken.large() returns correct value', () { expect(SpaceToken.large(), SpaceToken.large()); - expect(const SpaceToken('--mix-space-large'), SpaceToken.large); - expect('--mix-space-large', SpaceToken.large.name); - expect('--mix-space-large'.hashCode, SpaceToken.large.name.hashCode); + expect(const SpaceToken('mix.space.large', 24.0), SpaceToken.large); + expect('mix.space.large', SpaceToken.large.name); + expect('mix.space.large'.hashCode, SpaceToken.large.name.hashCode); }); test('SpaceToken.xlarge() returns correct value', () { expect(SpaceToken.xlarge(), SpaceToken.xlarge()); - expect(const SpaceToken('--mix-space-xlarge'), SpaceToken.xlarge); - expect('--mix-space-xlarge', SpaceToken.xlarge.name); - expect('--mix-space-xlarge'.hashCode, SpaceToken.xlarge.name.hashCode); + expect(const SpaceToken('mix.space.xlarge', 32.0), SpaceToken.xlarge); + expect('mix.space.xlarge', SpaceToken.xlarge.name); + expect('mix.space.xlarge'.hashCode, SpaceToken.xlarge.name.hashCode); expect(SpaceToken.xlarge(), lessThan(0)); expect(SpaceToken.xlarge(), -1.0 * SpaceToken.xlarge.hashCode); @@ -52,9 +54,9 @@ void main() { test('SpaceToken.xxlarge() returns correct value', () { expect(SpaceToken.xxlarge(), SpaceToken.xxlarge()); - expect(const SpaceToken('--mix-space-xxlarge'), SpaceToken.xxlarge); - expect('--mix-space-xxlarge', SpaceToken.xxlarge.name); - expect('--mix-space-xxlarge'.hashCode, SpaceToken.xxlarge.name.hashCode); + expect(const SpaceToken('mix.space.xxlarge', 40.0), SpaceToken.xxlarge); + expect('mix.space.xxlarge', SpaceToken.xxlarge.name); + expect('mix.space.xxlarge'.hashCode, SpaceToken.xxlarge.name.hashCode); expect(SpaceToken.xxlarge(), lessThan(0)); expect(SpaceToken.xxlarge(), -1.0 * SpaceToken.xxlarge.hashCode); @@ -63,25 +65,25 @@ void main() { group('WithSpaceTokens tests', () { test('WithSpaceTokens returns correct value', () { - final withSpaceTokens = UtilityWithSpaceTokens((value) => value); - expect(withSpaceTokens.xsmall, SpaceToken.xsmall()); - expect(withSpaceTokens.small, SpaceToken.small()); - expect(withSpaceTokens.medium, SpaceToken.medium()); - expect(withSpaceTokens.large, SpaceToken.large()); - expect(withSpaceTokens.xlarge, SpaceToken.xlarge()); - expect(withSpaceTokens.xxlarge, SpaceToken.xxlarge()); + const withSpaceTokens = SpacingSideUtility(UtilityTestAttribute.new); + expect(withSpaceTokens.xsmall().value, SpaceToken.xsmall()); + expect(withSpaceTokens.small().value, SpaceToken.small()); + expect(withSpaceTokens.medium().value, SpaceToken.medium()); + expect(withSpaceTokens.large().value, SpaceToken.large()); + expect(withSpaceTokens.xlarge().value, SpaceToken.xlarge()); + expect(withSpaceTokens.xxlarge().value, SpaceToken.xxlarge()); }); }); group('SpaceTokenUtil', () { test('SpaceTokenUtil returns correct value', () { - final spaceTokenUtil = SpaceTokenUtil(); - expect(spaceTokenUtil.xsmall, SpaceToken.xsmall()); - expect(spaceTokenUtil.small, SpaceToken.small()); - expect(spaceTokenUtil.medium, SpaceToken.medium()); - expect(spaceTokenUtil.large, SpaceToken.large()); - expect(spaceTokenUtil.xlarge, SpaceToken.xlarge()); - expect(spaceTokenUtil.xxlarge, SpaceToken.xxlarge()); + const spaceTokenUtil = SpaceTokenUtil(); + expect(spaceTokenUtil.xsmall(), SpaceToken.xsmall()); + expect(spaceTokenUtil.small(), SpaceToken.small()); + expect(spaceTokenUtil.medium(), SpaceToken.medium()); + expect(spaceTokenUtil.large(), SpaceToken.large()); + expect(spaceTokenUtil.xlarge(), SpaceToken.xlarge()); + expect(spaceTokenUtil.xxlarge(), SpaceToken.xxlarge()); }); }); } diff --git a/test/src/theme/tokens/text_style_token_test.dart b/test/src/theme/tokens/text_style_token_test.dart index 7224afac5..47de709f6 100644 --- a/test/src/theme/tokens/text_style_token_test.dart +++ b/test/src/theme/tokens/text_style_token_test.dart @@ -10,14 +10,14 @@ import '../../../helpers/testing_utils.dart'; void main() { group('TextStyleToken', () { test('Constructor assigns name correctly', () { - const textStyleToken = TextStyleToken('testName'); + const textStyleToken = TextStyleToken.name('testName'); expect(textStyleToken.name, 'testName'); }); test('Equality operator works correctly', () { - const textStyleToken1 = TextStyleToken('testName'); - const textStyleToken2 = TextStyleToken('testName'); - const textStyleToken3 = TextStyleToken('differentName'); + const textStyleToken1 = TextStyleToken.name('testName'); + const textStyleToken2 = TextStyleToken.name('testName'); + const textStyleToken3 = TextStyleToken.name('differentName'); expect(textStyleToken1 == textStyleToken2, isTrue); expect(textStyleToken1 == textStyleToken3, isFalse); @@ -25,19 +25,19 @@ void main() { }); test('hashCode is consistent with name', () { - const textStyleToken1 = TextStyleToken('testName'); - const textStyleToken2 = TextStyleToken('testName'); - const textStyleToken3 = TextStyleToken('differentName'); + const textStyleToken1 = TextStyleToken.name('testName'); + const textStyleToken2 = TextStyleToken.name('testName'); + const textStyleToken3 = TextStyleToken.name('differentName'); expect(textStyleToken1.hashCode, textStyleToken2.hashCode); expect(textStyleToken1.hashCode, isNot(textStyleToken3.hashCode)); }); testWidgets('Test it resolves correctly', (tester) async { - const redtextStyleToken = TextStyleToken('red'); - const greentextStyleToken = TextStyleToken('green'); - const bluetextStyleToken = TextStyleToken('blue'); - final theme = MixThemeData( + const redtextStyleToken = TextStyleToken.name('red'); + const greentextStyleToken = TextStyleToken.name('green'); + const bluetextStyleToken = TextStyleToken.name('blue'); + final theme = MixThemeData.tokenMap( textStyles: { redtextStyleToken: (_) => const TextStyle(color: Colors.red), greentextStyleToken: (_) => const TextStyle(color: Colors.green), @@ -51,11 +51,11 @@ void main() { final mixData = MixData.create(context, StyleMix.empty); - expect(mixData.resolver.textStyleToken(redtextStyleToken), + expect(mixData.tokens.textStyleToken(redtextStyleToken), const TextStyle(color: Colors.red)); - expect(mixData.resolver.textStyleToken(greentextStyleToken), + expect(mixData.tokens.textStyleToken(greentextStyleToken), const TextStyle(color: Colors.green)); - expect(mixData.resolver.textStyleToken(bluetextStyleToken), + expect(mixData.tokens.textStyleToken(bluetextStyleToken), const TextStyle(color: Colors.blue)); }); }); @@ -64,17 +64,17 @@ void main() { group('TextStyleResolvableToken', () { test('Constructor assigns name correctly', () { final textStyleToken = - TextStyleTokenResolver('testName', (_) => const TextStyle()); + TextStyleToken.resolvable('testName', (_) => const TextStyle()); expect(textStyleToken.name, 'testName'); }); test('Equality operator works correctly', () { final textStyleToken1 = - TextStyleTokenResolver('testName', (_) => const TextStyle()); + TextStyleToken.resolvable('testName', (_) => const TextStyle()); final textStyleToken2 = - TextStyleTokenResolver('testName', (_) => const TextStyle()); + TextStyleToken.resolvable('testName', (_) => const TextStyle()); final textStyleToken3 = - TextStyleTokenResolver('differentName', (_) => const TextStyle()); + TextStyleToken.resolvable('differentName', (_) => const TextStyle()); expect(textStyleToken1 == textStyleToken2, isTrue); expect(textStyleToken1 == textStyleToken3, isFalse); @@ -83,22 +83,22 @@ void main() { test('hashCode is consistent with name', () { final textStyleToken1 = - TextStyleTokenResolver('testName', (_) => const TextStyle()); + TextStyleToken.resolvable('testName', (_) => const TextStyle()); final textStyleToken2 = - TextStyleTokenResolver('testName', (_) => const TextStyle()); + TextStyleToken.resolvable('testName', (_) => const TextStyle()); final textStyleToken3 = - TextStyleTokenResolver('differentName', (_) => const TextStyle()); + TextStyleToken.resolvable('differentName', (_) => const TextStyle()); expect(textStyleToken1.hashCode, textStyleToken2.hashCode); expect(textStyleToken1.hashCode, isNot(textStyleToken3.hashCode)); }); testWidgets('Test it resolves correctly', (tester) async { - final redtextStyleToken = TextStyleTokenResolver( + final redtextStyleToken = TextStyleToken.resolvable( 'red', (_) => const TextStyle(color: Colors.red)); - final greentextStyleToken = TextStyleTokenResolver( + final greentextStyleToken = TextStyleToken.resolvable( 'green', (_) => const TextStyle(color: Colors.green)); - final bluetextStyleToken = TextStyleTokenResolver( + final bluetextStyleToken = TextStyleToken.resolvable( 'blue', (_) => const TextStyle(color: Colors.blue)); await tester.pumpMaterialApp(Container()); @@ -107,11 +107,11 @@ void main() { final mixData = MixData.create(context, StyleMix.empty); - expect(mixData.resolver.textStyleToken(redtextStyleToken), + expect(mixData.tokens.textStyleToken(redtextStyleToken), const TextStyle(color: Colors.red)); - expect(mixData.resolver.textStyleToken(greentextStyleToken), + expect(mixData.tokens.textStyleToken(greentextStyleToken), const TextStyle(color: Colors.green)); - expect(mixData.resolver.textStyleToken(bluetextStyleToken), + expect(mixData.tokens.textStyleToken(bluetextStyleToken), const TextStyle(color: Colors.blue)); }); }); diff --git a/test/src/utils/alignment_util_test.dart b/test/src/utils/alignment_util_test.dart deleted file mode 100644 index 82781ca36..000000000 --- a/test/src/utils/alignment_util_test.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'package:flutter/src/painting/alignment.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/mix.dart'; - -import '../../helpers/testing_utils.dart'; - -void main() { - group('AlignmentGeometryAttribute utilities:', () { - test('"alignment" takes in x and y and returns AlignmentAttribute', () { - var result = alignment(0, 1); - - expect(result, isA()); - expect(result, isA()); - expect(result.x, 0); - expect(result.y, 1); - }); - - test( - "alignmentDirectional takes in start and y and returns AlignmentDirectionalAttribute", - () { - var result = alignmentDirectional(0, 1); - - expect(result, isA()); - expect(result, isA()); - expect(result.start, 0); - expect(result.y, 1); - }); - - test( - 'predefined functions return correct AlignmentAttribute and resolves correctly', - () { - // alignmentTopLeft - expect(alignmentTopLeft(), isA()); - expect(alignmentTopLeft(), isA()); - expect(alignmentTopLeft().resolve(EmptyMixData), Alignment.topLeft); - - // alignmentTopCenter - expect(alignmentTopCenter(), isA()); - expect(alignmentTopCenter(), isA()); - expect(alignmentTopCenter().resolve(EmptyMixData), Alignment.topCenter); - - // alignmentTopRight - expect(alignmentTopRight(), isA()); - expect(alignmentTopRight(), isA()); - expect(alignmentTopRight().resolve(EmptyMixData), Alignment.topRight); - - // alignmentCenterLeft - expect(alignmentCenterLeft(), isA()); - expect(alignmentCenterLeft(), isA()); - expect(alignmentCenterLeft().resolve(EmptyMixData), Alignment.centerLeft); - - // alignmentCenter - expect(alignmentCenter(), isA()); - expect(alignmentCenter(), isA()); - expect(alignmentCenter().resolve(EmptyMixData), Alignment.center); - - // alignmentCenterRight - expect(alignmentCenterRight(), isA()); - expect(alignmentCenterRight(), isA()); - expect( - alignmentCenterRight().resolve(EmptyMixData), Alignment.centerRight); - - // alignmentBottomLeft - expect(alignmentBottomLeft(), isA()); - expect(alignmentBottomLeft(), isA()); - expect(alignmentBottomLeft().resolve(EmptyMixData), Alignment.bottomLeft); - - // alignmentBottomCenter - expect(alignmentBottomCenter(), isA()); - expect(alignmentBottomCenter(), isA()); - expect(alignmentBottomCenter().resolve(EmptyMixData), - Alignment.bottomCenter); - - // alignmentBottomRight - expect(alignmentBottomRight(), isA()); - expect(alignmentBottomRight(), isA()); - expect( - alignmentBottomRight().resolve(EmptyMixData), Alignment.bottomRight); - }); - test( - 'predefined functions return correct AlignmentDirectionalAttribute and resolves correctly', - () { - // alignmentTopStart - expect(alignmentTopStart(), isA()); - expect(alignmentTopStart(), isA()); - expect(alignmentTopStart().resolve(EmptyMixData), - AlignmentDirectional.topStart); - - // alignmentTopEnd - expect(alignmentTopEnd(), isA()); - expect(alignmentTopEnd(), isA()); - expect( - alignmentTopEnd().resolve(EmptyMixData), AlignmentDirectional.topEnd); - - // alignmentCenterStart - expect(alignmentCenterStart(), isA()); - expect(alignmentCenterStart(), isA()); - expect(alignmentCenterStart().resolve(EmptyMixData), - AlignmentDirectional.centerStart); - - // alignmentCenterEnd - expect(alignmentCenterEnd(), isA()); - expect(alignmentCenterEnd(), isA()); - expect(alignmentCenterEnd().resolve(EmptyMixData), - AlignmentDirectional.centerEnd); - - // alignmentBottomStart - expect(alignmentBottomStart(), isA()); - expect(alignmentBottomStart(), isA()); - expect(alignmentBottomStart().resolve(EmptyMixData), - AlignmentDirectional.bottomStart); - - // alignmentBottomEnd - expect(alignmentBottomEnd(), isA()); - expect(alignmentBottomEnd(), isA()); - expect(alignmentBottomEnd().resolve(EmptyMixData), - AlignmentDirectional.bottomEnd); - }); - }); -} diff --git a/test/src/utils/border_radius_util_test.dart b/test/src/utils/border_radius_util_test.dart deleted file mode 100644 index 1be4cc81d..000000000 --- a/test/src/utils/border_radius_util_test.dart +++ /dev/null @@ -1,197 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/mix.dart'; - -import '../../helpers/testing_utils.dart'; - -void main() { - group('BorderRadiusAttribute', () { - test('borderRadius()', () { - final attr = borderRadius(const Radius.circular(10)); - expect( - attr.resolve(EmptyMixData), - const BorderRadius.all( - Radius.circular(10), - )); - }); - - test('borderRadiusVertical()', () { - final attr = borderRadiusVertical( - top: const Radius.circular(10), - bottom: const Radius.circular(20), - ); - expect( - attr.resolve(EmptyMixData), - const BorderRadius.vertical( - top: Radius.circular(10), - bottom: Radius.circular(20), - )); - }); - - test('borderRadiusHorizontal()', () { - final attr = borderRadiusHorizontal( - left: const Radius.circular(10), - right: const Radius.circular(20), - ); - expect( - attr.resolve(EmptyMixData), - const BorderRadius.horizontal( - left: Radius.circular(10), - right: Radius.circular(20), - )); - }); - - test('borderRadiusZero()', () { - final attr = borderRadius(Radius.zero); - expect( - attr.resolve(EmptyMixData), - const BorderRadius.all( - Radius.zero, - )); - }); - - test('borderRadiusOnly()', () { - final attr = borderRadiusOnly( - topLeft: const Radius.circular(10), - topRight: const Radius.circular(20), - bottomLeft: const Radius.circular(30), - bottomRight: const Radius.circular(40), - ); - expect( - attr.resolve(EmptyMixData), - const BorderRadius.only( - topLeft: Radius.circular(10), - topRight: Radius.circular(20), - bottomLeft: Radius.circular(30), - bottomRight: Radius.circular(40), - )); - }); - - test('borderRadiusDirectional()', () { - final attr = borderRadiusDirectional(const Radius.circular(10)); - expect( - attr.resolve(EmptyMixData), - const BorderRadiusDirectional.all( - Radius.circular(10), - )); - }); - }); - - group('Rounded utilities', () { - test('rounded()', () { - final attr = rounded(10, 20, 30, 40); - expect( - attr.resolve(EmptyMixData), - const BorderRadius.only( - topLeft: Radius.circular(10), - topRight: Radius.circular(20), - bottomLeft: Radius.circular(30), - bottomRight: Radius.circular(40), - )); - }); - - test('roundedDirectional()', () { - final attr = roundedDirectional(10, 20, 30, 40); - expect( - attr.resolve(EmptyMixData), - const BorderRadiusDirectional.only( - topStart: Radius.circular(10), - topEnd: Radius.circular(20), - bottomStart: Radius.circular(30), - bottomEnd: Radius.circular(40), - )); - }); - - test('roundedTopLeft()', () { - final attr = roundedTopLeft(10); - expect( - attr.resolve(EmptyMixData), - const BorderRadius.only( - topLeft: Radius.circular(10), - )); - }); - - test('roundedTopRight()', () { - final attr = roundedTopRight(10); - expect( - attr.resolve(EmptyMixData), - const BorderRadius.only( - topRight: Radius.circular(10), - )); - }); - - test('roundedBottomLeft()', () { - final attr = roundedBottomLeft(10); - expect( - attr.resolve(EmptyMixData), - const BorderRadius.only( - bottomLeft: Radius.circular(10), - )); - }); - - test('roundedBottomRight()', () { - final attr = roundedBottomRight(10); - expect( - attr.resolve(EmptyMixData), - const BorderRadius.only( - bottomRight: Radius.circular(10), - )); - }); - - test('roundedTopStart()', () { - final attr = roundedTopStart(10); - expect( - attr.resolve(EmptyMixData), - const BorderRadiusDirectional.only( - topStart: Radius.circular(10), - )); - }); - - test('roundedTopEnd()', () { - final attr = roundedTopEnd(10); - expect( - attr.resolve(EmptyMixData), - const BorderRadiusDirectional.only( - topEnd: Radius.circular(10), - )); - }); - - test('roundedBottomStart()', () { - final attr = roundedBottomStart(10); - expect( - attr.resolve(EmptyMixData), - const BorderRadiusDirectional.only( - bottomStart: Radius.circular(10), - )); - }); - - test('roundedBottomEnd()', () { - final attr = roundedBottomEnd(10); - expect( - attr.resolve(EmptyMixData), - const BorderRadiusDirectional.only( - bottomEnd: Radius.circular(10), - )); - }); - - test('roundedHorizontal()', () { - final attr = roundedHorizontal(left: 10, right: 20); - expect( - attr.resolve(EmptyMixData), - const BorderRadius.horizontal( - left: Radius.circular(10), - right: Radius.circular(20), - )); - }); - - test('roundedVertical()', () { - final attr = roundedVertical(top: 10, bottom: 20); - expect( - attr.resolve(EmptyMixData), - const BorderRadius.vertical( - top: Radius.circular(10), - bottom: Radius.circular(20), - )); - }); - }); -} diff --git a/test/src/utils/border_util_test.dart b/test/src/utils/border_util_test.dart deleted file mode 100644 index cc185280d..000000000 --- a/test/src/utils/border_util_test.dart +++ /dev/null @@ -1,126 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/mix.dart'; - -void main() { - group('Border Util Tests', () { - test('borderTop()', () { - final result = borderTop( - color: Colors.red, - width: 10.0, - style: BorderStyle.solid, - strokeAlign: 0.5); - expect(result.top?.color?.value, Colors.red); - expect(result.top?.width, 10.0); - expect(result.top?.style, BorderStyle.solid); - expect(result.top?.strokeAlign, 0.5); - expect(result.right, null); - expect(result.bottom, null); - expect(result.left, null); - }); - - test('borderBottom()', () { - final result = borderBottom( - color: Colors.red, - width: 10.0, - style: BorderStyle.solid, - strokeAlign: 0.5); - expect(result.bottom?.color?.value, Colors.red); - expect(result.bottom?.width, 10.0); - expect(result.bottom?.style, BorderStyle.solid); - expect(result.bottom?.strokeAlign, 0.5); - expect(result.right, null); - expect(result.top, null); - expect(result.left, null); - }); - - test('borderLeft()', () { - final result = borderLeft( - color: Colors.red, - width: 10.0, - style: BorderStyle.solid, - strokeAlign: 0.5); - expect(result.left?.color?.value, Colors.red); - expect(result.left?.width, 10.0); - expect(result.left?.style, BorderStyle.solid); - expect(result.left?.strokeAlign, 0.5); - expect(result.right, null); - expect(result.top, null); - expect(result.bottom, null); - }); - - test('borderRight()', () { - final result = borderRight( - color: Colors.red, - width: 10.0, - style: BorderStyle.solid, - strokeAlign: 0.5); - expect(result.right?.color?.value, Colors.red); - expect(result.right?.width, 10.0); - expect(result.right?.style, BorderStyle.solid); - expect(result.right?.strokeAlign, 0.5); - expect(result.left, null); - expect(result.top, null); - expect(result.bottom, null); - }); - - test('borderHorizontal()', () { - final result = borderHorizontal( - color: Colors.blue, - width: 5.0, - style: BorderStyle.solid, - strokeAlign: 0.3); - expect(result.top?.color?.value, Colors.blue); - expect(result.top?.width, 5.0); - expect(result.top?.style, BorderStyle.solid); - expect(result.top?.strokeAlign, 0.3); - expect(result.bottom?.color?.value, Colors.blue); - expect(result.bottom?.width, 5.0); - expect(result.bottom?.style, BorderStyle.solid); - expect(result.bottom?.strokeAlign, 0.3); - expect(result.left, null); - expect(result.right, null); - }); - - test('borderVertical()', () { - final result = borderVertical( - color: Colors.green, - width: 7.0, - style: BorderStyle.solid, - strokeAlign: 0.2); - expect(result.left?.color?.value, Colors.green); - expect(result.left?.width, 7.0); - expect(result.left?.style, BorderStyle.solid); - expect(result.left?.strokeAlign, 0.2); - expect(result.right?.color?.value, Colors.green); - expect(result.right?.width, 7.0); - expect(result.right?.style, BorderStyle.solid); - expect(result.right?.strokeAlign, 0.2); - expect(result.top, null); - expect(result.bottom, null); - }); - - test('borderSymetric()', () { - final verticalSide = borderVertical( - color: Colors.purple, - width: 4.0, - style: BorderStyle.none, - strokeAlign: 1.0, - ); - final horizontalSide = borderHorizontal( - color: Colors.orange, - width: 2.0, - style: BorderStyle.none, - strokeAlign: 0.0, - ); - - final result = verticalSide.merge(horizontalSide); - - expect(result.left, verticalSide.left); - expect(result.right, verticalSide.right); - expect(result.top, horizontalSide.top); - expect(result.bottom, horizontalSide.bottom); - expect(result, isA()); - }); - }); -} diff --git a/test/src/utils/box_constraints_util_test.dart b/test/src/utils/box_constraints_util_test.dart deleted file mode 100644 index ed49edf02..000000000 --- a/test/src/utils/box_constraints_util_test.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/mix.dart'; - -void main() { - group('BoxConstraintsAttribute Utilities', () { - test('boxConstraints()', () { - final result = boxConstraints( - minWidth: 50.0, - maxWidth: 150.0, - minHeight: 100.0, - maxHeight: 200.0, - ); - expect(result.minWidth, 50.0); - expect(result.maxWidth, 150.0); - expect(result.minHeight, 100.0); - expect(result.maxHeight, 200.0); - }); - - test('minWidth()', () { - final result = minWidth(50.0); - expect(result.minWidth, 50.0); - expect(result.maxWidth, isNull); - expect(result.minHeight, isNull); - expect(result.maxHeight, isNull); - }); - - test('maxWidth()', () { - final result = maxWidth(150.0); - expect(result.minWidth, isNull); - expect(result.maxWidth, 150.0); - expect(result.minHeight, isNull); - expect(result.maxHeight, isNull); - }); - - test('minHeight()', () { - final result = minHeight(100.0); - expect(result.minWidth, isNull); - expect(result.maxWidth, isNull); - expect(result.minHeight, 100.0); - expect(result.maxHeight, isNull); - }); - - test('maxHeight()', () { - final result = maxHeight(200.0); - expect(result.minWidth, isNull); - expect(result.maxWidth, isNull); - expect(result.minHeight, isNull); - expect(result.maxHeight, 200.0); - }); - - // width - test('width()', () { - final result = width(50.0); - - expect(result.minWidth, isNull); - expect(result.maxWidth, isNull); - expect(result.width, 50.0); - expect(result.minHeight, isNull); - expect(result.maxHeight, isNull); - expect(result.height, isNull); - }); - - // height - test('height()', () { - final result = height(50.0); - expect(result.minWidth, isNull); - expect(result.maxWidth, isNull); - expect(result.height, 50.0); - expect(result.minHeight, isNull); - expect(result.maxHeight, isNull); - expect(result.width, isNull); - }); - }); -} diff --git a/test/src/utils/context_variant_util/on_breakpoint_util_test.dart b/test/src/utils/context_variant_util/on_breakpoint_util_test.dart new file mode 100644 index 000000000..923432862 --- /dev/null +++ b/test/src/utils/context_variant_util/on_breakpoint_util_test.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('Breakpoint Utils', () { + const xSmallScreenWidth = Size(320, 480); + const smallScreenWidth = Size(640, 480); + const mediumScreenWidth = Size(1240, 768); + const largeScreenWidth = Size(1440, 900); + testWidgets('xSmall screen context variant', (tester) async { + await tester.pumpWidget(createMediaQuery(xSmallScreenWidth)); + var context = tester.element(find.byType(Container)); + + expect(onXSmall.when(context), true, reason: 'xsmall'); + expect(onSmall.when(context), false, reason: 'small'); + expect(onMedium.when(context), false, reason: 'medium'); + expect(onLarge.when(context), false, reason: 'large'); + }); + + testWidgets('small screen context variant', (tester) async { + await tester.pumpWidget(createMediaQuery(smallScreenWidth)); + var context = tester.element(find.byType(Container)); + + expect(onXSmall.when(context), false, reason: 'xsmall'); + expect(onSmall.when(context), true, reason: 'small'); + expect(onMedium.when(context), false, reason: 'medium'); + expect(onLarge.when(context), false, reason: 'large'); + }); + + testWidgets('medium screen context variant', (tester) async { + await tester.pumpWidget(createMediaQuery(mediumScreenWidth)); + var context = tester.element(find.byType(Container)); + + expect(onXSmall.when(context), false, reason: 'xsmall'); + expect(onSmall.when(context), false, reason: 'small'); + expect(onMedium.when(context), true, reason: 'medium'); + expect(onLarge.when(context), false, reason: 'large'); + }); + + testWidgets('large screen context variant', (tester) async { + await tester.pumpWidget(createMediaQuery(largeScreenWidth)); + var context = tester.element(find.byType(Container)); + + expect(onXSmall.when(context), false, reason: 'xsmall'); + expect(onSmall.when(context), false, reason: 'small'); + expect(onMedium.when(context), false, reason: 'medium'); + expect(onLarge.when(context), true, reason: 'large'); + }); + + test('have correct variant names', () { + expect(onXSmall.name, 'on-mix.breakpoint.xsmall'); + expect(onSmall.name, 'on-mix.breakpoint.small'); + expect(onMedium.name, 'on-mix.breakpoint.medium'); + expect(onLarge.name, 'on-mix.breakpoint.large'); + }); + }); +} diff --git a/test/src/utils/context_variant_util/on_brightness_util_test.dart b/test/src/utils/context_variant_util/on_brightness_util_test.dart new file mode 100644 index 000000000..cb6df47b7 --- /dev/null +++ b/test/src/utils/context_variant_util/on_brightness_util_test.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/src/utils/context_variant_util/on_brightness_util.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('Brightness Utils', () { + testWidgets('onLight context variant', (tester) async { + await tester.pumpWidget(createBrightnessTheme(Brightness.light)); + var context = tester.element(find.byType(Container)); + + expect(onLight.when(context), true, reason: 'light'); + expect(onDark.when(context), false, reason: 'dark'); + }); + + testWidgets('onDark context variant', (widgetTester) async { + await widgetTester.pumpWidget(createBrightnessTheme(Brightness.dark)); + var context = widgetTester.element(find.byType(Container)); + + expect(onLight.when(context), false, reason: 'light'); + expect(onDark.when(context), true, reason: 'dark'); + }); + test('have correct variant names', () { + expect(onLight.name, 'on-light'); + expect(onDark.name, 'on-dark'); + }); + }); +} diff --git a/test/src/utils/context_variant_util/on_directionality_util_test.dart b/test/src/utils/context_variant_util/on_directionality_util_test.dart new file mode 100644 index 000000000..4bdf14db1 --- /dev/null +++ b/test/src/utils/context_variant_util/on_directionality_util_test.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('Directionality Utils', () { + testWidgets('onRTL context variant', (tester) async { + await tester.pumpWidget(createDirectionality(TextDirection.rtl)); + var context = tester.element(find.byType(Container)); + + expect(onRTL.when(context), true, reason: 'rtl'); + expect(onLTR.when(context), false, reason: 'ltr'); + }); + + testWidgets('onLTR context variant', (tester) async { + await tester.pumpWidget(createDirectionality(TextDirection.ltr)); + var context = tester.element(find.byType(Container)); + + expect(onRTL.when(context), false, reason: 'rtl'); + expect(onLTR.when(context), true, reason: 'ltr'); + }); + test('have correct variant names', () { + expect(onRTL.name, 'on-rtl'); + expect(onLTR.name, 'on-ltr'); + }); + }); +} diff --git a/test/src/utils/context_variant_util/on_helper_util_test.dart b/test/src/utils/context_variant_util/on_helper_util_test.dart new file mode 100644 index 000000000..51c356b5f --- /dev/null +++ b/test/src/utils/context_variant_util/on_helper_util_test.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('Not Utils', () { + testWidgets('reverts when of context variant', (tester) async { + await tester.pumpWidget(createBrightnessTheme(Brightness.light)); + var context = tester.element(find.byType(Container)); + + expect(onLight.when(context), true, reason: 'light'); + expect(onDark.when(context), false, reason: 'dark'); + + final notLight = onNot(onLight); + final notDark = onNot(onDark); + + expect(notLight.when(context), false, reason: 'not light'); + expect(notDark.when(context), true, reason: 'not dark'); + }); + }); +} diff --git a/test/src/utils/context_variant_util/on_orientation_util_test.dart b/test/src/utils/context_variant_util/on_orientation_util_test.dart new file mode 100644 index 000000000..8404635a2 --- /dev/null +++ b/test/src/utils/context_variant_util/on_orientation_util_test.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +void main() { + group('Orientation Utils', () { + testWidgets('onPortrait context variant', (tester) async { + tester.view.physicalSize = const Size(400, 600); + await tester.pumpWidget(Container()); + var context = tester.element(find.byType(Container)); + + expect(onPortrait.when(context), true, reason: 'portrait'); + expect(onLandscape.when(context), false, reason: 'landscape'); + + addTearDown(tester.view.resetPhysicalSize); + }); + + testWidgets('onLandscape context variant', (tester) async { + tester.view.physicalSize = const Size(600, 400); + await tester.pumpWidget(Container()); + var context = tester.element(find.byType(Container)); + + expect(onPortrait.when(context), false, reason: 'portrait'); + expect(onLandscape.when(context), true, reason: 'landscape'); + addTearDown(tester.view.resetPhysicalSize); + }); + test('have correct variant names', () { + expect(onPortrait.name, 'on-portrait'); + expect(onLandscape.name, 'on-landscape'); + }); + }); +} diff --git a/test/src/utils/context_variant_util_test.dart b/test/src/utils/context_variant_util_test.dart index 932aa708e..e331cbacb 100644 --- a/test/src/utils/context_variant_util_test.dart +++ b/test/src/utils/context_variant_util_test.dart @@ -18,129 +18,6 @@ void main() { expect(variant1, isNot(variant2)); }); }); - group('Breakpoint Utils', () { - const xSmallScreenWidth = Size(320, 480); - const smallScreenWidth = Size(640, 480); - const mediumScreenWidth = Size(1240, 768); - const largeScreenWidth = Size(1440, 900); - testWidgets('xSmall screen context variant', (tester) async { - await tester.pumpWidget(createMediaQuery(xSmallScreenWidth)); - var context = tester.element(find.byType(Container)); - - expect(onXSmall.when(context), true, reason: 'xsmall'); - expect(onSmall.when(context), false, reason: 'small'); - expect(onMedium.when(context), false, reason: 'medium'); - expect(onLarge.when(context), false, reason: 'large'); - }); - - testWidgets('small screen context variant', (tester) async { - await tester.pumpWidget(createMediaQuery(smallScreenWidth)); - var context = tester.element(find.byType(Container)); - - expect(onXSmall.when(context), false, reason: 'xsmall'); - expect(onSmall.when(context), true, reason: 'small'); - expect(onMedium.when(context), false, reason: 'medium'); - expect(onLarge.when(context), false, reason: 'large'); - }); - - testWidgets('medium screen context variant', (tester) async { - await tester.pumpWidget(createMediaQuery(mediumScreenWidth)); - var context = tester.element(find.byType(Container)); - - expect(onXSmall.when(context), false, reason: 'xsmall'); - expect(onSmall.when(context), false, reason: 'small'); - expect(onMedium.when(context), true, reason: 'medium'); - expect(onLarge.when(context), false, reason: 'large'); - }); - - testWidgets('large screen context variant', (tester) async { - await tester.pumpWidget(createMediaQuery(largeScreenWidth)); - var context = tester.element(find.byType(Container)); - - expect(onXSmall.when(context), false, reason: 'xsmall'); - expect(onSmall.when(context), false, reason: 'small'); - expect(onMedium.when(context), false, reason: 'medium'); - expect(onLarge.when(context), true, reason: 'large'); - }); - - test('have correct variant names', () { - expect(onXSmall.name, 'on-mix-breakpoint-xsmall'); - expect(onSmall.name, 'on-mix-breakpoint-small'); - expect(onMedium.name, 'on-mix-breakpoint-medium'); - expect(onLarge.name, 'on-mix-breakpoint-large'); - }); - }); - - group('Brightness Utils', () { - testWidgets('onLight context variant', (tester) async { - await tester.pumpWidget(createBrightnessTheme(Brightness.light)); - var context = tester.element(find.byType(Container)); - - expect(onLight.when(context), true, reason: 'light'); - expect(onDark.when(context), false, reason: 'dark'); - }); - - testWidgets('onDark context variant', (widgetTester) async { - await widgetTester.pumpWidget(createBrightnessTheme(Brightness.dark)); - var context = widgetTester.element(find.byType(Container)); - - expect(onLight.when(context), false, reason: 'light'); - expect(onDark.when(context), true, reason: 'dark'); - }); - test('have correct variant names', () { - expect(onLight.name, 'on-light'); - expect(onDark.name, 'on-dark'); - }); - }); - - group('Directionality Utils', () { - testWidgets('onRTL context variant', (tester) async { - await tester.pumpWidget(createDirectionality(TextDirection.rtl)); - var context = tester.element(find.byType(Container)); - - expect(onRTL.when(context), true, reason: 'rtl'); - expect(onLTR.when(context), false, reason: 'ltr'); - }); - - testWidgets('onLTR context variant', (tester) async { - await tester.pumpWidget(createDirectionality(TextDirection.ltr)); - var context = tester.element(find.byType(Container)); - - expect(onRTL.when(context), false, reason: 'rtl'); - expect(onLTR.when(context), true, reason: 'ltr'); - }); - test('have correct variant names', () { - expect(onRTL.name, 'on-rtl'); - expect(onLTR.name, 'on-ltr'); - }); - }); - - group('Orientation Utils', () { - testWidgets('onPortrait context variant', (tester) async { - tester.view.physicalSize = const Size(400, 600); - await tester.pumpWidget(Container()); - var context = tester.element(find.byType(Container)); - - expect(onPortrait.when(context), true, reason: 'portrait'); - expect(onLandscape.when(context), false, reason: 'landscape'); - - addTearDown(tester.view.resetPhysicalSize); - }); - - testWidgets('onLandscape context variant', (tester) async { - tester.view.physicalSize = const Size(600, 400); - await tester.pumpWidget(Container()); - var context = tester.element(find.byType(Container)); - - expect(onPortrait.when(context), false, reason: 'portrait'); - expect(onLandscape.when(context), true, reason: 'landscape'); - addTearDown(tester.view.resetPhysicalSize); - }); - test('have correct variant names', () { - expect(onPortrait.name, 'on-portrait'); - expect(onLandscape.name, 'on-landscape'); - }); - }); group('Not Utils', () { testWidgets('reverts when of context variant', (tester) async { diff --git a/test/src/utils/decorators_util_test.dart b/test/src/utils/decorators_util_test.dart index 87af1fef9..5a83d0bdf 100644 --- a/test/src/utils/decorators_util_test.dart +++ b/test/src/utils/decorators_util_test.dart @@ -1,29 +1,32 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; -import 'package:mix/src/decorators/clip_decorator.dart'; -import 'package:mix/src/utils/decorators_util.dart'; import '../../helpers/testing_utils.dart'; void main() { - group('Decorators', () { + group('Decorators: ', () { test('aspectRatio creates AspectRatioDecorator correctly', () { final aspectRatioDecorator = aspectRatio(2.0); - expect(aspectRatioDecorator.aspectRatio, 2.0); + expect(aspectRatioDecorator.value, 2.0); }); test('expanded creates FlexibleDecorator correctly', () { - final flexibleDecorator = expanded(); + final flexibleDecorator = flexible.expanded(); - expect(flexibleDecorator.flexFit, FlexFit.tight); + expect(flexibleDecorator.fit, FlexFit.tight); }); - test('flexible creates FlexibleDecorator correctly', () { + test('default flexible creates FlexibleDecorator correctly', () { final flexibleDecorator = flexible(); + final widget = + flexibleDecorator.build(const Empty(), EmptyMixData) as Flexible; - expect(flexibleDecorator.flexFit, FlexFit.loose); + expect(flexibleDecorator.fit, null); + expect(widget, isA()); + expect(widget.fit, FlexFit.loose); + expect(widget.flex, 1); }); test('opacity creates OpacityDecorator correctly', () { @@ -39,19 +42,19 @@ void main() { }); test('rotate90 creates RotateDecorator correctly', () { - final rotateDecorator = rotate90(); + final rotateDecorator = rotate.d90; expect(rotateDecorator.value, 1); }); test('rotate180 creates RotateDecorator correctly', () { - final rotateDecorator = rotate180(); + final rotateDecorator = rotate.d180; expect(rotateDecorator.value, 2); }); test('rotate270 creates RotateDecorator correctly', () { - final rotateDecorator = rotate270(); + final rotateDecorator = rotate.d270; expect(rotateDecorator.value, 3); }); @@ -59,11 +62,12 @@ void main() { test('scale creates ScaleDecorator correctly', () { final scaleDecorator = scale(0.5); - expect(scaleDecorator.scale, 0.5); + expect(scaleDecorator.value, 0.5); }); test('clipRRect creates ClipRRectDecorator correctly', () { - final clipRRectDecorator = clipRRect(10.0); + final clipRRectDecorator = + clipRRect(borderRadius: BorderRadius.circular(10.0)); expect(clipRRectDecorator.borderRadius, BorderRadius.circular(10.0)); }); @@ -71,21 +75,21 @@ void main() { test('clipOval creates ClipOvalDecorator correctly', () { final clipOvalDecorator = clipOval(); - expect(clipOvalDecorator.render(const Empty(), EmptyMixData), + expect(clipOvalDecorator.build(const Empty(), EmptyMixData), isA()); }); test('clipPath creates ClipPathDecorator correctly', () { final clipPathDecorator = clipPath(); - expect(clipPathDecorator.render(const Empty(), EmptyMixData), + expect(clipPathDecorator.build(const Empty(), EmptyMixData), isA()); }); test('clipTriangle creates ClipPathDecorator correctly', () { final clipTriangleDecorator = clipTriangle(); - expect(clipTriangleDecorator.render(const Empty(), EmptyMixData), + expect(clipTriangleDecorator.build(const Empty(), EmptyMixData), isA()); expect(clipTriangleDecorator.clipper, isA()); }); diff --git a/test/src/utils/gradient_util_test.dart b/test/src/utils/gradient_util_test.dart deleted file mode 100644 index a765f148e..000000000 --- a/test/src/utils/gradient_util_test.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/mix.dart'; - -void main() { - group('Gradient Creation Tests', () { - test('LinearGradient is created with correct parameters', () { - final colors = [Colors.red, Colors.blue]; - final attr = linearGradient( - colors: colors, - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ); - - final linearGradientValue = attr.value as LinearGradient; - expect(linearGradientValue, isA()); - - expect(linearGradientValue.begin, Alignment.topLeft); - expect(linearGradientValue.end, Alignment.bottomRight); - expect(linearGradientValue.colors, equals(colors)); - }); - - test('LinearGradient uses default values when parameters are not provided', - () { - final colors = [Colors.red, Colors.blue]; - final attr = linearGradient(colors: colors); - - final linearGradientValue = attr.value as LinearGradient; - expect(linearGradientValue, isA()); - expect(linearGradientValue.begin, LinearGradient(colors: colors).begin); - expect(linearGradientValue.end, LinearGradient(colors: colors).end); - }); - - test('RadialGradient is created with correct parameters', () { - final colors = [Colors.red, Colors.blue]; - final attr = - radialGradient(colors: colors, center: Alignment.center, radius: 0.5); - final radialGradientValue = attr.value as RadialGradient; - expect(radialGradientValue, isA()); - - expect(radialGradientValue.center, Alignment.center); - expect(radialGradientValue.radius, 0.5); - expect(radialGradientValue.colors, equals(colors)); - }); - - test('RadialGradient uses default values when parameters are not provided', - () { - final colors = [Colors.red, Colors.blue]; - final attr = radialGradient(colors: colors); - - final radialGradientValue = attr.value as RadialGradient; - expect(radialGradientValue, isA()); - expect(radialGradientValue.center, RadialGradient(colors: colors).center); - expect(radialGradientValue.radius, RadialGradient(colors: colors).radius); - }); - }); -} diff --git a/test/src/utils/space_util_test.dart b/test/src/utils/space_util_test.dart deleted file mode 100644 index 6854b9013..000000000 --- a/test/src/utils/space_util_test.dart +++ /dev/null @@ -1,548 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/mix.dart'; - -void main() { - group('Padding Utils', () { - test('padding()', () { - expect( - padding(10), - const PaddingAttribute(top: 10, bottom: 10, left: 10, right: 10), - ); - expect( - padding(10, 20), - const PaddingAttribute(top: 10, bottom: 10, left: 20, right: 20), - ); - expect( - padding(10, 20, 30), - const PaddingAttribute(top: 10, bottom: 30, left: 20, right: 20), - ); - expect( - padding(10, 20, 30, 40), - const PaddingAttribute(top: 10, bottom: 30, left: 40, right: 20), - ); - }); - - test('paddingDirectional()', () { - expect( - paddingDirectional(10), - const PaddingDirectionalAttribute( - start: 10, - end: 10, - top: 10, - bottom: 10, - ), - reason: '1', - ); - expect( - paddingDirectional(10, 20), - const PaddingDirectionalAttribute( - top: 10, - bottom: 10, - start: 20, - end: 20, - ), - reason: '2', - ); - expect( - paddingDirectional(10, 20, 30), - const PaddingDirectionalAttribute( - top: 10, - bottom: 30, - start: 20, - end: 20, - ), - reason: '3', - ); - expect( - paddingDirectional(10, 20, 30, 40), - const PaddingDirectionalAttribute( - top: 10, - end: 20, - bottom: 30, - start: 40, - ), - reason: '4', - ); - }); - - // paddingFrom - test('paddingFrom', () { - expect( - paddingFrom(const EdgeInsets.all(10)), - const PaddingAttribute(top: 10, bottom: 10, left: 10, right: 10), - reason: '1', - ); - - expect( - paddingFrom(const EdgeInsets.only(top: 10)), - const PaddingAttribute(top: 10, bottom: 0, left: 0, right: 0), - reason: '2', - ); - - expect( - paddingFrom(const EdgeInsets.only(left: 10)), - const PaddingAttribute(left: 10, bottom: 0, top: 0, right: 0), - reason: '3', - ); - - expect( - paddingFrom(const EdgeInsets.only(right: 10)), - const PaddingAttribute(right: 10, bottom: 0, top: 0, left: 0), - reason: '4', - ); - - expect( - paddingFrom(const EdgeInsets.only(bottom: 10)), - const PaddingAttribute(bottom: 10, top: 0, left: 0, right: 0), - reason: '5', - ); - - expect( - paddingFrom(const EdgeInsets.symmetric(horizontal: 10)), - const PaddingAttribute(left: 10, right: 10, top: 0, bottom: 0), - reason: '6', - ); - - expect( - paddingFrom(const EdgeInsets.symmetric(vertical: 10)), - const PaddingAttribute(top: 10, bottom: 10, left: 0, right: 0), - reason: '7', - ); - - expect( - paddingFrom(const EdgeInsetsDirectional.only(start: 10)), - const PaddingDirectionalAttribute(start: 10, end: 0, top: 0, bottom: 0), - reason: '8', - ); - - expect( - paddingFrom(const EdgeInsetsDirectional.only(end: 10)), - const PaddingDirectionalAttribute(end: 10, start: 0, top: 0, bottom: 0), - reason: '9', - ); - - expect( - paddingFrom(const EdgeInsetsDirectional.only(top: 10)), - const PaddingDirectionalAttribute(top: 10, bottom: 0, start: 0, end: 0), - reason: '10', - ); - - expect( - paddingFrom(const EdgeInsetsDirectional.only(bottom: 10)), - const PaddingDirectionalAttribute(bottom: 10, top: 0, start: 0, end: 0), - reason: '11', - ); - - expect( - paddingFrom(const EdgeInsetsDirectional.only(start: 10, end: 20)), - const PaddingDirectionalAttribute( - start: 10, - end: 20, - top: 0, - bottom: 0, - ), - reason: '12', - ); - - expect( - paddingFrom( - const EdgeInsetsDirectional.only(start: 10, end: 20, top: 30)), - const PaddingDirectionalAttribute( - start: 10, - end: 20, - top: 30, - bottom: 0, - ), - reason: '13', - ); - - expect( - paddingFrom( - const EdgeInsetsDirectional.only( - start: 10, - end: 20, - top: 30, - bottom: 40, - ), - ), - const PaddingDirectionalAttribute( - start: 10, - end: 20, - top: 30, - bottom: 40, - ), - reason: '14', - ); - }); - - // paddingDirectionalFrom - - test('paddingOnly', () { - expect( - paddingOnly(top: 10), - const PaddingAttribute(top: 10), - ); - - expect( - paddingOnly(left: 10), - const PaddingAttribute(left: 10), - ); - - expect( - paddingOnly(right: 10), - const PaddingAttribute(right: 10), - ); - - expect( - paddingOnly(bottom: 10), - const PaddingAttribute(bottom: 10), - ); - }); - - test('paddingTop', () { - expect( - paddingTop(10), - const PaddingAttribute(top: 10), - ); - }); - - test('paddingBottom', () { - expect( - paddingBottom(10), - const PaddingAttribute(bottom: 10), - ); - }); - - test('paddingLeft', () { - expect( - paddingLeft(10), - const PaddingAttribute(left: 10), - ); - }); - - test('paddingRight', () { - expect( - paddingRight(10), - const PaddingAttribute(right: 10), - ); - }); - - test('paddingStart', () { - expect( - paddingStart(10), - const PaddingDirectionalAttribute(start: 10), - ); - }); - - test('paddingEnd', () { - expect( - paddingEnd(10), - const PaddingDirectionalAttribute(end: 10), - ); - }); - - test('paddingHorizontal', () { - expect( - paddingHorizontal(10), - const PaddingAttribute(left: 10, right: 10), - ); - }); - - test('paddingVertical', () { - expect( - paddingVertical(10), - const PaddingAttribute(top: 10, bottom: 10), - ); - }); - - test('paddingAll', () { - expect( - paddingAll(10), - const PaddingAttribute(top: 10, bottom: 10, left: 10, right: 10), - ); - }); - - test('paddingDirectionalOnly', () { - expect( - paddingDirectionalOnly(start: 10), - const PaddingDirectionalAttribute(start: 10), - ); - - expect( - paddingDirectionalOnly(end: 10), - const PaddingDirectionalAttribute(end: 10), - ); - - expect( - paddingDirectionalOnly(start: 10, end: 20), - const PaddingDirectionalAttribute(start: 10, end: 20), - ); - - expect( - paddingDirectionalOnly(start: 10, end: 20, top: 30), - const PaddingDirectionalAttribute(start: 10, end: 20, top: 30), - ); - - expect( - paddingDirectionalOnly(start: 10, end: 20, top: 30, bottom: 40), - const PaddingDirectionalAttribute( - start: 10, end: 20, top: 30, bottom: 40), - ); - }); - }); - - group('Margin Utils', () { - test('margin()', () { - expect( - margin(10), - const MarginAttribute(top: 10, bottom: 10, left: 10, right: 10), - ); - expect( - margin(10, 20), - const MarginAttribute(top: 10, bottom: 10, left: 20, right: 20), - ); - expect( - margin(10, 20, 30), - const MarginAttribute(top: 10, bottom: 30, left: 20, right: 20), - ); - expect( - margin(10, 20, 30, 40), - const MarginAttribute(top: 10, bottom: 30, left: 40, right: 20), - ); - }); - - // marginDirectional() - test('marginDirectional()', () { - expect( - marginDirectional(10), - const MarginDirectionalAttribute( - start: 10, - end: 10, - top: 10, - bottom: 10, - ), - reason: '1', - ); - expect( - marginDirectional(10, 20), - const MarginDirectionalAttribute( - top: 10, - bottom: 10, - start: 20, - end: 20, - ), - reason: '2', - ); - expect( - marginDirectional(10, 20, 30), - const MarginDirectionalAttribute( - top: 10, - bottom: 30, - start: 20, - end: 20, - ), - reason: '3', - ); - expect( - marginDirectional(10, 20, 30, 40), - const MarginDirectionalAttribute( - top: 10, - end: 20, - bottom: 30, - start: 40, - ), - reason: '4', - ); - }); - - test('marginOnly', () { - expect( - marginOnly(top: 10), - const MarginAttribute(top: 10), - ); - - expect( - marginOnly(left: 10), - const MarginAttribute(left: 10), - ); - - expect( - marginOnly(right: 10), - const MarginAttribute(right: 10), - ); - - expect( - marginOnly(bottom: 10), - const MarginAttribute(bottom: 10), - ); - }); - - test('marginTop', () { - expect( - marginTop(10), - const MarginAttribute(top: 10), - ); - }); - - test('marginBottom', () { - expect( - marginBottom(10), - const MarginAttribute(bottom: 10), - ); - }); - - test('marginLeft', () { - expect( - marginLeft(10), - const MarginAttribute(left: 10), - ); - }); - - test('marginRight', () { - expect( - marginRight(10), - const MarginAttribute(right: 10), - ); - }); - - test('marginStart', () { - expect( - marginStart(10), - const MarginDirectionalAttribute(start: 10), - ); - }); - - test('marginEnd', () { - expect( - marginEnd(10), - const MarginDirectionalAttribute(end: 10), - ); - }); - - test('marginHorizontal', () { - expect( - marginHorizontal(10), - const MarginAttribute(left: 10, right: 10), - ); - }); - - test('marginVertical', () { - expect( - marginVertical(10), - const MarginAttribute(top: 10, bottom: 10), - ); - }); - - test('marginAll', () { - expect( - marginAll(10), - const MarginAttribute(top: 10, bottom: 10, left: 10, right: 10), - ); - }); - - test('marginFrom', () { - expect( - marginFrom(const EdgeInsets.all(10)), - const MarginAttribute(top: 10, bottom: 10, left: 10, right: 10), - ); - - expect( - marginFrom(const EdgeInsets.only(top: 10)), - const MarginAttribute(top: 10, bottom: 0, left: 0, right: 0), - ); - - expect( - marginFrom(const EdgeInsets.only(left: 10)), - const MarginAttribute(left: 10, bottom: 0, top: 0, right: 0), - ); - - expect( - marginFrom(const EdgeInsets.only(right: 10)), - const MarginAttribute(right: 10, bottom: 0, top: 0, left: 0), - ); - - expect( - marginFrom(const EdgeInsets.only(bottom: 10)), - const MarginAttribute(bottom: 10, top: 0, left: 0, right: 0), - ); - - expect( - marginFrom(const EdgeInsets.symmetric(horizontal: 10)), - const MarginAttribute(left: 10, right: 10, top: 0, bottom: 0), - ); - - expect( - marginFrom(const EdgeInsets.symmetric(vertical: 10)), - const MarginAttribute(top: 10, bottom: 10, left: 0, right: 0), - ); - - expect( - marginFrom(const EdgeInsetsDirectional.only(start: 10)), - const MarginDirectionalAttribute(start: 10, end: 0, top: 0, bottom: 0), - ); - - expect( - marginFrom(const EdgeInsetsDirectional.only(end: 10)), - const MarginDirectionalAttribute(end: 10, start: 0, top: 0, bottom: 0), - ); - - expect( - marginFrom(const EdgeInsetsDirectional.only(top: 10)), - const MarginDirectionalAttribute(top: 10, bottom: 0, start: 0, end: 0), - ); - - expect( - marginFrom(const EdgeInsetsDirectional.only(bottom: 10)), - const MarginDirectionalAttribute(bottom: 10, top: 0, start: 0, end: 0), - ); - - expect( - marginFrom(const EdgeInsetsDirectional.only(start: 10, end: 20)), - const MarginDirectionalAttribute(start: 10, end: 20, top: 0, bottom: 0), - ); - - expect( - marginFrom( - const EdgeInsetsDirectional.only(start: 10, end: 20, top: 30)), - const MarginDirectionalAttribute( - start: 10, end: 20, top: 30, bottom: 0), - ); - - expect( - marginFrom(const EdgeInsetsDirectional.only( - start: 10, end: 20, top: 30, bottom: 40)), - const MarginDirectionalAttribute( - start: 10, end: 20, top: 30, bottom: 40), - ); - }); - - test('marginDirectionalOnly', () { - expect( - marginDirectionalOnly(start: 10), - const MarginDirectionalAttribute(start: 10), - ); - - expect( - marginDirectionalOnly(end: 10), - const MarginDirectionalAttribute(end: 10), - ); - - expect( - marginDirectionalOnly(start: 10, end: 20), - const MarginDirectionalAttribute(start: 10, end: 20), - ); - - expect( - marginDirectionalOnly(start: 10, end: 20, top: 30), - const MarginDirectionalAttribute(start: 10, end: 20, top: 30), - ); - - expect( - marginDirectionalOnly(start: 10, end: 20, top: 30, bottom: 40), - const MarginDirectionalAttribute( - start: 10, end: 20, top: 30, bottom: 40), - ); - }); - }); -} diff --git a/test/src/utils/text_util_test.dart b/test/src/utils/text_util_test.dart deleted file mode 100644 index 5eb5e4a9f..000000000 --- a/test/src/utils/text_util_test.dart +++ /dev/null @@ -1,89 +0,0 @@ -// Import necessary packages -import 'dart:ui'; - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/mix.dart'; - -void main() { - group('TextStyleAttribute', () { - test('textStyle creates TextStyleAttribute correctly', () { - final yellowPaint = Paint()..color = Colors.yellow; - final purplePaint = Paint()..color = Colors.purple; - final textStyleAttribute = textStyle( - fontFamily: 'Roboto', - fontWeight: FontWeight.bold, - fontStyle: FontStyle.italic, - fontSize: 16.0, - letterSpacing: 1.0, - wordSpacing: 2.0, - textBaseline: TextBaseline.ideographic, - shadows: [ - const Shadow( - blurRadius: 1.0, - color: Colors.black, - offset: Offset(1.0, 1.0), - ), - ], - color: Colors.red, - backgroundColor: Colors.blue, - fontFeatures: [const FontFeature.alternative(4)], - decoration: TextDecoration.underline, - decorationColor: Colors.green, - decorationStyle: TextDecorationStyle.dashed, - foreground: yellowPaint, - background: purplePaint, - debugLabel: 'debugLabel', - locale: const Locale('en', 'US'), - height: 2.0, - ); - - expect(textStyleAttribute.fontWeight, FontWeight.bold); - expect(textStyleAttribute.fontStyle, FontStyle.italic); - expect(textStyleAttribute.fontSize, 16.0); - expect(textStyleAttribute.letterSpacing, 1.0); - expect(textStyleAttribute.wordSpacing, 2.0); - expect(textStyleAttribute.textBaseline, TextBaseline.ideographic); - expect(textStyleAttribute.shadows?.length, 1); - expect(textStyleAttribute.shadows?.first.blurRadius, 1.0); - expect(textStyleAttribute.shadows?.first.color?.value, Colors.black); - expect(textStyleAttribute.shadows?.first.offset, const Offset(1.0, 1.0)); - expect(textStyleAttribute.color?.value, Colors.red); - expect(textStyleAttribute.backgroundColor?.value, Colors.blue); - expect(textStyleAttribute.fontFeatures?.length, 1); - expect( - textStyleAttribute.fontFeatures?.first, - const FontFeature.alternative(4), - ); - expect(textStyleAttribute.decoration, TextDecoration.underline); - expect(textStyleAttribute.decorationColor?.value, Colors.green); - expect(textStyleAttribute.decorationStyle, TextDecorationStyle.dashed); - expect(textStyleAttribute.foreground, yellowPaint); - expect(textStyleAttribute.background, purplePaint); - expect(textStyleAttribute.debugLabel, 'debugLabel'); - expect(textStyleAttribute.locale, const Locale('en', 'US')); - expect(textStyleAttribute.height, 2.0); - }); - - test('bold returns TextStyleAttribute with FontWeight.bold', () { - final textStyleAttribute = bold(); - - expect(textStyleAttribute.fontWeight, FontWeight.bold); - }); - - test('italic returns TextStyleAttribute with FontStyle.italic', () { - final textStyleAttribute = italic(); - - expect(textStyleAttribute.fontStyle, FontStyle.italic); - }); - - // textDirective - test('textDirective returns TextStyleAttribute with TextDirective', () { - final textStyleAttribute = textDirective(const UppercaseDirective()); - - expect(textStyleAttribute.value, [const UppercaseDirective()]); - expect(textStyleAttribute.value.first, const UppercaseDirective()); - expect(textStyleAttribute.value.first.runtimeType, UppercaseDirective); - }); - }); -} diff --git a/test/src/variants/multi_variant_test.dart b/test/src/variants/multi_variant_test.dart index 3484aa01f..de5f5d54b 100644 --- a/test/src/variants/multi_variant_test.dart +++ b/test/src/variants/multi_variant_test.dart @@ -23,18 +23,24 @@ void main() { const variant1 = Variant('variant1'); const variant2 = Variant('variant2'); const variant3 = Variant('variant3'); - final multiVariant = MultiVariant.and(const [variant1, variant2]); + final multiAndVariant = MultiVariant.and(const [variant1, variant2]); + + final multiOrVariant = MultiVariant.or(const [variant1, variant2]); - expect(multiVariant.matches([variant1, variant2, variant3]), isTrue); - expect(multiVariant.matches([variant1]), isFalse); + expect(multiAndVariant.matches([variant1, variant2, variant3]), isTrue); + expect(multiAndVariant.matches([variant1]), isFalse); + expect(multiOrVariant.matches([variant1, variant2, variant3]), isTrue); + expect(multiOrVariant.matches([variant1]), isTrue); }); test('when should correctly match context variants', () { final variant1 = ContextVariant('variant1', when: (context) => true); final variant2 = ContextVariant('variant2', when: (context) => false); - final multiVariant = MultiVariant.and([variant1, variant2]); + final multiAndVariant = MultiVariant.and([variant1, variant2]); + final multiOrVariant = MultiVariant.or([variant1, variant2]); - expect(multiVariant.when(MockBuildContext()), isFalse); + expect(multiAndVariant.when(MockBuildContext()), isFalse); + expect(multiOrVariant.when(MockBuildContext()), isTrue); }); test('MultiVariant.and should correctly create a MultiVariant', () { @@ -43,7 +49,7 @@ void main() { final multiVariant = MultiVariant.and(const [variant1, variant2]); expect(multiVariant.variants, containsAll([variant1, variant2])); - expect(multiVariant.type, MultiVariantType.and); + expect(multiVariant.operatorType, MultiVariantOperator.and); }); test('MultiVariant.or should correctly create a MultiVariant', () { @@ -52,7 +58,7 @@ void main() { final multiVariant = MultiVariant.or(const [variant1, variant2]); expect(multiVariant.variants, containsAll([variant1, variant2])); - expect(multiVariant.type, MultiVariantType.or); + expect(multiVariant.operatorType, MultiVariantOperator.or); }); }); } diff --git a/test/src/widgets/container_widget_test.dart b/test/src/widgets/container_widget_test.dart deleted file mode 100644 index a79ac7211..000000000 --- a/test/src/widgets/container_widget_test.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/mix.dart'; - -import '../../helpers/testing_utils.dart'; - -void main() { - testWidgets('StyledContainer', (WidgetTester tester) async { - final paddingAttr = padding(10); - final marginAttr = margin(10); - final backgroundColorAttr = backgroundColor(Colors.red); - final alignmentAttr = alignmentCenter(); - final clipAttr = clip(Clip.hardEdge); - - final boxDecorationAttr = boxDecoration( - border: border(color: Colors.red, width: 1, style: BorderStyle.solid), - borderRadius: borderRadius( - const Radius.circular(10), - ), - color: const ColorAttribute(Colors.red), - ); - - await tester.pumpStyledWidget( - StyledContainer( - style: StyleMix( - paddingAttr, - marginAttr, - backgroundColorAttr, - alignmentAttr, - clipAttr, - boxDecorationAttr, - ), - ), - ); - - final containerFinder = find.byType(Container); - Container containerWidget = tester.widget(containerFinder); - - final containerDecoration = containerWidget.decoration as BoxDecoration; - - final resolvedDecoration = boxDecorationAttr.resolve(EmptyMixData); - - expect(containerFinder, findsOneWidget); - expect(containerWidget.margin, marginAttr.resolve(EmptyMixData)); - expect(containerWidget.padding, paddingAttr.resolve(EmptyMixData)); - expect(containerWidget.clipBehavior, clipAttr.resolve(EmptyMixData)); - expect(containerWidget.alignment, alignmentAttr.resolve(EmptyMixData)); - expect(containerWidget.clipBehavior, clipAttr.resolve(EmptyMixData)); - expect(containerDecoration.border, resolvedDecoration.border); - expect(containerDecoration.color, resolvedDecoration.color); - expect(containerDecoration.borderRadius, resolvedDecoration.borderRadius); - }); -} diff --git a/test/src/widgets/gap_widget_test.dart b/test/src/widgets/gap_widget_test.dart new file mode 100644 index 000000000..c63b8a616 --- /dev/null +++ b/test/src/widgets/gap_widget_test.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/src/widgets/gap_widget.dart'; + +void main() { + group('Gap widget', () { + testWidgets( + 'Gap maintains fixed width when enough space is available in a Row', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Row( + children: [ + Container(width: 100, height: 100, color: Colors.red), + const Gap(50), + Container(width: 100, height: 100, color: Colors.blue), + ], + ), + ), + ), + ); + + final Finder gapFinder = find.byType(Gap); + expect(gapFinder, findsOneWidget); + + final RenderBox gapRenderBox = tester.renderObject(gapFinder); + expect(gapRenderBox.size.width, equals(50)); + }); + + testWidgets( + 'Gap maintains fixed height when enough space is available in a Column', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Column( + children: [ + Container(width: 100, height: 100, color: Colors.red), + const Gap(50), + Container(width: 100, height: 100, color: Colors.blue), + ], + ), + ), + ), + ); + + final Finder gapFinder = find.byType(Gap); + expect(gapFinder, findsOneWidget); + + final RenderBox gapRenderBox = tester.renderObject(gapFinder); + expect(gapRenderBox.size.height, equals(50)); + }); + }); + + testWidgets( + 'spaceBetween layout includes Gap in space distribution', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container(width: 50, height: 50, color: Colors.red), + const Gap(20), + Container(width: 50, height: 50, color: Colors.blue), + ], + ), + ), + ), + ); + + final Finder firstContainerFinder = find.byType(Container).first; + final Finder gapFinder = find.byType(Gap); + final Finder lastContainerFinder = find.byType(Container).last; + + final RenderBox firstContainerBox = + tester.renderObject(firstContainerFinder); + final RenderBox gapBox = tester.renderObject(gapFinder); + final RenderBox lastContainerBox = + tester.renderObject(lastContainerFinder); + + // Check if the Gap is positioned between the two containers + expect(gapBox.localToGlobal(Offset.zero).dx, + greaterThan(firstContainerBox.localToGlobal(Offset.zero).dx + 50)); + expect(gapBox.localToGlobal(Offset.zero).dx, + lessThan(lastContainerBox.localToGlobal(Offset.zero).dx)); + }, + ); + + testWidgets( + 'Gap widget adjusts size in spaceBetween layout', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container(width: 100, height: 50, color: Colors.red), + const Gap(20), + Container(width: 100, height: 50, color: Colors.blue), + ], + ), + ), + ), + ); + + await tester.pumpAndSettle(); + + final Finder gapFinder = find.byType(Gap); + expect(gapFinder, findsOneWidget); + + final RenderBox gapRenderBox = tester.renderObject(gapFinder); + // Expect the Gap to adjust its size based on available space + // The exact size will depend on the screen size and container widths + expect(gapRenderBox.size.width, lessThanOrEqualTo(20)); + // You may also assert that the gap width is greater than 0 if there's enough space + }, + ); +} diff --git a/test/src/widgets/stack_widget_test.dart b/test/src/widgets/stack_widget_test.dart deleted file mode 100644 index 4b7f7f7cc..000000000 --- a/test/src/widgets/stack_widget_test.dart +++ /dev/null @@ -1,125 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/mix.dart'; - -import '../../helpers/testing_utils.dart'; - -// import '../specs/stack_spec.dart'; -// import 'container_widget.dart'; -// import 'styled_widget.dart'; - -// class StyledStack extends StyledWidget { -// const StyledStack({ -// this.children = const [], -// super.inherit, -// super.key, -// super.style, -// }); - -// final List children; - -// @override -// Widget build(BuildContext context) { -// return buildWithStyle(context, (data) { -// final spec = StackSpec.resolve(data); - -// const fallback = Stack(); - -// return Stack( -// alignment: spec.alignment ?? fallback.alignment, -// fit: spec.fit ?? fallback.fit, -// clipBehavior: spec.clipBehavior ?? fallback.clipBehavior, -// children: children, -// ); -// }); -// } -// } - -// class ZBox extends StyledWidget { -// const ZBox({ -// this.children = const [], -// super.inherit, -// super.key, -// super.style, -// }); - -// final List children; - -// @override -// Widget build(BuildContext context) { -// return buildWithStyle(context, (data) { -// return StyledContainer( -// inherit: true, -// child: StyledStack(inherit: true, children: children), -// ); -// }); -// } -// } - -void main() { - testWidgets('Stack', (tester) async { - final style = StyleMix( - stackFit(StackFit.expand), - alignmentTopCenter(), - clip(Clip.antiAlias), - textDirection(TextDirection.ltr), - ); - await tester.pumpMaterialApp( - StyledStack( - style: style, - children: [ - Container( - height: 100, - width: 100, - color: const Color(0xFF000000), - ), - Container( - height: 50, - width: 50, - color: const Color(0xFF0000FF), - ), - ], - ), - ); - - final stack = tester.widget(find.byType(Stack)); - - expect(find.byType(Stack), findsOneWidget); - expect(find.byType(Container), findsNWidgets(2)); - expect(stack.alignment, Alignment.topCenter); - expect(stack.fit, StackFit.expand); - expect(stack.clipBehavior, Clip.antiAlias); - expect(stack.textDirection, TextDirection.ltr); - }); - - testWidgets('ZBox', (tester) async { - final style = StyleMix( - stackFit(StackFit.expand), - alignmentTopCenter(), - clip(Clip.antiAlias), - textDirection(TextDirection.ltr), - backgroundColor(Colors.red), - ); - - await tester.pumpMaterialApp( - ZBox( - style: style, - children: const [], - ), - ); - - final stack = tester.widget(find.byType(Stack)); - final container = tester.widget(find.byType(Container)); - - expect(find.byType(Stack), findsOneWidget); - - expect(find.byType(StyledContainer), findsOneWidget); - - expect((container.decoration as BoxDecoration).color, Colors.red); - - expect(stack.alignment, Alignment.topCenter); - expect(stack.fit, StackFit.expand); - expect(stack.clipBehavior, Clip.antiAlias); - expect(stack.textDirection, TextDirection.ltr); - }); -} diff --git a/tools/update_exports.dart b/tools/update_exports.dart index 41eaf8e65..87f6537dc 100755 --- a/tools/update_exports.dart +++ b/tools/update_exports.dart @@ -7,8 +7,6 @@ import 'package:path/path.dart' as p; void main() { final libDirectory = Directory('lib'); final exportFilePath = p.join('lib', 'exports.dart'); - final coreDirectoryPath = - p.join('lib', 'src', 'core'); // Directory to exclude if (!libDirectory.existsSync()) { print('The lib directory was not found.'); @@ -27,11 +25,20 @@ void main() { for (final entity in libDirectory.listSync(recursive: true)) { // Get the relative path using the path package final relativePath = p.relative(entity.path, from: libDirectory.path); - // Exclude files from the core directory - if (!relativePath.startsWith(p.join('src', 'core')) && - relativePath.endsWith('.dart')) { - newExports.add('export \'$relativePath\';'); + + if (relativePath.startsWith(p.join('src', 'helpers'))) { + continue; + } + + if (!relativePath.endsWith('.dart')) { + continue; } + + if (relativePath.startsWith('mix.dart')) { + continue; + } + + newExports.add('export \'$relativePath\';'); } exportFile.writeAsStringSync(newExports.join('\n')); diff --git a/website/pages/docs/concepts/mixable-widgets.mdx b/website/pages/docs/concepts/mixable-widgets.mdx index 18f556e12..c50a7f8b6 100644 --- a/website/pages/docs/concepts/mixable-widgets.mdx +++ b/website/pages/docs/concepts/mixable-widgets.mdx @@ -30,7 +30,7 @@ Container(height: 100); You can think of `Utilities` as shortcuts to defining visual properties. Thus: ```dart -Box(style: StyleMix(rounded(100))); +Box(style: StyleMix(borderRadius(100))); ``` will become the following: @@ -56,10 +56,10 @@ Box( style: StyleMix( height(100), width(300), - rounded(15), + borderRadius(15), backgroundColor(Colors.amber), alignment(Alignment.center), - elevation(4), // can be 0, 1, 2, 3, 4, 6, 8, 9, 12, 16, 24 + elevation(4) ), ), ``` @@ -138,7 +138,7 @@ Pressable( // when pressing, bg color is a darker grey backgroundColor(Colors.grey.shade600), ), - rounded(8), + borderRadius(8), padding(10), alignment(Alignment.center), ), diff --git a/website/pages/docs/concepts/mixing.mdx b/website/pages/docs/concepts/mixing.mdx index d0c147a03..968a05f9b 100644 --- a/website/pages/docs/concepts/mixing.mdx +++ b/website/pages/docs/concepts/mixing.mdx @@ -3,7 +3,6 @@ id: mixing title: "Mixing" --- -import Code from "../../../components/Code.js"; import { Callout } from "nextra-theme-docs"; # Mixing @@ -32,12 +31,12 @@ final mix = StyleMix( ``` -### `Mix.fromAttributes()` +### `StyleMix.create()` Allows you to create a Mix from a List of attributes. This has the same behavior but provides the ability to receive a List instead of positional arguments. ```dart -final mix = Mix.fromAttributes([ +final mix = StyleMix.create([ height(100), width(100), backgroundColor(Colors.purple), @@ -48,7 +47,7 @@ final mix = Mix.fromAttributes([ It also provides the possibility to use optional attributes using `if/else`: ```dart -final mix = Mix.fromAttributes([ +final mix = StyleMix.fromAttributes([ if (hasError) backgroundColor(Colors.red) else @@ -69,7 +68,7 @@ Mixes are immutable. The new Mix created will inherit all attributes of the orig If you have an existing Mix you can add attributes to it by calling the `mix` method. This will return a new Mix with the new attributes added. ```dart -final newMix = mix.apply( +final newMix = StyleMix.apply( height(200), width(200), backgroundColor(Colors.red), @@ -81,7 +80,7 @@ final newMix = mix.apply( Similar to `apply` but allows you to pass a List of attributes. ```dart -final newMix = mix.addAttributes([ +final newMix = StyleMix.addAttributes([ height(200), width(200), backgroundColor(Colors.red), @@ -111,17 +110,17 @@ final thirdMix = firstMix.applyNullable(secondMix); final thirdMix = firstMix.merge(secondMix); ``` -#### Mix.combine() +#### StyleMix.combine() Creates a new Mix from a positional arguments of Mixes. ```dart -final thirdMix = Mix.combine(firstMix, secondMix); +final thirdMix = StyleMix.combine(firstMix, secondMix); ``` ## Utility Helpers -### Mix.chooser() +### StyleMix.chooser() Uses a _Mix_ based on a condition @@ -129,7 +128,7 @@ Uses a _Mix_ based on a condition final errorMix = StyleMix(backgroundColor(Colors.red)); final successMix = StyleMix(backgroundColor(Colors.green)); -final mix = Mix.chooser( +final mix = StyleMix.chooser( condition: hasError, ifTrue: errorMix, ifFalse: succesMix, diff --git a/website/pages/docs/concepts/variants.mdx b/website/pages/docs/concepts/variants.mdx index a7c4c9452..b13d42627 100644 --- a/website/pages/docs/concepts/variants.mdx +++ b/website/pages/docs/concepts/variants.mdx @@ -7,18 +7,19 @@ import Code from "../../../components/Code.js"; # Variants -While building your design system you will find have the need to create certain variations -of a Widget. This makes the design system more flexible and reusable, by leveraging shared -visual properties between them. +## Overview -In the following example `hover()` is a `Variant` that will be applied when a `Pressable` -triggers the hover state. The `backgroundColor` and `textColor` properties will be overridden with the new value when the variant applies +Variants in Mix are a foundational concept for creating dynamic and modular style definitions. They enable developers to encapsulate and manage groups of styling attributes, which can be applied to various aspects of a Flutter application's UI. + +## The Role of Variants + +Variants allow for the efficient organization of style attributes, facilitating the easy application and switching between different style sets. This approach provides a streamlined method for handling diverse styling requirements across different components, contributing to a more maintainable and consistent design system within your applications. By using Variants, developers can craft adaptable and cohesive UI components, enhancing both the functionality and aesthetic of their Flutter projects. ```dart highlight="10-13" final style = StyleMix( backgroundColor(MaterialTokens.colorScheme.primary), textStyle(color: $onPrimary), - hover( + onHover( backgroundColor(MaterialTokens.colorScheme.primary), textStyle(color: $onPrimary), ), @@ -38,11 +39,11 @@ Widgets. To use a variant, call it on a _Mix_ ```dart -final mix = StyleMix( - dark( +final style = StyleMix( + onDark( textStyle(color: Colors.white), ), - light( + onLight( textStyle(color: Colors.black), ), ); @@ -55,9 +56,9 @@ The operators `|` and `&` can be used to add conditions to your mix: - `|`: whether one of the variants apply ```dart highlight="3-7" -final mix = StyleMix( - padding(20.0), - (small | medium)( // Whether it's small OR medium +final style = StyleMix( + padding(20), + (onSmall | onMedium)( // Whether it's small OR medium width(300), height(400), backgroundColor(Colors.white), @@ -70,55 +71,24 @@ final mix = StyleMix( ```dart highlight="3-6" final mix = StyleMix( padding(20.0), - (hover & press)( // When it's hovering AND pressing + (onHover & onPress)( // When it's hovering AND pressing textStyle(color: Colors.black), bold(), ), ); ``` -The `not` operator can also be called to execute the variant content only when the given +The `onNot` operator can also be called to execute the variant content only when the given variant is not called. For example: ```dart highlight="2-4" final mix = StyleMix( - (not(disabled))( + onNot(disabled)( scale(1.2), ), ); ``` -### Conditional variants - -The `when` variant lets you concisely evaluate expressions within a mix or a within variant list. - -```dart highlight="5-7" -bool hasError = true; - -final mix = StyleMix( - backgroundColor(Colors.green), - when(hasError)( - backgroundColor(Colors.red), - ), -); -``` - -If the `hasError` condition is true, the attributes inside the statement are applied and, if necessary, will override previous attributes. -To ensure specific control over the flow, you can use the else statement by adding another statement right after it: - -```dart highlight="4-8" -bool hasError = true; - -final mix = StyleMix( - when(hasError)( - backgroundColor(Colors.red), - )( - // will be executed if `hasError` is false - backgroundColor(Colors.green), - ), -); -``` - ## Variants catalog Mix already has some `Reactive Variants` defined which can be used to create responsive diff --git a/website/pages/docs/introduction/getting-started.mdx b/website/pages/docs/introduction/getting-started.mdx index 515f3a67b..6bc7592da 100644 --- a/website/pages/docs/introduction/getting-started.mdx +++ b/website/pages/docs/introduction/getting-started.mdx @@ -22,11 +22,11 @@ class CustomMixWidget extends StatelessWidget { animated(), marginY(10), elevation(10), - rounded(10), + borderRadius(10), backgroundColor(MaterialTokens.colorScheme.primary), textStyle($button), textStyle(color: $onPrimary), - hover( + onHover( elevation(2), padding(20), backgroundColor($secondary),