From 8a9e4b0567353560f994a5a747963f102d7def8b Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Fri, 5 Jul 2024 16:17:52 -0300 Subject: [PATCH 01/27] chore: bump flutter version to 3.19.0 --- .fvmrc | 4 +- .vscode/settings.json | 2 +- .../Flutter/GeneratedPluginRegistrant.swift | 12 ++++++ .../ephemeral/Flutter-Generated.xcconfig | 11 +++++ .../ephemeral/flutter_export_environment.sh | 12 ++++++ examples/themed_button/macos/Podfile | 43 +++++++++++++++++++ examples/themed_button/pubspec.yaml | 4 +- examples/todo_list/pubspec.yaml | 4 +- melos.yaml | 4 +- packages/mix/pubspec.yaml | 4 +- packages/mix_annotations/pubspec.yaml | 2 +- packages/mix_generator/pubspec.yaml | 4 +- packages/mix_lint/pubspec.yaml | 2 +- packages/mix_lint_test/pubspec.yaml | 4 +- pubspec.yaml | 2 +- 15 files changed, 96 insertions(+), 18 deletions(-) create mode 100644 examples/themed_button/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 examples/themed_button/macos/Flutter/ephemeral/Flutter-Generated.xcconfig create mode 100755 examples/themed_button/macos/Flutter/ephemeral/flutter_export_environment.sh create mode 100644 examples/themed_button/macos/Podfile diff --git a/.fvmrc b/.fvmrc index a09a8c2f3..c9e0c1af0 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,7 +1,7 @@ { - "flutter": "3.10.6", + "flutter": "3.19.0", "flavors": { "prod": "stable", - "mincompat": "3.10.6" + "mincompat": "3.19.0" } } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 0565bdbbf..4feffae5f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,7 +4,7 @@ "source.fixAll": "explicit", "source.dcm.fixAll": "explicit" }, - "dart.flutterSdkPath": ".fvm/versions/3.10.6", + "dart.flutterSdkPath": ".fvm/versions/3.19.0", "search.exclude": { "**/.fvm/versions": true }, diff --git a/examples/themed_button/macos/Flutter/GeneratedPluginRegistrant.swift b/examples/themed_button/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 000000000..e777c67df --- /dev/null +++ b/examples/themed_button/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,12 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import path_provider_foundation + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) +} diff --git a/examples/themed_button/macos/Flutter/ephemeral/Flutter-Generated.xcconfig b/examples/themed_button/macos/Flutter/ephemeral/Flutter-Generated.xcconfig new file mode 100644 index 000000000..ee4dab857 --- /dev/null +++ b/examples/themed_button/macos/Flutter/ephemeral/Flutter-Generated.xcconfig @@ -0,0 +1,11 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=/Users/lucasoliveira/fvm/versions/3.19.0 +FLUTTER_APPLICATION_PATH=/Users/lucasoliveira/Developer/Concepta/open_source/mix_ecosystem/mix/examples/themed_button +COCOAPODS_PARALLEL_CODE_SIGN=true +FLUTTER_BUILD_DIR=build +FLUTTER_BUILD_NAME=1.0.0 +FLUTTER_BUILD_NUMBER=1 +DART_OBFUSCATION=false +TRACK_WIDGET_CREATION=true +TREE_SHAKE_ICONS=false +PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/examples/themed_button/macos/Flutter/ephemeral/flutter_export_environment.sh b/examples/themed_button/macos/Flutter/ephemeral/flutter_export_environment.sh new file mode 100755 index 000000000..4ed367c72 --- /dev/null +++ b/examples/themed_button/macos/Flutter/ephemeral/flutter_export_environment.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=/Users/lucasoliveira/fvm/versions/3.19.0" +export "FLUTTER_APPLICATION_PATH=/Users/lucasoliveira/Developer/Concepta/open_source/mix_ecosystem/mix/examples/themed_button" +export "COCOAPODS_PARALLEL_CODE_SIGN=true" +export "FLUTTER_BUILD_DIR=build" +export "FLUTTER_BUILD_NAME=1.0.0" +export "FLUTTER_BUILD_NUMBER=1" +export "DART_OBFUSCATION=false" +export "TRACK_WIDGET_CREATION=true" +export "TREE_SHAKE_ICONS=false" +export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/examples/themed_button/macos/Podfile b/examples/themed_button/macos/Podfile new file mode 100644 index 000000000..c795730db --- /dev/null +++ b/examples/themed_button/macos/Podfile @@ -0,0 +1,43 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/examples/themed_button/pubspec.yaml b/examples/themed_button/pubspec.yaml index 22c5bd6e3..4a0b5e3c2 100644 --- a/examples/themed_button/pubspec.yaml +++ b/examples/themed_button/pubspec.yaml @@ -6,8 +6,8 @@ publish_to: "none" version: 1.0.0+1 environment: - sdk: ">=3.0.6 <4.0.0" - flutter: ">=3.10.6" + sdk: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" dependencies: flutter: diff --git a/examples/todo_list/pubspec.yaml b/examples/todo_list/pubspec.yaml index 15ecfe7b1..3030db5d1 100644 --- a/examples/todo_list/pubspec.yaml +++ b/examples/todo_list/pubspec.yaml @@ -6,8 +6,8 @@ publish_to: "none" version: 1.0.0+1 environment: - sdk: ">=3.0.6 <4.0.0" - flutter: ">=3.10.6" + sdk: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" dependencies: flutter: diff --git a/melos.yaml b/melos.yaml index ae81efd48..877a6c11d 100644 --- a/melos.yaml +++ b/melos.yaml @@ -18,8 +18,8 @@ categories: command: bootstrap: environment: - sdk: ">=3.0.6 <4.0.0" - flutter: ">=3.10.6" + sdk: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" publish: hooks: pre: melos run brb diff --git a/packages/mix/pubspec.yaml b/packages/mix/pubspec.yaml index bba7aca37..2b39aa835 100644 --- a/packages/mix/pubspec.yaml +++ b/packages/mix/pubspec.yaml @@ -4,8 +4,8 @@ version: 1.2.0 homepage: https://github.com/leoafarias/mix environment: - sdk: ">=3.0.6 <4.0.0" - flutter: ">=3.10.6" + sdk: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" dependencies: mix_annotations: ^0.2.0 diff --git a/packages/mix_annotations/pubspec.yaml b/packages/mix_annotations/pubspec.yaml index b5f20b315..f6c5950ca 100644 --- a/packages/mix_annotations/pubspec.yaml +++ b/packages/mix_annotations/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.2.0 repository: https://github.com/conceptadev/mix environment: - sdk: ">=3.0.6 <4.0.0" + sdk: ">=3.3.0 <4.0.0" dev_dependencies: lints: ^3.0.0 diff --git a/packages/mix_generator/pubspec.yaml b/packages/mix_generator/pubspec.yaml index 8c25e510c..ac054b48f 100644 --- a/packages/mix_generator/pubspec.yaml +++ b/packages/mix_generator/pubspec.yaml @@ -4,8 +4,8 @@ version: 0.2.0 homepage: https://github.com/conceptadev/mix environment: - sdk: ">=3.0.6 <4.0.0" - flutter: ">=3.10.6" + sdk: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" dependencies: mix_annotations: ^0.2.0 diff --git a/packages/mix_lint/pubspec.yaml b/packages/mix_lint/pubspec.yaml index e9f1532e3..64e3b41b2 100644 --- a/packages/mix_lint/pubspec.yaml +++ b/packages/mix_lint/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.1.0 repository: https://github.com/conceptadev/mix environment: - sdk: ">=3.0.6 <4.0.0" + sdk: ">=3.3.0 <4.0.0" dependencies: analyzer: ^6.0.0 diff --git a/packages/mix_lint_test/pubspec.yaml b/packages/mix_lint_test/pubspec.yaml index 1a6957cf3..f69f112a5 100644 --- a/packages/mix_lint_test/pubspec.yaml +++ b/packages/mix_lint_test/pubspec.yaml @@ -3,8 +3,8 @@ description: A sample command-line application. publish_to: "none" environment: - sdk: ">=3.0.6 <4.0.0" - flutter: ">=3.10.6" + sdk: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" dependencies: flutter: diff --git a/pubspec.yaml b/pubspec.yaml index ac25b8a2c..acd1eed53 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: mix environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.3.0 <4.0.0" dev_dependencies: melos: ^6.0.0 From d9aef57b7bf8bcf8afeb6cb19d855ca96222344e Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 8 Jul 2024 11:43:58 -0300 Subject: [PATCH 02/27] fix deprecates --- .../src/attributes/scalars/scalar_util.dart | 1 - .../attributes/text_style/text_style_dto.dart | 1 - .../attributes/text_style/text_style_util.dart | 1 - packages/mix/lib/src/core/utility.dart | 10 ++++++++++ packages/mix/lib/src/specs/text/text_spec.dart | 5 ++++- .../mix/lib/src/specs/text/text_spec.g.dart | 18 +++++++++++++++++- .../mix/lib/src/specs/text/text_widget.dart | 7 ++++++- .../lib/src/theme/tokens/text_style_token.dart | 1 - .../widgets/widget_state/pressable_widget.dart | 2 +- .../text_style/text_style_util_test.dart | 1 - .../src/specs/text/text_attribute_test.dart | 2 +- .../test/src/specs/text/text_spec_test.dart | 12 ++++++------ .../test/src/specs/text/text_widget_test.dart | 4 ++-- 13 files changed, 47 insertions(+), 18 deletions(-) diff --git a/packages/mix/lib/src/attributes/scalars/scalar_util.dart b/packages/mix/lib/src/attributes/scalars/scalar_util.dart index f880abe4e..0ab4a2e5e 100644 --- a/packages/mix/lib/src/attributes/scalars/scalar_util.dart +++ b/packages/mix/lib/src/attributes/scalars/scalar_util.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'dart:typed_data'; -import 'dart:ui'; import 'package:flutter/widgets.dart'; import 'package:mix/mix.dart'; diff --git a/packages/mix/lib/src/attributes/text_style/text_style_dto.dart b/packages/mix/lib/src/attributes/text_style/text_style_dto.dart index 3fcdef084..138f83bac 100644 --- a/packages/mix/lib/src/attributes/text_style/text_style_dto.dart +++ b/packages/mix/lib/src/attributes/text_style/text_style_dto.dart @@ -1,6 +1,5 @@ // ignore_for_file: prefer_relative_imports,avoid-importing-entrypoint-exports -import 'dart:ui'; import 'package:flutter/widgets.dart'; import 'package:mix/mix.dart'; diff --git a/packages/mix/lib/src/attributes/text_style/text_style_util.dart b/packages/mix/lib/src/attributes/text_style/text_style_util.dart index 2b2a6b095..fb96d5283 100644 --- a/packages/mix/lib/src/attributes/text_style/text_style_util.dart +++ b/packages/mix/lib/src/attributes/text_style/text_style_util.dart @@ -1,4 +1,3 @@ -import 'dart:ui'; import 'package:flutter/widgets.dart'; diff --git a/packages/mix/lib/src/core/utility.dart b/packages/mix/lib/src/core/utility.dart index b7e70262c..468690c47 100644 --- a/packages/mix/lib/src/core/utility.dart +++ b/packages/mix/lib/src/core/utility.dart @@ -80,3 +80,13 @@ abstract base class SizingUtility extends ScalarUtility { SizingUtility(super.builder); } + +// create a Utility class for TextScaler +final class TextScalerUtility + extends ScalarUtility { + const TextScalerUtility(super.builder); + + T noScaling() => builder(TextScaler.noScaling); + T linear(double textScaleFactor) => + builder(TextScaler.linear(textScaleFactor)); +} diff --git a/packages/mix/lib/src/specs/text/text_spec.dart b/packages/mix/lib/src/specs/text/text_spec.dart index a1e0f9a67..b1b90beae 100644 --- a/packages/mix/lib/src/specs/text/text_spec.dart +++ b/packages/mix/lib/src/specs/text/text_spec.dart @@ -10,10 +10,12 @@ final class TextSpec extends Spec with _$TextSpec { final TextOverflow? overflow; final StrutStyle? strutStyle; final TextAlign? textAlign; - final double? textScaleFactor; final int? maxLines; final TextWidthBasis? textWidthBasis; final TextHeightBehavior? textHeightBehavior; + @Deprecated('Use textScaler instead') + final double? textScaleFactor; + final TextScaler? textScaler; final TextStyle? style; final TextDirection? textDirection; final bool? softWrap; @@ -42,6 +44,7 @@ final class TextSpec extends Spec with _$TextSpec { this.strutStyle, this.textAlign, this.textScaleFactor, + this.textScaler, this.maxLines, this.style, this.textWidthBasis, diff --git a/packages/mix/lib/src/specs/text/text_spec.g.dart b/packages/mix/lib/src/specs/text/text_spec.g.dart index 4c0c25b3e..9ae00f8ee 100644 --- a/packages/mix/lib/src/specs/text/text_spec.g.dart +++ b/packages/mix/lib/src/specs/text/text_spec.g.dart @@ -1,5 +1,7 @@ // GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: deprecated_member_use_from_same_package + part of 'text_spec.dart'; // ************************************************************************** @@ -37,6 +39,7 @@ base mixin _$TextSpec on Spec { StrutStyle? strutStyle, TextAlign? textAlign, double? textScaleFactor, + TextScaler? textScaler, int? maxLines, TextStyle? style, TextWidthBasis? textWidthBasis, @@ -51,6 +54,7 @@ base mixin _$TextSpec on Spec { strutStyle: strutStyle ?? _$this.strutStyle, textAlign: textAlign ?? _$this.textAlign, textScaleFactor: textScaleFactor ?? _$this.textScaleFactor, + textScaler: textScaler ?? _$this.textScaler, maxLines: maxLines ?? _$this.maxLines, style: style ?? _$this.style, textWidthBasis: textWidthBasis ?? _$this.textWidthBasis, @@ -77,7 +81,7 @@ base mixin _$TextSpec on Spec { /// - [MixHelpers.lerpDouble] for [textScaleFactor]. /// - [MixHelpers.lerpTextStyle] for [style]. - /// For [overflow] and [textAlign] and [maxLines] and [textWidthBasis] and [textHeightBehavior] and [textDirection] and [softWrap] and [directive] and [animated], the interpolation is performed using a step function. + /// For [overflow] and [textAlign] and [textScaler] and [maxLines] and [textWidthBasis] and [textHeightBehavior] and [textDirection] and [softWrap] and [directive] and [animated], the interpolation is performed using a step function. /// If [t] is less than 0.5, the value from the current [TextSpec] is used. Otherwise, the value /// from the [other] [TextSpec] is used. /// @@ -94,6 +98,7 @@ base mixin _$TextSpec on Spec { textAlign: t < 0.5 ? _$this.textAlign : other.textAlign, textScaleFactor: MixHelpers.lerpDouble( _$this.textScaleFactor, other.textScaleFactor, t), + textScaler: t < 0.5 ? _$this.textScaler : other.textScaler, maxLines: t < 0.5 ? _$this.maxLines : other.maxLines, style: MixHelpers.lerpTextStyle(_$this.style, other.style, t), textWidthBasis: t < 0.5 ? _$this.textWidthBasis : other.textWidthBasis, @@ -116,6 +121,7 @@ base mixin _$TextSpec on Spec { _$this.strutStyle, _$this.textAlign, _$this.textScaleFactor, + _$this.textScaler, _$this.maxLines, _$this.style, _$this.textWidthBasis, @@ -141,6 +147,7 @@ final class TextSpecAttribute extends SpecAttribute { final StrutStyleDto? strutStyle; final TextAlign? textAlign; final double? textScaleFactor; + final TextScaler? textScaler; final int? maxLines; final TextStyleDto? style; final TextWidthBasis? textWidthBasis; @@ -154,6 +161,7 @@ final class TextSpecAttribute extends SpecAttribute { this.strutStyle, this.textAlign, this.textScaleFactor, + this.textScaler, this.maxLines, this.style, this.textWidthBasis, @@ -179,6 +187,7 @@ final class TextSpecAttribute extends SpecAttribute { strutStyle: strutStyle?.resolve(mix), textAlign: textAlign, textScaleFactor: textScaleFactor, + textScaler: textScaler, maxLines: maxLines, style: style?.resolve(mix), textWidthBasis: textWidthBasis, @@ -207,6 +216,7 @@ final class TextSpecAttribute extends SpecAttribute { strutStyle: strutStyle?.merge(other.strutStyle) ?? other.strutStyle, textAlign: other.textAlign ?? textAlign, textScaleFactor: other.textScaleFactor ?? textScaleFactor, + textScaler: other.textScaler ?? textScaler, maxLines: other.maxLines ?? maxLines, style: style?.merge(other.style) ?? other.style, textWidthBasis: other.textWidthBasis ?? textWidthBasis, @@ -228,6 +238,7 @@ final class TextSpecAttribute extends SpecAttribute { strutStyle, textAlign, textScaleFactor, + textScaler, maxLines, style, textWidthBasis, @@ -257,6 +268,9 @@ base class TextSpecUtility /// Utility for defining [TextSpecAttribute.textScaleFactor] late final textScaleFactor = DoubleUtility((v) => only(textScaleFactor: v)); + /// Utility for defining [TextSpecAttribute.textScaler] + late final textScaler = TextScalerUtility((v) => only(textScaler: v)); + /// Utility for defining [TextSpecAttribute.maxLines] late final maxLines = IntUtility((v) => only(maxLines: v)); @@ -310,6 +324,7 @@ base class TextSpecUtility StrutStyleDto? strutStyle, TextAlign? textAlign, double? textScaleFactor, + TextScaler? textScaler, int? maxLines, TextStyleDto? style, TextWidthBasis? textWidthBasis, @@ -324,6 +339,7 @@ base class TextSpecUtility strutStyle: strutStyle, textAlign: textAlign, textScaleFactor: textScaleFactor, + textScaler: textScaler, maxLines: maxLines, style: style, textWidthBasis: textWidthBasis, diff --git a/packages/mix/lib/src/specs/text/text_widget.dart b/packages/mix/lib/src/specs/text/text_widget.dart index 79badfb37..882bbc877 100644 --- a/packages/mix/lib/src/specs/text/text_widget.dart +++ b/packages/mix/lib/src/specs/text/text_widget.dart @@ -88,6 +88,11 @@ class TextSpecWidget extends StatelessWidget { @override Widget build(BuildContext context) { // The Text widget is used here, applying the resolved styles and properties from TextSpec. + + final textScaler = + // ignore: deprecated_member_use_from_same_package + spec?.textScaler ?? TextScaler.linear(spec?.textScaleFactor ?? 1); + return Text( spec?.directive?.apply(text) ?? text, style: spec?.style, @@ -97,7 +102,7 @@ class TextSpecWidget extends StatelessWidget { locale: locale, softWrap: spec?.softWrap, overflow: spec?.overflow, - textScaleFactor: spec?.textScaleFactor, + textScaler: textScaler, maxLines: spec?.maxLines, semanticsLabel: semanticsLabel, textWidthBasis: spec?.textWidthBasis, diff --git a/packages/mix/lib/src/theme/tokens/text_style_token.dart b/packages/mix/lib/src/theme/tokens/text_style_token.dart index 37bb4c3d4..4e5b247da 100644 --- a/packages/mix/lib/src/theme/tokens/text_style_token.dart +++ b/packages/mix/lib/src/theme/tokens/text_style_token.dart @@ -1,4 +1,3 @@ -import 'dart:ui'; import 'package:flutter/widgets.dart'; diff --git a/packages/mix/lib/src/widgets/widget_state/pressable_widget.dart b/packages/mix/lib/src/widgets/widget_state/pressable_widget.dart index 2636a8a7b..e113b801b 100644 --- a/packages/mix/lib/src/widgets/widget_state/pressable_widget.dart +++ b/packages/mix/lib/src/widgets/widget_state/pressable_widget.dart @@ -140,7 +140,7 @@ abstract class _MixStateWidgetBuilder extends StatefulWidget { final FocusNode? focusNode; /// {@macro flutter.widgets.Focus.onKey} - final FocusOnKeyCallback? onKey; + final FocusOnKeyEventCallback? onKey; /// {@macro flutter.widgets.Focus.onKeyEvent} final FocusOnKeyEventCallback? onKeyEvent; diff --git a/packages/mix/test/src/attributes/text_style/text_style_util_test.dart b/packages/mix/test/src/attributes/text_style/text_style_util_test.dart index e5dfa2c72..37a46646f 100644 --- a/packages/mix/test/src/attributes/text_style/text_style_util_test.dart +++ b/packages/mix/test/src/attributes/text_style/text_style_util_test.dart @@ -1,6 +1,5 @@ // Import necessary packages -import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/mix/test/src/specs/text/text_attribute_test.dart b/packages/mix/test/src/specs/text/text_attribute_test.dart index b95642261..4ebccd064 100644 --- a/packages/mix/test/src/specs/text/text_attribute_test.dart +++ b/packages/mix/test/src/specs/text/text_attribute_test.dart @@ -98,7 +98,7 @@ void main() { expect(resolved.strutStyle!.fontSize, 12); expect(resolved.strutStyle!.fontWeight, FontWeight.w500); expect(resolved.textAlign, TextAlign.center); - expect(resolved.textScaleFactor, 1.5); + expect(resolved.textScaler, const TextScaler.linear(1.5)); expect(resolved.maxLines, 2); expect(resolved.style!.fontFamily, 'Roboto'); expect(resolved.style!.fontSize, 12); diff --git a/packages/mix/test/src/specs/text/text_spec_test.dart b/packages/mix/test/src/specs/text/text_spec_test.dart index 914c204d8..e84593b0b 100644 --- a/packages/mix/test/src/specs/text/text_spec_test.dart +++ b/packages/mix/test/src/specs/text/text_spec_test.dart @@ -34,7 +34,7 @@ void main() { 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.textScaler, const TextScaler.linear(1)); expect(spec.maxLines, 2); expect(spec.style, const TextStyle(color: Colors.red)); expect(spec.textWidthBasis, TextWidthBasis.longestLine); @@ -85,7 +85,7 @@ void main() { expect(copiedSpec.overflow, TextOverflow.fade); expect(copiedSpec.strutStyle, const StrutStyle(fontSize: 30.0)); expect(copiedSpec.textAlign, TextAlign.start); - expect(copiedSpec.textScaleFactor, 2.0); + expect(copiedSpec.textScaler, const TextScaler.linear(2)); expect(copiedSpec.maxLines, 3); expect(copiedSpec.style, const TextStyle(color: Colors.blue)); expect(copiedSpec.textWidthBasis, TextWidthBasis.parent); @@ -141,7 +141,7 @@ void main() { expect(lerpedSpec.overflow, TextOverflow.fade); expect(lerpedSpec.strutStyle, const StrutStyle(fontSize: 25)); expect(lerpedSpec.textAlign, TextAlign.start); - expect(lerpedSpec.textScaleFactor, 1.5); + expect(lerpedSpec.textScaler, const TextScaler.linear(1.5)); expect(lerpedSpec.maxLines, 3); expect( lerpedSpec.style, @@ -172,7 +172,7 @@ void main() { expect(spec.overflow, isNull); expect(spec.strutStyle, isNull); expect(spec.textAlign, isNull); - expect(spec.textScaleFactor, isNull); + expect(spec.textScaler, isNull); expect(spec.maxLines, isNull); expect(spec.style, isNull); expect(spec.textWidthBasis, isNull); @@ -209,7 +209,7 @@ void main() { 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.textScaler, const TextScaler.linear(1.0)); expect(spec.maxLines, 2); expect(spec.style, const TextStyle(color: Colors.red)); expect(spec.textWidthBasis, TextWidthBasis.longestLine); @@ -263,7 +263,7 @@ void main() { expect(lerpedSpec.overflow, TextOverflow.fade); expect(lerpedSpec.strutStyle, const StrutStyle(fontSize: 25)); expect(lerpedSpec.textAlign, TextAlign.start); - expect(lerpedSpec.textScaleFactor, 1.5); + expect(lerpedSpec.textScaler, const TextScaler.linear(1.5)); expect(lerpedSpec.maxLines, 3); expect( lerpedSpec.style, diff --git a/packages/mix/test/src/specs/text/text_widget_test.dart b/packages/mix/test/src/specs/text/text_widget_test.dart index d2bfdf8d2..0e1d9e319 100644 --- a/packages/mix/test/src/specs/text/text_widget_test.dart +++ b/packages/mix/test/src/specs/text/text_widget_test.dart @@ -48,7 +48,7 @@ void main() { textDirection: textSpec.textDirection, softWrap: textSpec.softWrap, overflow: textSpec.overflow, - textScaleFactor: textSpec.textScaleFactor, + textScaler: textSpec.textScaler, maxLines: textSpec.maxLines, textWidthBasis: textSpec.textWidthBasis, textHeightBehavior: textSpec.textHeightBehavior, @@ -90,7 +90,7 @@ void main() { expect(textWidget.textDirection, mixedTextWidget.textDirection); expect(textWidget.softWrap, mixedTextWidget.softWrap); expect(textWidget.overflow, mixedTextWidget.overflow); - expect(textWidget.textScaleFactor, mixedTextWidget.textScaleFactor); + expect(textWidget.textScaler, mixedTextWidget.textScaler); expect(textWidget.maxLines, mixedTextWidget.maxLines); expect(textWidget.textWidthBasis, mixedTextWidget.textWidthBasis); expect(textWidget.textHeightBehavior, mixedTextWidget.textHeightBehavior); From 08fdae1707d32660a59c633ee05ed684abc75a55 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 8 Jul 2024 13:27:27 -0300 Subject: [PATCH 03/27] fix tests --- .../mix/lib/src/specs/text/text_spec.dart | 7 +++++-- .../src/specs/text/text_attribute_test.dart | 2 +- .../test/src/specs/text/text_spec_test.dart | 20 +++++++++---------- .../test/src/specs/text/text_widget_test.dart | 2 +- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/mix/lib/src/specs/text/text_spec.dart b/packages/mix/lib/src/specs/text/text_spec.dart index b1b90beae..e19328f98 100644 --- a/packages/mix/lib/src/specs/text/text_spec.dart +++ b/packages/mix/lib/src/specs/text/text_spec.dart @@ -43,7 +43,7 @@ final class TextSpec extends Spec with _$TextSpec { this.overflow, this.strutStyle, this.textAlign, - this.textScaleFactor, + @Deprecated('Use textScaler instead') this.textScaleFactor, this.textScaler, this.maxLines, this.style, @@ -53,7 +53,10 @@ final class TextSpec extends Spec with _$TextSpec { this.softWrap, this.directive, super.animated, - }); + }) : assert( + textScaler == null || textScaleFactor == null, + 'textScaleFactor is deprecated and cannot be specified when textScaler is specified.', + ); Widget call(String text, {String? semanticLabel, Locale? locale}) { return isAnimated diff --git a/packages/mix/test/src/specs/text/text_attribute_test.dart b/packages/mix/test/src/specs/text/text_attribute_test.dart index 4ebccd064..dac995c8a 100644 --- a/packages/mix/test/src/specs/text/text_attribute_test.dart +++ b/packages/mix/test/src/specs/text/text_attribute_test.dart @@ -98,7 +98,7 @@ void main() { expect(resolved.strutStyle!.fontSize, 12); expect(resolved.strutStyle!.fontWeight, FontWeight.w500); expect(resolved.textAlign, TextAlign.center); - expect(resolved.textScaler, const TextScaler.linear(1.5)); + expect(resolved.textScaler, null); expect(resolved.maxLines, 2); expect(resolved.style!.fontFamily, 'Roboto'); expect(resolved.style!.fontSize, 12); diff --git a/packages/mix/test/src/specs/text/text_spec_test.dart b/packages/mix/test/src/specs/text/text_spec_test.dart index e84593b0b..c8d59da27 100644 --- a/packages/mix/test/src/specs/text/text_spec_test.dart +++ b/packages/mix/test/src/specs/text/text_spec_test.dart @@ -14,7 +14,7 @@ void main() { overflow: TextOverflow.ellipsis, strutStyle: const StrutStyleDto(fontSize: 20.0), textAlign: TextAlign.center, - textScaleFactor: 1.0, + textScaler: const TextScaler.linear(1.0), maxLines: 2, style: TextStyleDto(color: const ColorDto(Colors.red)), textWidthBasis: TextWidthBasis.longestLine, @@ -54,7 +54,7 @@ void main() { overflow: TextOverflow.ellipsis, strutStyle: StrutStyle(fontSize: 20.0), textAlign: TextAlign.center, - textScaleFactor: 1.0, + textScaler: TextScaler.linear(1.0), maxLines: 2, style: TextStyle(color: Colors.red), textWidthBasis: TextWidthBasis.longestLine, @@ -71,7 +71,7 @@ void main() { overflow: TextOverflow.fade, strutStyle: const StrutStyle(fontSize: 30.0), textAlign: TextAlign.start, - textScaleFactor: 2.0, + textScaler: const TextScaler.linear(2.0), maxLines: 3, style: const TextStyle(color: Colors.blue), textWidthBasis: TextWidthBasis.parent, @@ -106,7 +106,7 @@ void main() { overflow: TextOverflow.ellipsis, strutStyle: StrutStyle(fontSize: 20.0), textAlign: TextAlign.center, - textScaleFactor: 1.0, + textScaler: TextScaler.linear(1.0), maxLines: 2, style: TextStyle(color: Colors.red), textWidthBasis: TextWidthBasis.longestLine, @@ -122,7 +122,7 @@ void main() { overflow: TextOverflow.fade, strutStyle: StrutStyle(fontSize: 30.0), textAlign: TextAlign.start, - textScaleFactor: 2.0, + textScaler: TextScaler.linear(2.0), maxLines: 3, style: TextStyle(color: Colors.blue), textWidthBasis: TextWidthBasis.parent, @@ -141,7 +141,7 @@ void main() { expect(lerpedSpec.overflow, TextOverflow.fade); expect(lerpedSpec.strutStyle, const StrutStyle(fontSize: 25)); expect(lerpedSpec.textAlign, TextAlign.start); - expect(lerpedSpec.textScaler, const TextScaler.linear(1.5)); + expect(lerpedSpec.textScaler, const TextScaler.linear(2)); expect(lerpedSpec.maxLines, 3); expect( lerpedSpec.style, @@ -190,7 +190,7 @@ void main() { overflow: TextOverflow.ellipsis, strutStyle: const StrutStyleDto(fontSize: 20.0), textAlign: TextAlign.center, - textScaleFactor: 1.0, + textScaler: const TextScaler.linear(1.0), maxLines: 2, style: TextStyleDto(color: const ColorDto(Colors.red)), textWidthBasis: TextWidthBasis.longestLine, @@ -229,7 +229,7 @@ void main() { overflow: TextOverflow.ellipsis, strutStyle: StrutStyle(fontSize: 20.0), textAlign: TextAlign.center, - textScaleFactor: 1.0, + textScaler: TextScaler.linear(1.0), maxLines: 2, style: TextStyle(color: Colors.red), textWidthBasis: TextWidthBasis.longestLine, @@ -245,7 +245,7 @@ void main() { overflow: TextOverflow.fade, strutStyle: StrutStyle(fontSize: 30.0), textAlign: TextAlign.start, - textScaleFactor: 2.0, + textScaler: TextScaler.linear(2.0), maxLines: 3, style: TextStyle(color: Colors.blue), textWidthBasis: TextWidthBasis.parent, @@ -263,7 +263,7 @@ void main() { expect(lerpedSpec.overflow, TextOverflow.fade); expect(lerpedSpec.strutStyle, const StrutStyle(fontSize: 25)); expect(lerpedSpec.textAlign, TextAlign.start); - expect(lerpedSpec.textScaler, const TextScaler.linear(1.5)); + expect(lerpedSpec.textScaler, const TextScaler.linear(2)); expect(lerpedSpec.maxLines, 3); expect( lerpedSpec.style, diff --git a/packages/mix/test/src/specs/text/text_widget_test.dart b/packages/mix/test/src/specs/text/text_widget_test.dart index 0e1d9e319..7b2ac3f5d 100644 --- a/packages/mix/test/src/specs/text/text_widget_test.dart +++ b/packages/mix/test/src/specs/text/text_widget_test.dart @@ -32,7 +32,7 @@ void main() { textDirection: TextDirection.rtl, softWrap: false, overflow: TextOverflow.ellipsis, - textScaleFactor: 1.5, + textScaler: TextScaler.linear(1.5), maxLines: 3, textWidthBasis: TextWidthBasis.longestLine, textHeightBehavior: TextHeightBehavior(applyHeightToFirstAscent: false), From 6269ba202b7754fd75f03c09543e42868240bff5 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 8 Jul 2024 13:36:46 -0300 Subject: [PATCH 04/27] fix DCM warnings --- .github/workflows/test.yml | 2 +- .../src/theme/tokens/breakpoints_token.dart | 12 +++ .../mix/lib/src/theme/tokens/color_token.dart | 15 ++++ .../mix/lib/src/theme/tokens/mix_token.dart | 1 + .../lib/src/theme/tokens/radius_token.dart | 15 ++++ .../src/theme/tokens/text_style_token.dart | 77 ++++++++++++++++++- 6 files changed, 120 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bb239c4f6..c8e3ed567 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,4 +46,4 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} run-dcm: false - flutter-version: '3.10.6' \ No newline at end of file + flutter-version: '3.19.0' \ No newline at end of file diff --git a/packages/mix/lib/src/theme/tokens/breakpoints_token.dart b/packages/mix/lib/src/theme/tokens/breakpoints_token.dart index 7bacd87d1..c38d6923f 100644 --- a/packages/mix/lib/src/theme/tokens/breakpoints_token.dart +++ b/packages/mix/lib/src/theme/tokens/breakpoints_token.dart @@ -72,6 +72,18 @@ class BreakpointToken extends MixToken { ? themeValue.resolve(context) : themeValue ?? const Breakpoint(); } + + @override + operator ==(Object other) { + if (identical(this, other)) return true; + + if (runtimeType != other.runtimeType) return false; + + return other is MixToken && other.name == name; + } + + @override + int get hashCode => Object.hash(name, runtimeType); } /// A resolver for breakpoint tokens. diff --git a/packages/mix/lib/src/theme/tokens/color_token.dart b/packages/mix/lib/src/theme/tokens/color_token.dart index 653e54ccc..87a50356e 100644 --- a/packages/mix/lib/src/theme/tokens/color_token.dart +++ b/packages/mix/lib/src/theme/tokens/color_token.dart @@ -45,6 +45,21 @@ class ColorResolver extends Color with WithTokenResolver { final BuildContextResolver resolve; const ColorResolver(this.resolve) : super(0); + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is Color && other.value == value; + } + + @override + int get hashCode => value.hashCode; } /// A reference to a color token. diff --git a/packages/mix/lib/src/theme/tokens/mix_token.dart b/packages/mix/lib/src/theme/tokens/mix_token.dart index 3a1388523..5051684cb 100644 --- a/packages/mix/lib/src/theme/tokens/mix_token.dart +++ b/packages/mix/lib/src/theme/tokens/mix_token.dart @@ -22,6 +22,7 @@ abstract class MixToken { T call(); T resolve(BuildContext context); + @override operator ==(Object other) { if (identical(this, other)) return true; diff --git a/packages/mix/lib/src/theme/tokens/radius_token.dart b/packages/mix/lib/src/theme/tokens/radius_token.dart index 8108297f4..608319fcb 100644 --- a/packages/mix/lib/src/theme/tokens/radius_token.dart +++ b/packages/mix/lib/src/theme/tokens/radius_token.dart @@ -30,6 +30,21 @@ class RadiusResolver extends Radius with WithTokenResolver { final BuildContextResolver resolve; const RadiusResolver(this.resolve) : super.circular(0); + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (runtimeType != other.runtimeType) { + return false; + } + + return other is Radius && other.x == x && other.y == y; + } + + @override + int get hashCode => Object.hash(x, y); } @immutable diff --git a/packages/mix/lib/src/theme/tokens/text_style_token.dart b/packages/mix/lib/src/theme/tokens/text_style_token.dart index 4e5b247da..5d19b17f4 100644 --- a/packages/mix/lib/src/theme/tokens/text_style_token.dart +++ b/packages/mix/lib/src/theme/tokens/text_style_token.dart @@ -1,4 +1,4 @@ - +import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import '../mix/mix_theme.dart'; @@ -32,6 +32,81 @@ class TextStyleResolver extends TextStyle with WithTokenResolver { final BuildContextResolver resolve; const TextStyleResolver(this.resolve); + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is TextStyle && + other.inherit == inherit && + other.color == color && + other.backgroundColor == backgroundColor && + other.fontSize == fontSize && + other.fontWeight == fontWeight && + other.fontStyle == fontStyle && + other.letterSpacing == letterSpacing && + other.wordSpacing == wordSpacing && + other.textBaseline == textBaseline && + other.height == height && + other.leadingDistribution == leadingDistribution && + other.locale == locale && + other.foreground == foreground && + other.background == background && + listEquals(other.shadows, shadows) && + listEquals(other.fontFeatures, fontFeatures) && + listEquals(other.fontVariations, fontVariations) && + other.decoration == decoration && + other.decorationColor == decorationColor && + other.decorationStyle == decorationStyle && + other.decorationThickness == decorationThickness && + other.fontFamily == fontFamily && + listEquals(other.fontFamilyFallback, fontFamilyFallback) && + other.overflow == overflow; + } + + @override + int get hashCode { + final List? fontFamilyFallback = this.fontFamilyFallback; + final int fontHash = Object.hash( + decorationStyle, + decorationThickness, + fontFamily, + fontFamilyFallback == null ? null : Object.hashAll(fontFamilyFallback), + overflow, + ); + + final List? shadows = this.shadows; + final List? fontFeatures = this.fontFeatures; + final List? fontVariations = this.fontVariations; + + return Object.hash( + inherit, + color, + backgroundColor, + fontSize, + fontWeight, + fontStyle, + letterSpacing, + wordSpacing, + textBaseline, + height, + leadingDistribution, + locale, + foreground, + background, + shadows == null ? null : Object.hashAll(shadows), + fontFeatures == null ? null : Object.hashAll(fontFeatures), + fontVariations == null ? null : Object.hashAll(fontVariations), + decoration, + decorationColor, + fontHash, + ); + } } @immutable From 39bf800d1adb56ec73b83dc7a746c0c0c35a2f71 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 8 Jul 2024 14:05:08 -0300 Subject: [PATCH 05/27] Add deprecated tests --- .../mix/lib/src/specs/box/box_spec.g.dart | 2 + .../mix/lib/src/specs/flex/flex_spec.g.dart | 2 + .../mix/lib/src/specs/icon/icon_spec.g.dart | 2 + .../mix/lib/src/specs/image/image_spec.g.dart | 2 + .../mix/lib/src/specs/stack/stack_spec.g.dart | 2 + .../mix/lib/src/specs/text/text_spec.dart | 5 +- .../mix/lib/src/specs/text/text_spec.g.dart | 4 +- .../mix/lib/src/specs/text/text_widget.dart | 8 +- .../src/deprecated/text_attribute_test.dart | 113 +++++++ .../test/src/deprecated/text_spec_test.dart | 288 ++++++++++++++++++ .../test/src/deprecated/text_widget_test.dart | 245 +++++++++++++++ .../lib/src/mixable_spec_generator.dart | 3 + 12 files changed, 667 insertions(+), 9 deletions(-) create mode 100644 packages/mix/test/src/deprecated/text_attribute_test.dart create mode 100644 packages/mix/test/src/deprecated/text_spec_test.dart create mode 100644 packages/mix/test/src/deprecated/text_widget_test.dart diff --git a/packages/mix/lib/src/specs/box/box_spec.g.dart b/packages/mix/lib/src/specs/box/box_spec.g.dart index 35620124c..f916dddc8 100644 --- a/packages/mix/lib/src/specs/box/box_spec.g.dart +++ b/packages/mix/lib/src/specs/box/box_spec.g.dart @@ -6,6 +6,8 @@ part of 'box_spec.dart'; // MixableSpecGenerator // ************************************************************************** +// ignore_for_file: deprecated_member_use_from_same_package + base mixin _$BoxSpec on Spec { static BoxSpec from(MixData mix) { return mix.attributeOf()?.resolve(mix) ?? const BoxSpec(); diff --git a/packages/mix/lib/src/specs/flex/flex_spec.g.dart b/packages/mix/lib/src/specs/flex/flex_spec.g.dart index 78ed6bb1b..2fb8d07dd 100644 --- a/packages/mix/lib/src/specs/flex/flex_spec.g.dart +++ b/packages/mix/lib/src/specs/flex/flex_spec.g.dart @@ -6,6 +6,8 @@ part of 'flex_spec.dart'; // MixableSpecGenerator // ************************************************************************** +// ignore_for_file: deprecated_member_use_from_same_package + base mixin _$FlexSpec on Spec { static FlexSpec from(MixData mix) { return mix.attributeOf()?.resolve(mix) ?? diff --git a/packages/mix/lib/src/specs/icon/icon_spec.g.dart b/packages/mix/lib/src/specs/icon/icon_spec.g.dart index d95b3f931..84878f37d 100644 --- a/packages/mix/lib/src/specs/icon/icon_spec.g.dart +++ b/packages/mix/lib/src/specs/icon/icon_spec.g.dart @@ -6,6 +6,8 @@ part of 'icon_spec.dart'; // MixableSpecGenerator // ************************************************************************** +// ignore_for_file: deprecated_member_use_from_same_package + base mixin _$IconSpec on Spec { static IconSpec from(MixData mix) { return mix.attributeOf()?.resolve(mix) ?? diff --git a/packages/mix/lib/src/specs/image/image_spec.g.dart b/packages/mix/lib/src/specs/image/image_spec.g.dart index 39d366d13..c34e6e75a 100644 --- a/packages/mix/lib/src/specs/image/image_spec.g.dart +++ b/packages/mix/lib/src/specs/image/image_spec.g.dart @@ -6,6 +6,8 @@ part of 'image_spec.dart'; // MixableSpecGenerator // ************************************************************************** +// ignore_for_file: deprecated_member_use_from_same_package + base mixin _$ImageSpec on Spec { static ImageSpec from(MixData mix) { return mix.attributeOf()?.resolve(mix) ?? diff --git a/packages/mix/lib/src/specs/stack/stack_spec.g.dart b/packages/mix/lib/src/specs/stack/stack_spec.g.dart index d64b3b8e7..68528321b 100644 --- a/packages/mix/lib/src/specs/stack/stack_spec.g.dart +++ b/packages/mix/lib/src/specs/stack/stack_spec.g.dart @@ -6,6 +6,8 @@ part of 'stack_spec.dart'; // MixableSpecGenerator // ************************************************************************** +// ignore_for_file: deprecated_member_use_from_same_package + base mixin _$StackSpec on Spec { static StackSpec from(MixData mix) { return mix.attributeOf()?.resolve(mix) ?? diff --git a/packages/mix/lib/src/specs/text/text_spec.dart b/packages/mix/lib/src/specs/text/text_spec.dart index e19328f98..e409d6387 100644 --- a/packages/mix/lib/src/specs/text/text_spec.dart +++ b/packages/mix/lib/src/specs/text/text_spec.dart @@ -13,13 +13,14 @@ final class TextSpec extends Spec with _$TextSpec { final int? maxLines; final TextWidthBasis? textWidthBasis; final TextHeightBehavior? textHeightBehavior; - @Deprecated('Use textScaler instead') - final double? textScaleFactor; final TextScaler? textScaler; final TextStyle? style; final TextDirection? textDirection; final bool? softWrap; + @Deprecated('Use textScaler instead') + final double? textScaleFactor; + @MixableProperty( utilities: [ MixableUtility( diff --git a/packages/mix/lib/src/specs/text/text_spec.g.dart b/packages/mix/lib/src/specs/text/text_spec.g.dart index 9ae00f8ee..2d5222d37 100644 --- a/packages/mix/lib/src/specs/text/text_spec.g.dart +++ b/packages/mix/lib/src/specs/text/text_spec.g.dart @@ -1,13 +1,13 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: deprecated_member_use_from_same_package - part of 'text_spec.dart'; // ************************************************************************** // MixableSpecGenerator // ************************************************************************** +// ignore_for_file: deprecated_member_use_from_same_package + base mixin _$TextSpec on Spec { static TextSpec from(MixData mix) { return mix.attributeOf()?.resolve(mix) ?? diff --git a/packages/mix/lib/src/specs/text/text_widget.dart b/packages/mix/lib/src/specs/text/text_widget.dart index 882bbc877..64a54834b 100644 --- a/packages/mix/lib/src/specs/text/text_widget.dart +++ b/packages/mix/lib/src/specs/text/text_widget.dart @@ -89,10 +89,6 @@ class TextSpecWidget extends StatelessWidget { Widget build(BuildContext context) { // The Text widget is used here, applying the resolved styles and properties from TextSpec. - final textScaler = - // ignore: deprecated_member_use_from_same_package - spec?.textScaler ?? TextScaler.linear(spec?.textScaleFactor ?? 1); - return Text( spec?.directive?.apply(text) ?? text, style: spec?.style, @@ -102,7 +98,9 @@ class TextSpecWidget extends StatelessWidget { locale: locale, softWrap: spec?.softWrap, overflow: spec?.overflow, - textScaler: textScaler, + // ignore: deprecated_member_use, deprecated_member_use_from_same_package + textScaleFactor: spec?.textScaleFactor, + textScaler: spec?.textScaler, maxLines: spec?.maxLines, semanticsLabel: semanticsLabel, textWidthBasis: spec?.textWidthBasis, diff --git a/packages/mix/test/src/deprecated/text_attribute_test.dart b/packages/mix/test/src/deprecated/text_attribute_test.dart new file mode 100644 index 000000000..9932495c9 --- /dev/null +++ b/packages/mix/test/src/deprecated/text_attribute_test.dart @@ -0,0 +1,113 @@ +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('TextSpecAttribute', () { + final textSpecAttribute = TextSpecAttribute( + overflow: TextOverflow.ellipsis, + strutStyle: const StrutStyleDto( + fontFamily: 'Roboto', + fontSize: 12, + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.center, + textScaleFactor: 1.5, + maxLines: 2, + style: TextStyleDto( + fontSize: 12, + fontWeight: FontWeight.w500, + fontFamily: 'Roboto', + ), + textWidthBasis: TextWidthBasis.longestLine, + textHeightBehavior: const TextHeightBehavior( + applyHeightToFirstAscent: true, + applyHeightToLastDescent: true, + ), + textDirection: TextDirection.rtl, + softWrap: true, + ); + + // Constructor + test('constructor', () { + expect(textSpecAttribute.overflow, TextOverflow.ellipsis); + expect(textSpecAttribute.strutStyle, isA()); + expect(textSpecAttribute.textAlign, TextAlign.center); + expect(textSpecAttribute.textScaleFactor, 1.5); + expect(textSpecAttribute.maxLines, 2); + expect(textSpecAttribute.style, isA()); + expect(textSpecAttribute.textWidthBasis, TextWidthBasis.longestLine); + expect(textSpecAttribute.textHeightBehavior, isA()); + expect(textSpecAttribute.textDirection, TextDirection.rtl); + expect(textSpecAttribute.softWrap, true); + }); + + // merge + test('merge', () { + final other = TextSpecAttribute( + overflow: TextOverflow.clip, + strutStyle: const StrutStyleDto( + fontFamily: 'Helvetica', + fontSize: 14, + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.center, + textScaleFactor: 1.5, + maxLines: 2, + style: TextStyleDto( + fontSize: 16, + fontWeight: FontWeight.w200, + fontFamily: 'Helvetica', + ), + textWidthBasis: TextWidthBasis.longestLine, + textHeightBehavior: const TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: false, + ), + textDirection: TextDirection.ltr, + softWrap: false, + ); + + final merged = textSpecAttribute.merge(other); + + expect(merged.overflow, TextOverflow.clip); + expect(merged.strutStyle!.fontFamily, 'Helvetica'); + expect(merged.strutStyle!.fontSize, 14); + expect(merged.strutStyle!.fontWeight, FontWeight.w500); + expect(merged.textAlign, TextAlign.center); + expect(merged.textScaleFactor, 1.5); + expect(merged.maxLines, 2); + expect(merged.style!.resolve(EmptyMixData).fontFamily, 'Helvetica'); + expect(merged.style!.resolve(EmptyMixData).fontSize, 16); + expect(merged.style!.resolve(EmptyMixData).fontWeight, FontWeight.w200); + expect(merged.textWidthBasis, TextWidthBasis.longestLine); + expect(merged.textHeightBehavior!.applyHeightToFirstAscent, false); + expect(merged.textHeightBehavior!.applyHeightToLastDescent, false); + expect(merged.textDirection, TextDirection.ltr); + expect(merged.softWrap, false); + }); + + // resolve + test('resolve', () { + final resolved = textSpecAttribute.resolve(EmptyMixData); + + expect(resolved.overflow, TextOverflow.ellipsis); + expect(resolved.strutStyle!.fontFamily, 'Roboto'); + expect(resolved.strutStyle!.fontSize, 12); + expect(resolved.strutStyle!.fontWeight, FontWeight.w500); + expect(resolved.textAlign, TextAlign.center); + expect(resolved.textScaleFactor, 1.5); + expect(resolved.maxLines, 2); + expect(resolved.style!.fontFamily, 'Roboto'); + expect(resolved.style!.fontSize, 12); + expect(resolved.style!.fontWeight, FontWeight.w500); + expect(resolved.textWidthBasis, TextWidthBasis.longestLine); + expect(resolved.textHeightBehavior!.applyHeightToFirstAscent, true); + expect(resolved.textHeightBehavior!.applyHeightToLastDescent, true); + expect(resolved.textDirection, TextDirection.rtl); + expect(resolved.softWrap, true); + }); + }); +} diff --git a/packages/mix/test/src/deprecated/text_spec_test.dart b/packages/mix/test/src/deprecated/text_spec_test.dart new file mode 100644 index 000000000..82c0a574f --- /dev/null +++ b/packages/mix/test/src/deprecated/text_spec_test.dart @@ -0,0 +1,288 @@ +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('TextSpec', () { + test('resolve', () { + final mix = MixData.create( + MockBuildContext(), + Style( + TextSpecAttribute( + overflow: TextOverflow.ellipsis, + strutStyle: const StrutStyleDto(fontSize: 20.0), + textAlign: TextAlign.center, + textScaleFactor: 1.0, + maxLines: 2, + style: TextStyleDto(color: const ColorDto(Colors.red)), + textWidthBasis: TextWidthBasis.longestLine, + textHeightBehavior: const TextHeightBehavior( + applyHeightToFirstAscent: true, + applyHeightToLastDescent: true, + ), + textDirection: TextDirection.ltr, + softWrap: true, + ), + ), + ); + + final spec = mix.attributeOf()?.resolve(mix) ?? + const TextSpec(); + + 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( + spec.textHeightBehavior, + const TextHeightBehavior( + applyHeightToFirstAscent: true, + applyHeightToLastDescent: true, + ), + ); + expect(spec.textDirection, TextDirection.ltr); + expect(spec.softWrap, true); + }); + + test('copyWith', () { + const spec = TextSpec( + overflow: TextOverflow.ellipsis, + strutStyle: StrutStyle(fontSize: 20.0), + textAlign: TextAlign.center, + textScaleFactor: 1.0, + maxLines: 2, + style: TextStyle(color: Colors.red), + textWidthBasis: TextWidthBasis.longestLine, + textHeightBehavior: TextHeightBehavior( + applyHeightToFirstAscent: true, + applyHeightToLastDescent: true, + ), + textDirection: TextDirection.ltr, + softWrap: true, + ); + + final copiedSpec = spec.copyWith( + softWrap: false, + overflow: TextOverflow.fade, + strutStyle: const StrutStyle(fontSize: 30.0), + textAlign: TextAlign.start, + textScaleFactor: 2.0, + maxLines: 3, + style: const TextStyle(color: Colors.blue), + textWidthBasis: TextWidthBasis.parent, + textHeightBehavior: const TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: false, + ), + textDirection: TextDirection.rtl, + ); + + expect(copiedSpec.overflow, TextOverflow.fade); + expect(copiedSpec.strutStyle, const StrutStyle(fontSize: 30.0)); + expect(copiedSpec.textAlign, TextAlign.start); + expect(copiedSpec.textScaleFactor, 2.0); + expect(copiedSpec.maxLines, 3); + expect(copiedSpec.style, const TextStyle(color: Colors.blue)); + expect(copiedSpec.textWidthBasis, TextWidthBasis.parent); + expect( + copiedSpec.textHeightBehavior, + const TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: false, + ), + ); + + expect(copiedSpec.textDirection, TextDirection.rtl); + expect(copiedSpec.softWrap, false); + }); + + test('lerp', () { + const spec1 = TextSpec( + overflow: TextOverflow.ellipsis, + strutStyle: StrutStyle(fontSize: 20.0), + textAlign: TextAlign.center, + textScaleFactor: 1.0, + maxLines: 2, + style: TextStyle(color: Colors.red), + textWidthBasis: TextWidthBasis.longestLine, + textHeightBehavior: TextHeightBehavior( + applyHeightToFirstAscent: true, + applyHeightToLastDescent: true, + ), + textDirection: TextDirection.ltr, + softWrap: true, + ); + + const spec2 = TextSpec( + overflow: TextOverflow.fade, + strutStyle: StrutStyle(fontSize: 30.0), + textAlign: TextAlign.start, + textScaleFactor: 2.0, + maxLines: 3, + style: TextStyle(color: Colors.blue), + textWidthBasis: TextWidthBasis.parent, + textHeightBehavior: TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: false, + ), + textDirection: TextDirection.rtl, + softWrap: false, + ); + + const t = 0.5; + + final lerpedSpec = spec1.lerp(spec2, t); + + expect(lerpedSpec.overflow, TextOverflow.fade); + expect(lerpedSpec.strutStyle, const StrutStyle(fontSize: 25)); + expect(lerpedSpec.textAlign, TextAlign.start); + expect(lerpedSpec.textScaleFactor, 1.5); + expect(lerpedSpec.maxLines, 3); + expect( + lerpedSpec.style, + TextStyle.lerp( + const TextStyle(color: Colors.red), + const TextStyle(color: Colors.blue), + t, + ), + ); + expect(lerpedSpec.textWidthBasis, TextWidthBasis.parent); + + expect( + lerpedSpec.textHeightBehavior, + const TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: false, + ), + ); + expect(lerpedSpec.textDirection, TextDirection.rtl); + expect(lerpedSpec.softWrap, false); + + expect(lerpedSpec, isNot(spec1)); + }); + + test('TextSpec.empty() constructor', () { + const spec = TextSpec(); + + expect(spec.overflow, isNull); + expect(spec.strutStyle, isNull); + expect(spec.textAlign, isNull); + expect(spec.textScaleFactor, isNull); + expect(spec.maxLines, isNull); + expect(spec.style, isNull); + expect(spec.textWidthBasis, isNull); + expect(spec.textHeightBehavior, isNull); + expect(spec.textDirection, isNull); + expect(spec.softWrap, isNull); + expect(spec.directive, isNull); + }); + + test('TextSpec.from(MixData mix)', () { + final mixData = MixData.create( + MockBuildContext(), + Style( + TextSpecAttribute( + overflow: TextOverflow.ellipsis, + strutStyle: const StrutStyleDto(fontSize: 20.0), + textAlign: TextAlign.center, + textScaleFactor: 1.0, + maxLines: 2, + style: TextStyleDto(color: const ColorDto(Colors.red)), + textWidthBasis: TextWidthBasis.longestLine, + textHeightBehavior: const TextHeightBehavior( + applyHeightToFirstAscent: true, + applyHeightToLastDescent: true, + ), + textDirection: TextDirection.ltr, + softWrap: true, + ), + ), + ); + + final spec = TextSpec.from(mixData); + + 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( + spec.textHeightBehavior, + const TextHeightBehavior( + applyHeightToFirstAscent: true, + applyHeightToLastDescent: true, + ), + ); + expect(spec.textDirection, TextDirection.ltr); + expect(spec.softWrap, true); + }); + + test('TextSpecTween lerp', () { + const spec1 = TextSpec( + overflow: TextOverflow.ellipsis, + strutStyle: StrutStyle(fontSize: 20.0), + textAlign: TextAlign.center, + textScaleFactor: 1.0, + maxLines: 2, + style: TextStyle(color: Colors.red), + textWidthBasis: TextWidthBasis.longestLine, + textHeightBehavior: TextHeightBehavior( + applyHeightToFirstAscent: true, + applyHeightToLastDescent: true, + ), + textDirection: TextDirection.ltr, + softWrap: true, + ); + + const spec2 = TextSpec( + overflow: TextOverflow.fade, + strutStyle: StrutStyle(fontSize: 30.0), + textAlign: TextAlign.start, + textScaleFactor: 2.0, + maxLines: 3, + style: TextStyle(color: Colors.blue), + textWidthBasis: TextWidthBasis.parent, + textHeightBehavior: TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: false, + ), + textDirection: TextDirection.rtl, + softWrap: false, + ); + + final tween = TextSpecTween(begin: spec1, end: spec2); + + final lerpedSpec = tween.lerp(0.5); + expect(lerpedSpec.overflow, TextOverflow.fade); + expect(lerpedSpec.strutStyle, const StrutStyle(fontSize: 25)); + expect(lerpedSpec.textAlign, TextAlign.start); + expect(lerpedSpec.textScaleFactor, 1.5); + expect(lerpedSpec.maxLines, 3); + expect( + lerpedSpec.style, + TextStyle.lerp( + const TextStyle(color: Colors.red), + const TextStyle(color: Colors.blue), + 0.5, + ), + ); + expect(lerpedSpec.textWidthBasis, TextWidthBasis.parent); + expect( + lerpedSpec.textHeightBehavior, + const TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: false, + ), + ); + expect(lerpedSpec.textDirection, TextDirection.rtl); + expect(lerpedSpec.softWrap, false); + }); + }); +} diff --git a/packages/mix/test/src/deprecated/text_widget_test.dart b/packages/mix/test/src/deprecated/text_widget_test.dart new file mode 100644 index 000000000..527b13455 --- /dev/null +++ b/packages/mix/test/src/deprecated/text_widget_test.dart @@ -0,0 +1,245 @@ +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( + 'StyledText should apply modifiers only once', + (tester) async { + await tester.pumpMaterialApp( + StyledText( + 'test', + style: Style( + $box.height(100), + $box.width(100), + $with.align(), + ), + ), + ); + + expect(find.byType(Align), findsOneWidget); + }, + ); + + testWidgets('TextSpec properties should match Text properties', + (WidgetTester tester) async { + const textSpec = TextSpec( + style: TextStyle(fontSize: 20, color: Colors.red), + strutStyle: StrutStyle(fontSize: 16), + textAlign: TextAlign.center, + textDirection: TextDirection.rtl, + softWrap: false, + overflow: TextOverflow.ellipsis, + textScaleFactor: 1.5, + maxLines: 3, + textWidthBasis: TextWidthBasis.longestLine, + textHeightBehavior: TextHeightBehavior(applyHeightToFirstAscent: false), + ); + + const textKey = Key('text'); + final text = Text( + 'Sample Text', + key: textKey, + style: textSpec.style, + strutStyle: textSpec.strutStyle, + textAlign: textSpec.textAlign, + textDirection: textSpec.textDirection, + softWrap: textSpec.softWrap, + overflow: textSpec.overflow, + textScaler: textSpec.textScaler, + maxLines: textSpec.maxLines, + textWidthBasis: textSpec.textWidthBasis, + textHeightBehavior: textSpec.textHeightBehavior, + ); + const mixedTextKey = Key('mixed_text'); + const mixedText = TextSpecWidget( + 'Mixed Text', + key: mixedTextKey, + spec: textSpec, + ); + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Column( + children: [text, mixedText], + ), + ), + ), + ); + + final textFinder = find.byKey(textKey); + final textWidget = tester.widget(textFinder); + expect(textFinder, findsOneWidget); + final mixedTextFinder = find.byKey(mixedTextKey); + expect(mixedTextFinder, findsOneWidget); + + final mixedTextWidget = tester.widget(find.descendant( + of: mixedTextFinder, + matching: find.byType(Text), + )); + + expect(textWidget.data, 'Sample Text'); + expect(mixedTextWidget.data, 'Mixed Text'); + expect(textWidget.style, mixedTextWidget.style); + expect(textWidget.strutStyle, mixedTextWidget.strutStyle); + expect(textWidget.textAlign, mixedTextWidget.textAlign); + expect(textWidget.textDirection, mixedTextWidget.textDirection); + expect(textWidget.softWrap, mixedTextWidget.softWrap); + expect(textWidget.overflow, mixedTextWidget.overflow); + expect(textWidget.textScaler, mixedTextWidget.textScaler); + expect(textWidget.maxLines, mixedTextWidget.maxLines); + expect(textWidget.textWidthBasis, mixedTextWidget.textWidthBasis); + expect(textWidget.textHeightBehavior, mixedTextWidget.textHeightBehavior); + }); + + testWidgets('StyledText should apply TextSpec properties', (tester) async { + await tester.pumpMaterialApp( + StyledText( + 'Hello, World!', + style: Style( + $text.style.color(Colors.red), + $text.style.fontSize(24.0), + ), + ), + ); + + final textFinder = find.byType(Text); + expect(textFinder, findsOneWidget); + + final text = tester.widget(textFinder); + expect(text.style?.color, Colors.red); + expect(text.style?.fontSize, 24.0); + }); + + testWidgets('StyledText should handle inherit property', (tester) async { + await tester.pumpMaterialApp( + StyledText( + 'Hello, World!', + style: Style($text.style.color(Colors.red)), + inherit: false, + ), + ); + + final textFinder = find.byType(Text); + expect(textFinder, findsOneWidget); + + final text = tester.widget(textFinder); + expect(text.style?.color, Colors.red); + }); + + testWidgets('StyledText should handle locale property', (tester) async { + await tester.pumpMaterialApp( + StyledText( + 'Hello, World!', + style: Style($text.style.color(Colors.red)), + locale: const Locale('en', 'US'), + ), + ); + + final textFinder = find.byType(Text); + expect(textFinder, findsOneWidget); + + final text = tester.widget(textFinder); + expect(text.locale, const Locale('en', 'US')); + }); + + testWidgets('TextSpecWidget should handle semanticsLabel and locale', + (tester) async { + const spec = TextSpec(style: TextStyle(color: Colors.blue)); + + await tester.pumpMaterialApp( + const TextSpecWidget( + 'Hello, World!', + spec: spec, + semanticsLabel: 'Custom Text', + locale: Locale('fr', 'FR'), + ), + ); + + final textFinder = find.byType(Text); + expect(textFinder, findsOneWidget); + + final text = tester.widget(textFinder); + expect(text.semanticsLabel, 'Custom Text'); + expect(text.locale, const Locale('fr', 'FR')); + }); + + testWidgets('TextSpecWidget should apply TextSpec.directive', (tester) async { + final spec = TextSpec(directive: TextDirective((v) => v.toUpperCase())); + + await tester.pumpMaterialApp( + TextSpecWidget( + 'Hello, World!', + spec: spec, + ), + ); + + final textFinder = find.text('HELLO, WORLD!'); + expect(textFinder, findsOneWidget); + }); + + testWidgets('AnimatedTextSpecWidget should animate TextSpec properties', + (tester) async { + const spec1 = TextSpec(style: TextStyle(color: Colors.red, fontSize: 16.0)); + const spec2 = + TextSpec(style: TextStyle(color: Colors.blue, fontSize: 24.0)); + + await tester.pumpMaterialApp( + const AnimatedTextSpecWidget( + 'Hello, World!', + spec: spec1, + duration: Duration(milliseconds: 500), + ), + ); + + expect(find.byType(TextSpecWidget), findsOneWidget); + Text text = tester.widget(find.byType(Text)); + expect(text.style?.color, Colors.red); + expect(text.style?.fontSize, 16.0); + + await tester.pumpMaterialApp( + const AnimatedTextSpecWidget( + 'Hello, World!', + spec: spec2, + duration: Duration(milliseconds: 500), + ), + ); + await tester.pump(const Duration(milliseconds: 250)); + + expect(find.byType(TextSpecWidget), findsOneWidget); + text = tester.widget(find.byType(Text)); + expect(text.style?.color, Color.lerp(Colors.red, Colors.blue, 0.5)); + expect(text.style?.fontSize, 20.0); + + await tester.pump(const Duration(milliseconds: 250)); + + expect(find.byType(TextSpecWidget), findsOneWidget); + text = tester.widget(find.byType(Text)); + expect(text.style?.color, Colors.blue); + expect(text.style?.fontSize, 24.0); + }); + + testWidgets('AnimatedTextSpecWidget should handle semanticsLabel and locale', + (tester) async { + const spec = TextSpec(style: TextStyle(color: Colors.blue)); + + await tester.pumpMaterialApp( + const AnimatedTextSpecWidget( + 'Hello, World!', + spec: spec, + duration: Duration(milliseconds: 500), + semanticsLabel: 'Custom Animated Text', + locale: Locale('es', 'ES'), + ), + ); + + expect(find.byType(TextSpecWidget), findsOneWidget); + final textSpecWidget = + tester.widget(find.byType(TextSpecWidget)); + expect(textSpecWidget.semanticsLabel, 'Custom Animated Text'); + expect(textSpecWidget.locale, const Locale('es', 'ES')); + }); +} diff --git a/packages/mix_generator/lib/src/mixable_spec_generator.dart b/packages/mix_generator/lib/src/mixable_spec_generator.dart index ba34957c7..0ba7aa6f4 100644 --- a/packages/mix_generator/lib/src/mixable_spec_generator.dart +++ b/packages/mix_generator/lib/src/mixable_spec_generator.dart @@ -41,6 +41,9 @@ class MixableSpecGenerator extends GeneratorForAnnotation { ); final output = ''' + + // ignore_for_file: deprecated_member_use_from_same_package + ${specMixin(context)} ${specAttributeClass(context)} From 58465a6a9be6016b3c4ebfa4ea6faaa4cf72b1af Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 8 Jul 2024 14:13:56 -0300 Subject: [PATCH 06/27] fix tests --- packages/mix/test/src/deprecated/text_attribute_test.dart | 1 + packages/mix/test/src/deprecated/text_spec_test.dart | 1 + packages/mix/test/src/deprecated/text_widget_test.dart | 1 + 3 files changed, 3 insertions(+) diff --git a/packages/mix/test/src/deprecated/text_attribute_test.dart b/packages/mix/test/src/deprecated/text_attribute_test.dart index 9932495c9..3e8fe3c94 100644 --- a/packages/mix/test/src/deprecated/text_attribute_test.dart +++ b/packages/mix/test/src/deprecated/text_attribute_test.dart @@ -1,3 +1,4 @@ +// ignore_for_file: deprecated_member_use_from_same_package import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; diff --git a/packages/mix/test/src/deprecated/text_spec_test.dart b/packages/mix/test/src/deprecated/text_spec_test.dart index 82c0a574f..2c6344cb0 100644 --- a/packages/mix/test/src/deprecated/text_spec_test.dart +++ b/packages/mix/test/src/deprecated/text_spec_test.dart @@ -1,3 +1,4 @@ +// ignore_for_file: deprecated_member_use_from_same_package import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; diff --git a/packages/mix/test/src/deprecated/text_widget_test.dart b/packages/mix/test/src/deprecated/text_widget_test.dart index 527b13455..56ae174f9 100644 --- a/packages/mix/test/src/deprecated/text_widget_test.dart +++ b/packages/mix/test/src/deprecated/text_widget_test.dart @@ -1,3 +1,4 @@ +// ignore_for_file: deprecated_member_use_from_same_package import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; From 26d2be38bfdd08ecfdca4fedabfa6ccf0c32cc1d Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 8 Jul 2024 14:16:19 -0300 Subject: [PATCH 07/27] Update text_widget_test.dart --- packages/mix/test/src/deprecated/text_widget_test.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mix/test/src/deprecated/text_widget_test.dart b/packages/mix/test/src/deprecated/text_widget_test.dart index 56ae174f9..3cdf5a183 100644 --- a/packages/mix/test/src/deprecated/text_widget_test.dart +++ b/packages/mix/test/src/deprecated/text_widget_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: deprecated_member_use_from_same_package, deprecated_member_use import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; @@ -49,7 +49,7 @@ void main() { textDirection: textSpec.textDirection, softWrap: textSpec.softWrap, overflow: textSpec.overflow, - textScaler: textSpec.textScaler, + textScaleFactor: textSpec.textScaleFactor, maxLines: textSpec.maxLines, textWidthBasis: textSpec.textWidthBasis, textHeightBehavior: textSpec.textHeightBehavior, @@ -90,7 +90,7 @@ void main() { expect(textWidget.textDirection, mixedTextWidget.textDirection); expect(textWidget.softWrap, mixedTextWidget.softWrap); expect(textWidget.overflow, mixedTextWidget.overflow); - expect(textWidget.textScaler, mixedTextWidget.textScaler); + expect(textWidget.textScaleFactor, mixedTextWidget.textScaleFactor); expect(textWidget.maxLines, mixedTextWidget.maxLines); expect(textWidget.textWidthBasis, mixedTextWidget.textWidthBasis); expect(textWidget.textHeightBehavior, mixedTextWidget.textHeightBehavior); From c73bd39dd9cdae96b33824d253e32c29ae1278f7 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 8 Jul 2024 21:01:55 -0300 Subject: [PATCH 08/27] Revert "fix DCM warnings" This reverts commit 6269ba202b7754fd75f03c09543e42868240bff5. --- .../src/theme/tokens/breakpoints_token.dart | 12 --- .../mix/lib/src/theme/tokens/color_token.dart | 15 ---- .../mix/lib/src/theme/tokens/mix_token.dart | 1 - .../lib/src/theme/tokens/radius_token.dart | 15 ---- .../src/theme/tokens/text_style_token.dart | 77 +------------------ 5 files changed, 1 insertion(+), 119 deletions(-) diff --git a/packages/mix/lib/src/theme/tokens/breakpoints_token.dart b/packages/mix/lib/src/theme/tokens/breakpoints_token.dart index c38d6923f..7bacd87d1 100644 --- a/packages/mix/lib/src/theme/tokens/breakpoints_token.dart +++ b/packages/mix/lib/src/theme/tokens/breakpoints_token.dart @@ -72,18 +72,6 @@ class BreakpointToken extends MixToken { ? themeValue.resolve(context) : themeValue ?? const Breakpoint(); } - - @override - operator ==(Object other) { - if (identical(this, other)) return true; - - if (runtimeType != other.runtimeType) return false; - - return other is MixToken && other.name == name; - } - - @override - int get hashCode => Object.hash(name, runtimeType); } /// A resolver for breakpoint tokens. diff --git a/packages/mix/lib/src/theme/tokens/color_token.dart b/packages/mix/lib/src/theme/tokens/color_token.dart index 87a50356e..653e54ccc 100644 --- a/packages/mix/lib/src/theme/tokens/color_token.dart +++ b/packages/mix/lib/src/theme/tokens/color_token.dart @@ -45,21 +45,6 @@ class ColorResolver extends Color with WithTokenResolver { final BuildContextResolver resolve; const ColorResolver(this.resolve) : super(0); - - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - if (other.runtimeType != runtimeType) { - return false; - } - - return other is Color && other.value == value; - } - - @override - int get hashCode => value.hashCode; } /// A reference to a color token. diff --git a/packages/mix/lib/src/theme/tokens/mix_token.dart b/packages/mix/lib/src/theme/tokens/mix_token.dart index 5051684cb..3a1388523 100644 --- a/packages/mix/lib/src/theme/tokens/mix_token.dart +++ b/packages/mix/lib/src/theme/tokens/mix_token.dart @@ -22,7 +22,6 @@ abstract class MixToken { T call(); T resolve(BuildContext context); - @override operator ==(Object other) { if (identical(this, other)) return true; diff --git a/packages/mix/lib/src/theme/tokens/radius_token.dart b/packages/mix/lib/src/theme/tokens/radius_token.dart index 608319fcb..8108297f4 100644 --- a/packages/mix/lib/src/theme/tokens/radius_token.dart +++ b/packages/mix/lib/src/theme/tokens/radius_token.dart @@ -30,21 +30,6 @@ class RadiusResolver extends Radius with WithTokenResolver { final BuildContextResolver resolve; const RadiusResolver(this.resolve) : super.circular(0); - - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - if (runtimeType != other.runtimeType) { - return false; - } - - return other is Radius && other.x == x && other.y == y; - } - - @override - int get hashCode => Object.hash(x, y); } @immutable diff --git a/packages/mix/lib/src/theme/tokens/text_style_token.dart b/packages/mix/lib/src/theme/tokens/text_style_token.dart index 5d19b17f4..4e5b247da 100644 --- a/packages/mix/lib/src/theme/tokens/text_style_token.dart +++ b/packages/mix/lib/src/theme/tokens/text_style_token.dart @@ -1,4 +1,4 @@ -import 'package:flutter/foundation.dart'; + import 'package:flutter/widgets.dart'; import '../mix/mix_theme.dart'; @@ -32,81 +32,6 @@ class TextStyleResolver extends TextStyle with WithTokenResolver { final BuildContextResolver resolve; const TextStyleResolver(this.resolve); - - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - if (other.runtimeType != runtimeType) { - return false; - } - - return other is TextStyle && - other.inherit == inherit && - other.color == color && - other.backgroundColor == backgroundColor && - other.fontSize == fontSize && - other.fontWeight == fontWeight && - other.fontStyle == fontStyle && - other.letterSpacing == letterSpacing && - other.wordSpacing == wordSpacing && - other.textBaseline == textBaseline && - other.height == height && - other.leadingDistribution == leadingDistribution && - other.locale == locale && - other.foreground == foreground && - other.background == background && - listEquals(other.shadows, shadows) && - listEquals(other.fontFeatures, fontFeatures) && - listEquals(other.fontVariations, fontVariations) && - other.decoration == decoration && - other.decorationColor == decorationColor && - other.decorationStyle == decorationStyle && - other.decorationThickness == decorationThickness && - other.fontFamily == fontFamily && - listEquals(other.fontFamilyFallback, fontFamilyFallback) && - other.overflow == overflow; - } - - @override - int get hashCode { - final List? fontFamilyFallback = this.fontFamilyFallback; - final int fontHash = Object.hash( - decorationStyle, - decorationThickness, - fontFamily, - fontFamilyFallback == null ? null : Object.hashAll(fontFamilyFallback), - overflow, - ); - - final List? shadows = this.shadows; - final List? fontFeatures = this.fontFeatures; - final List? fontVariations = this.fontVariations; - - return Object.hash( - inherit, - color, - backgroundColor, - fontSize, - fontWeight, - fontStyle, - letterSpacing, - wordSpacing, - textBaseline, - height, - leadingDistribution, - locale, - foreground, - background, - shadows == null ? null : Object.hashAll(shadows), - fontFeatures == null ? null : Object.hashAll(fontFeatures), - fontVariations == null ? null : Object.hashAll(fontVariations), - decoration, - decorationColor, - fontHash, - ); - } } @immutable From 5f2703efefa32a334106ce410537f388717da858 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 8 Jul 2024 21:27:56 -0300 Subject: [PATCH 09/27] use generated code --- packages/mix/analysis_options.yaml | 1 + .../src/attributes/scalars/scalar_util.dart | 6 ++++++ .../src/attributes/scalars/scalar_util.g.dart | 19 +++++++++++++++++++ packages/mix/lib/src/core/utility.dart | 10 ---------- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/mix/analysis_options.yaml b/packages/mix/analysis_options.yaml index e3acf998c..aea72eec4 100644 --- a/packages/mix/analysis_options.yaml +++ b/packages/mix/analysis_options.yaml @@ -25,6 +25,7 @@ dart_code_metrics: avoid-importing-entrypoint-exports: only-in-src: true prefer-match-file-name: false + prefer-overriding-parent-equality: false prefer-correct-callback-field-name: false prefer-single-widget-per-file: false match-getter-setter-field-names: false diff --git a/packages/mix/lib/src/attributes/scalars/scalar_util.dart b/packages/mix/lib/src/attributes/scalars/scalar_util.dart index 0ab4a2e5e..747fa5ce1 100644 --- a/packages/mix/lib/src/attributes/scalars/scalar_util.dart +++ b/packages/mix/lib/src/attributes/scalars/scalar_util.dart @@ -160,3 +160,9 @@ final class FontFamilyUtility extends MixUtility with _$FontFamilyUtility { const FontFamilyUtility(super.builder); } + +@MixableClassUtility() +final class TextScalerUtility + extends MixUtility with _$TextScalerUtility { + const TextScalerUtility(super.builder); +} diff --git a/packages/mix/lib/src/attributes/scalars/scalar_util.g.dart b/packages/mix/lib/src/attributes/scalars/scalar_util.g.dart index b1fa8c71b..1bb63e505 100644 --- a/packages/mix/lib/src/attributes/scalars/scalar_util.g.dart +++ b/packages/mix/lib/src/attributes/scalars/scalar_util.g.dart @@ -601,3 +601,22 @@ base mixin _$FontFamilyUtility on MixUtility { /// Creates an [Attribute] instance with the specified String value. T call(String value) => builder(value); } + +/// {@template text_scaler_utility} +/// A utility class for creating [Attribute] instances from [TextScaler] values. +/// +/// This class extends [MixUtility] and provides methods to create [Attribute] instances +/// from predefined [TextScaler] values. +/// {@endtemplate} +base mixin _$TextScalerUtility + on MixUtility { + /// Creates an [Attribute] instance with [TextScaler.noScaling] value. + T noScaling() => builder(TextScaler.noScaling); + + /// Creates an [Attribute] instance using the [TextScaler.linear] constructor. + T linear(double textScaleFactor) => + builder(TextScaler.linear(textScaleFactor)); + + /// Creates an [Attribute] instance with the specified TextScaler value. + T call(TextScaler value) => builder(value); +} diff --git a/packages/mix/lib/src/core/utility.dart b/packages/mix/lib/src/core/utility.dart index 468690c47..b7e70262c 100644 --- a/packages/mix/lib/src/core/utility.dart +++ b/packages/mix/lib/src/core/utility.dart @@ -80,13 +80,3 @@ abstract base class SizingUtility extends ScalarUtility { SizingUtility(super.builder); } - -// create a Utility class for TextScaler -final class TextScalerUtility - extends ScalarUtility { - const TextScalerUtility(super.builder); - - T noScaling() => builder(TextScaler.noScaling); - T linear(double textScaleFactor) => - builder(TextScaler.linear(textScaleFactor)); -} From 3ab0d7e7da21b54b79f01ce55b6e153f64ea8d48 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Thu, 27 Jun 2024 10:22:23 -0300 Subject: [PATCH 10/27] feat: inline modifiers --- melos.yaml | 2 +- .../attributes/modifiers/modifiers_data.dart | 7 + .../modifiers/modifiers_data_dto.dart | 29 ++++ .../attributes/modifiers/modifiers_util.dart | 38 +++++ packages/mix/lib/src/core/spec.dart | 8 +- packages/mix/lib/src/core/styled_widget.dart | 6 +- .../src/modifiers/render_widget_modifier.dart | 66 ++++++--- packages/mix/lib/src/specs/box/box_spec.dart | 5 + .../mix/lib/src/specs/box/box_spec.g.dart | 15 +- .../modifiers/modifiers_data_test.dart | 119 +++++++++++++++ .../widget_modifier_widget_test.dart | 25 ++-- .../modifiers/widget_modifiers_util_test.dart | 10 +- .../src/specs/box/box_attribute_test.dart | 135 ++++++++++++------ .../mix/test/src/specs/box/box_spec_test.dart | 51 ++++++- .../lib/src/helpers/type_ref_repository.dart | 1 + 15 files changed, 426 insertions(+), 91 deletions(-) create mode 100644 packages/mix/lib/src/attributes/modifiers/modifiers_data.dart create mode 100644 packages/mix/lib/src/attributes/modifiers/modifiers_data_dto.dart create mode 100644 packages/mix/lib/src/attributes/modifiers/modifiers_util.dart create mode 100644 packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart diff --git a/melos.yaml b/melos.yaml index 877a6c11d..91c8675b6 100644 --- a/melos.yaml +++ b/melos.yaml @@ -1,6 +1,6 @@ name: mix_workspace -sdkPath: .fvm/flutter_sdk +# sdkPath: .fvm/flutter_sdk packages: - packages/** - examples/** diff --git a/packages/mix/lib/src/attributes/modifiers/modifiers_data.dart b/packages/mix/lib/src/attributes/modifiers/modifiers_data.dart new file mode 100644 index 000000000..d151ce7d6 --- /dev/null +++ b/packages/mix/lib/src/attributes/modifiers/modifiers_data.dart @@ -0,0 +1,7 @@ +import '../../core/modifier.dart'; + +class ModifiersData { + // ignore: avoid-dynamic + final List> value; + const ModifiersData(this.value); +} diff --git a/packages/mix/lib/src/attributes/modifiers/modifiers_data_dto.dart b/packages/mix/lib/src/attributes/modifiers/modifiers_data_dto.dart new file mode 100644 index 000000000..ff24be7ef --- /dev/null +++ b/packages/mix/lib/src/attributes/modifiers/modifiers_data_dto.dart @@ -0,0 +1,29 @@ +import '../../core/core.dart'; +import 'modifiers_data.dart'; + +class ModifiersDataDto extends Dto { + final List value; + + const ModifiersDataDto(this.value); + + @override + ModifiersDataDto merge(ModifiersDataDto? other) { + if (other == null) return this; + final thisMap = AttributeMap(value); + final otherMap = AttributeMap(other.value); + final mergedMap = thisMap.merge(otherMap).values; + + return ModifiersDataDto(mergedMap); + } + + @override + ModifiersData resolve(MixData mix) { + return ModifiersData(value.map((e) => e.resolve(mix)).toList()); + } + + @override + ModifiersData get defaultValue => const ModifiersData([]); + + @override + List get props => [value]; +} diff --git a/packages/mix/lib/src/attributes/modifiers/modifiers_util.dart b/packages/mix/lib/src/attributes/modifiers/modifiers_util.dart new file mode 100644 index 000000000..83902103e --- /dev/null +++ b/packages/mix/lib/src/attributes/modifiers/modifiers_util.dart @@ -0,0 +1,38 @@ +import '../../core/core.dart'; +import '../../modifiers/modifiers.dart'; +import '../spacing/spacing_util.dart'; +import 'modifiers_data_dto.dart'; + +final class ModifiersDataUtility + extends MixUtility { + late final add = WithModifierUtility((v) => builder(ModifiersDataDto([v]))); + + late final intrinsicWidth = IntrinsicWidthWidgetUtility(only); + late final intrinsicHeight = IntrinsicHeightWidgetUtility(only); + late final rotate = RotatedBoxWidgetUtility(only); + late final opacity = OpacityUtility(only); + late final clipPath = ClipPathUtility(only); + late final clipRRect = ClipRRectUtility(only); + late final clipOval = ClipOvalUtility(only); + late final clipRect = ClipRectUtility(only); + late final clipTriangle = ClipTriangleUtility(only); + late final visibility = VisibilityUtility(only); + late final show = visibility.on; + late final hide = visibility.off; + late final aspectRatio = AspectRatioUtility(only); + late final flexible = FlexibleModifierUtility(only); + late final expanded = flexible.expanded; + late final transform = TransformUtility(only); + + late final scale = transform.scale; + late final align = AlignWidgetUtility(only); + late final fractionallySizedBox = FractionallySizedBoxModifierUtility(only); + late final sizedBox = SizedBoxModifierUtility(only); + late final padding = SpacingUtility(PaddingModifierUtility(only)); + + ModifiersDataUtility(super.builder); + + T only(WidgetModifierAttribute attribute) { + return builder(ModifiersDataDto([attribute])); + } +} diff --git a/packages/mix/lib/src/core/spec.dart b/packages/mix/lib/src/core/spec.dart index d94bd1b3c..989fb0149 100644 --- a/packages/mix/lib/src/core/spec.dart +++ b/packages/mix/lib/src/core/spec.dart @@ -2,6 +2,8 @@ import 'package:flutter/foundation.dart'; import '../attributes/animated/animated_data.dart'; import '../attributes/animated/animated_data_dto.dart'; +import '../attributes/modifiers/modifiers_data.dart'; +import '../attributes/modifiers/modifiers_data_dto.dart'; import '../internal/compare_mixin.dart'; import 'attribute.dart'; import 'factory/mix_data.dart'; @@ -10,8 +12,9 @@ import 'utility.dart'; @immutable abstract class Spec> with EqualityMixin { final AnimatedData? animated; + final ModifiersData? modifiers; - const Spec({this.animated}); + const Spec({this.animated, this.modifiers}); Type get type => T; @@ -31,8 +34,9 @@ abstract class Spec> with EqualityMixin { /// The [Self] type represents the concrete implementation of the attribute, while the [Value] type represents the resolvable value. abstract base class SpecAttribute extends StyledAttribute { final AnimatedDataDto? animated; + final ModifiersDataDto? modifiers; - const SpecAttribute({this.animated}); + const SpecAttribute({this.animated, this.modifiers}); Value resolve(MixData mix); @override diff --git a/packages/mix/lib/src/core/styled_widget.dart b/packages/mix/lib/src/core/styled_widget.dart index e029e5ee2..4eeaf462a 100644 --- a/packages/mix/lib/src/core/styled_widget.dart +++ b/packages/mix/lib/src/core/styled_widget.dart @@ -62,15 +62,13 @@ abstract class StyledWidget extends StatelessWidget { Widget applyModifiers(MixData mix, Widget child) { return mix.isAnimated ? RenderAnimatedModifiers( - mix: mix, - orderOfModifiers: orderOfModifiers, + modifiers: resolveModifierSpecs(orderOfModifiers, mix), duration: mix.animation!.duration, curve: mix.animation!.curve, child: child, ) : RenderModifiers( - mix: mix, - orderOfModifiers: orderOfModifiers, + modifiers: resolveModifierSpecs(orderOfModifiers, mix), child: child, ); } diff --git a/packages/mix/lib/src/modifiers/render_widget_modifier.dart b/packages/mix/lib/src/modifiers/render_widget_modifier.dart index b1249188e..d4cdbc527 100644 --- a/packages/mix/lib/src/modifiers/render_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/render_widget_modifier.dart @@ -1,10 +1,11 @@ // ignore_for_file: avoid-dynamic -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; import '../core/attributes_map.dart'; import '../core/factory/mix_data.dart'; import '../core/modifier.dart'; +import '../core/spec.dart'; import 'align_widget_modifier.dart'; import 'aspect_ratio_widget_modifier.dart'; import 'clip_widget_modifier.dart'; @@ -71,23 +72,19 @@ const _defaultOrder = [ class RenderModifiers extends StatelessWidget { const RenderModifiers({ - required this.mix, required this.child, + required this.modifiers, super.key, - required this.orderOfModifiers, }); - final MixData mix; final Widget child; - final List orderOfModifiers; + final Set> modifiers; @override Widget build(BuildContext context) { - final specs = resolveModifierSpecs(orderOfModifiers, mix); - var current = child; - for (final spec in specs) { + for (final spec in modifiers) { current = spec.build(current); } @@ -97,18 +94,17 @@ class RenderModifiers extends StatelessWidget { class RenderAnimatedModifiers extends ImplicitlyAnimatedWidget { const RenderAnimatedModifiers({ - required this.mix, + required this.modifiers, required this.child, - required this.orderOfModifiers, - super.key, required super.duration, + super.key, super.curve = Curves.linear, super.onEnd, }); - final MixData mix; final Widget child; - final List orderOfModifiers; + + final Set> modifiers; @override RenderAnimatedModifiersState createState() => RenderAnimatedModifiersState(); @@ -120,12 +116,9 @@ class RenderAnimatedModifiersState @override void forEachTween(TweenVisitor visitor) { - final specs = resolveModifierSpecs(widget.orderOfModifiers, widget.mix); - - for (final spec in specs) { + for (final spec in widget.modifiers) { final specType = spec.runtimeType; final previousSpec = _specs[specType]; - _specs[specType] = visitor( previousSpec, spec, @@ -155,6 +148,15 @@ Set resolveModifierSpecs( final modifiers = mix.whereType(); if (modifiers.isEmpty) return {}; + + return orderModifierSpecs(orderOfModifiers, mix, modifiers); +} + +Set orderModifierSpecs( + List orderOfModifiers, + MixData mix, + Iterable modifiers, +) { final modifierMap = AttributeMap(modifiers).toMap(); final listOfModifiers = { @@ -178,6 +180,36 @@ Set resolveModifierSpecs( return specs.toSet(); } +class RenderInlineModifiers extends StatelessWidget { + const RenderInlineModifiers({ + required this.mix, + required this.orderOfModifiers, + required this.child, + required this.spec, + super.key, + }); + + final MixData mix; + final Widget child; + final List orderOfModifiers; + final Spec spec; + + @override + Widget build(BuildContext context) { + return spec.isAnimated + ? RenderAnimatedModifiers( + modifiers: spec.modifiers?.value.toSet() ?? {}, + duration: spec.animated!.duration, + curve: spec.animated!.curve, + child: child, + ) + : RenderModifiers( + modifiers: spec.modifiers?.value.toSet() ?? {}, + child: child, + ); + } +} + class ModifierSpecTween extends Tween { /// Creates an [EdgeInsetsGeometry] tween. /// diff --git a/packages/mix/lib/src/specs/box/box_spec.dart b/packages/mix/lib/src/specs/box/box_spec.dart index ef3a31a13..115158fca 100644 --- a/packages/mix/lib/src/specs/box/box_spec.dart +++ b/packages/mix/lib/src/specs/box/box_spec.dart @@ -3,6 +3,10 @@ import 'package:flutter/widgets.dart'; import 'package:mix/mix.dart'; import 'package:mix_annotations/mix_annotations.dart'; +import '../../attributes/modifiers/modifiers_data.dart'; +import '../../attributes/modifiers/modifiers_data_dto.dart'; +import '../../attributes/modifiers/modifiers_util.dart'; + part 'box_spec.g.dart'; const _constraints = MixableUtility( @@ -96,6 +100,7 @@ final class BoxSpec extends Spec with _$BoxSpec { this.clipBehavior, this.width, this.height, + super.modifiers, super.animated, }); diff --git a/packages/mix/lib/src/specs/box/box_spec.g.dart b/packages/mix/lib/src/specs/box/box_spec.g.dart index f916dddc8..48f706228 100644 --- a/packages/mix/lib/src/specs/box/box_spec.g.dart +++ b/packages/mix/lib/src/specs/box/box_spec.g.dart @@ -45,6 +45,7 @@ base mixin _$BoxSpec on Spec { Clip? clipBehavior, double? width, double? height, + ModifiersData? modifiers, AnimatedData? animated, }) { return BoxSpec( @@ -59,6 +60,7 @@ base mixin _$BoxSpec on Spec { clipBehavior: clipBehavior ?? _$this.clipBehavior, width: width ?? _$this.width, height: height ?? _$this.height, + modifiers: modifiers ?? _$this.modifiers, animated: animated ?? _$this.animated, ); } @@ -81,7 +83,7 @@ base mixin _$BoxSpec on Spec { /// - [MixHelpers.lerpMatrix4] for [transform]. /// - [MixHelpers.lerpDouble] for [width] and [height]. - /// For [clipBehavior] and [animated], the interpolation is performed using a step function. + /// For [clipBehavior] and [modifiers] and [animated], the interpolation is performed using a step function. /// If [t] is less than 0.5, the value from the current [BoxSpec] is used. Otherwise, the value /// from the [other] [BoxSpec] is used. /// @@ -106,6 +108,7 @@ base mixin _$BoxSpec on Spec { clipBehavior: t < 0.5 ? _$this.clipBehavior : other.clipBehavior, width: MixHelpers.lerpDouble(_$this.width, other.width, t), height: MixHelpers.lerpDouble(_$this.height, other.height, t), + modifiers: t < 0.5 ? _$this.modifiers : other.modifiers, animated: t < 0.5 ? _$this.animated : other.animated, ); } @@ -127,6 +130,7 @@ base mixin _$BoxSpec on Spec { _$this.clipBehavior, _$this.width, _$this.height, + _$this.modifiers, _$this.animated, ]; @@ -165,6 +169,7 @@ final class BoxSpecAttribute extends SpecAttribute { this.clipBehavior, this.width, this.height, + super.modifiers, super.animated, }); @@ -190,6 +195,7 @@ final class BoxSpecAttribute extends SpecAttribute { clipBehavior: clipBehavior, width: width, height: height, + modifiers: modifiers?.resolve(mix), animated: animated?.resolve(mix) ?? mix.animation, ); } @@ -219,6 +225,7 @@ final class BoxSpecAttribute extends SpecAttribute { clipBehavior: other.clipBehavior ?? clipBehavior, width: other.width ?? width, height: other.height ?? height, + modifiers: modifiers?.merge(other.modifiers) ?? other.modifiers, animated: animated?.merge(other.animated) ?? other.animated, ); } @@ -240,6 +247,7 @@ final class BoxSpecAttribute extends SpecAttribute { clipBehavior, width, height, + modifiers, animated, ]; } @@ -340,6 +348,9 @@ base class BoxSpecUtility /// Utility for defining [BoxSpecAttribute.height] late final height = DoubleUtility((v) => only(height: v)); + /// Utility for defining [BoxSpecAttribute.modifiers] + late final modifiers = ModifiersDataUtility((v) => only(modifiers: v)); + /// Utility for defining [BoxSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); @@ -361,6 +372,7 @@ base class BoxSpecUtility Clip? clipBehavior, double? width, double? height, + ModifiersDataDto? modifiers, AnimatedDataDto? animated, }) { return builder(BoxSpecAttribute( @@ -375,6 +387,7 @@ base class BoxSpecUtility clipBehavior: clipBehavior, width: width, height: height, + modifiers: modifiers, animated: animated, )); } diff --git a/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart b/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart new file mode 100644 index 000000000..3e3a17e1b --- /dev/null +++ b/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart @@ -0,0 +1,119 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; +import 'package:mix/src/attributes/modifiers/modifiers_data.dart'; +import 'package:mix/src/attributes/modifiers/modifiers_data_dto.dart'; + +import '../../../helpers/testing_utils.dart'; + +void main() { + group('ModifiersDataDto', () { + test('should create an instance with default values', () { + const dto = ModifiersDataDto([]); + expect(dto.value, isEmpty); + }); + + test('should create an instance with provided values', () { + const modifier1 = TestModifierSpecAttribute(); + const modifier2 = TestModifierSpecAttribute(); + // ignore: equal_elements_in_set + const dto = ModifiersDataDto([modifier1, modifier2]); + expect(dto.value, contains(modifier1)); + }); + + test('should merge with another instance', () { + const dto1 = ModifiersDataDto([TestModifierSpecAttribute()]); + const dto2 = ModifiersDataDto([TestModifierSpecAttribute(2)]); + final merged = dto1.merge(dto2); + expect(merged.value, hasLength(1)); + expect(merged.value.first, const TestModifierSpecAttribute(2)); + }); + + test('should resolve to a ModifiersData instance', () { + const modifier = TestModifierSpecAttribute(); + const dto = ModifiersDataDto([modifier]); + final modifiersData = dto.resolve(EmptyMixData); + expect(modifiersData.value.length, 1); + expect(modifiersData.value.first, const TestModifierSpec()); + }); + + // test equality + test('should be equal to another instance', () { + const dto1 = ModifiersDataDto([TestModifierSpecAttribute()]); + const dto2 = ModifiersDataDto([TestModifierSpecAttribute()]); + expect(dto1, equals(dto2)); + }); + + test('should not be equal to another instance', () { + const dto1 = ModifiersDataDto([TestModifierSpecAttribute()]); + const dto2 = ModifiersDataDto([TestModifierSpecAttribute(2)]); + expect(dto1, isNot(equals(dto2))); + }); + }); + + group('ModifiersData', () { + test('should create an instance with provided values', () { + const modifier1 = TestModifierSpec(); + const modifier2 = TestModifierSpec(); + // ignore: equal_elements_in_set + const modifiersData = ModifiersData([modifier1, modifier2]); + expect(modifiersData.value, contains(modifier1)); + }); + + // equality + test('should be equal to another instance', () { + const modifiersData1 = ModifiersData([TestModifierSpec()]); + const modifiersData2 = ModifiersData([TestModifierSpec()]); + expect(modifiersData1, equals(modifiersData2)); + }); + + test('should not be equal to another instance', () { + const modifiersData1 = ModifiersData([TestModifierSpec()]); + const modifiersData2 = ModifiersData([]); + expect(modifiersData1, isNot(equals(modifiersData2))); + }); + }); +} + +final class TestModifierSpec extends WidgetModifierSpec { + const TestModifierSpec([this.value = 1]); + final int value; + @override + Widget build(Widget child) { + throw UnimplementedError(); + } + + @override + TestModifierSpec lerp(TestModifierSpec other, double t) { + return this; + } + + @override + TestModifierSpec copyWith() { + throw UnimplementedError(); + } + + @override + List get props => [value]; +} + +final class TestModifierSpecAttribute extends WidgetModifierAttribute< + TestModifierSpecAttribute, TestModifierSpec> { + const TestModifierSpecAttribute([this.value = 1]); + + final int value; + + @override + List get props => [value]; + + @override + SpecAttribute merge( + covariant SpecAttribute? other) { + return other ?? this; + } + + @override + TestModifierSpec resolve(MixData mix) { + return const TestModifierSpec(); + } +} diff --git a/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart b/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart index c89d30df7..6446669da 100644 --- a/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart +++ b/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart @@ -20,8 +20,7 @@ void main() { testWidgets('Renders modifiers in the correct order', (tester) async { await tester.pumpMaterialApp( RenderModifiers( - mix: mixData, - orderOfModifiers: const [], + modifiers: resolveModifierSpecs(const [], mixData), child: const Text('child'), ), ); @@ -83,8 +82,7 @@ void main() { await tester.pumpMaterialApp( RenderModifiers( - mix: mixData, - orderOfModifiers: const [], + modifiers: resolveModifierSpecs(const [], mixData), child: const Text('child'), ), ); @@ -98,8 +96,7 @@ void main() { await tester.pumpMaterialApp( RenderModifiers( - mix: mixData, - orderOfModifiers: const [], + modifiers: resolveModifierSpecs(const [], mixData), child: const Text('child'), ), ); @@ -113,14 +110,13 @@ void main() { (tester) async { await tester.pumpMaterialApp( RenderModifiers( - mix: mixData, - orderOfModifiers: const [ + modifiers: resolveModifierSpecs([ ClipOvalModifierAttribute, AspectRatioModifierAttribute, TransformModifierAttribute, OpacityModifierAttribute, VisibilityModifierAttribute, - ], + ], mixData), child: const Text('child'), ), ); @@ -183,11 +179,9 @@ void main() { (tester) async { await tester.pumpMaterialApp( RenderModifiers( - mix: mixData, - orderOfModifiers: const [ - ClipOvalModifierAttribute, - AspectRatioModifierAttribute - ], + modifiers: resolveModifierSpecs( + [ClipOvalModifierAttribute, AspectRatioModifierAttribute], + mixData), child: const Text('child'), ), ); @@ -259,8 +253,7 @@ void main() { await tester.pumpMaterialApp( RenderAnimatedModifiers( - mix: mixData, - orderOfModifiers: const [], + modifiers: resolveModifierSpecs(const [], mixData), duration: const Duration(milliseconds: 300), child: const Text('child'), ), diff --git a/packages/mix/test/src/modifiers/widget_modifiers_util_test.dart b/packages/mix/test/src/modifiers/widget_modifiers_util_test.dart index 1cea84330..d7ec04819 100644 --- a/packages/mix/test/src/modifiers/widget_modifiers_util_test.dart +++ b/packages/mix/test/src/modifiers/widget_modifiers_util_test.dart @@ -430,10 +430,12 @@ class _TestableRenderModifier extends StatelessWidget { @override Widget build(BuildContext context) { return RenderModifiers( - orderOfModifiers: const [], - mix: MixData.create( - context, - style, + modifiers: resolveModifierSpecs( + const [], + MixData.create( + context, + style, + ), ), child: Container(), ); diff --git a/packages/mix/test/src/specs/box/box_attribute_test.dart b/packages/mix/test/src/specs/box/box_attribute_test.dart index ffeb74d33..b4d85613d 100644 --- a/packages/mix/test/src/specs/box/box_attribute_test.dart +++ b/packages/mix/test/src/specs/box/box_attribute_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/attributes/modifiers/modifiers_data_dto.dart'; import '../../../helpers/testing_utils.dart'; @@ -22,6 +23,10 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, + modifiers: const ModifiersDataDto([ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 10), + ]), ); expect(containerSpecAttribute.alignment, Alignment.center); @@ -47,26 +52,35 @@ void main() { ); expect(containerSpecAttribute.transform, Matrix4.identity()); expect(containerSpecAttribute.width, 100); + expect( + containerSpecAttribute.modifiers, + const ModifiersDataDto([ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 10), + ])); }); // resolve() test('resolve() returns correct instance', () { final containerSpecAttribute = BoxSpecAttribute( - alignment: Alignment.center, - padding: SpacingDto.only(top: 20, bottom: 20, left: 20, right: 20), - margin: SpacingDto.only( - top: 10, - bottom: 10, - left: 10, - right: 10, - ), - constraints: const BoxConstraintsDto(maxHeight: 100), - decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), - transform: Matrix4.identity(), - clipBehavior: Clip.antiAlias, - width: 100, - height: 100, - ); + alignment: Alignment.center, + padding: SpacingDto.only(top: 20, bottom: 20, left: 20, right: 20), + margin: SpacingDto.only( + top: 10, + bottom: 10, + left: 10, + right: 10, + ), + constraints: const BoxConstraintsDto(maxHeight: 100), + decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), + transform: Matrix4.identity(), + clipBehavior: Clip.antiAlias, + width: 100, + height: 100, + modifiers: const ModifiersDataDto([ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 10), + ])); final containerSpec = containerSpecAttribute.resolve(EmptyMixData); @@ -90,26 +104,33 @@ void main() { ); expect(containerSpec.transform, Matrix4.identity()); expect(containerSpec.width, 100); + expect(containerSpec.modifiers!.value, [ + const OpacityModifierSpec(0.5), + const SizedBoxModifierSpec(height: 10, width: 10), + ]); }); // merge() test('merge() returns correct instance', () { final containerSpecAttribute = BoxSpecAttribute( - alignment: Alignment.center, - padding: SpacingDto.only(top: 20, bottom: 20, left: 20, right: 20), - margin: SpacingDto.only( - top: 10, - bottom: 10, - left: 10, - right: 10, - ), - constraints: const BoxConstraintsDto(maxHeight: 100), - decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), - transform: Matrix4.identity(), - clipBehavior: Clip.antiAlias, - width: 100, - height: 100, - ); + alignment: Alignment.center, + padding: SpacingDto.only(top: 20, bottom: 20, left: 20, right: 20), + margin: SpacingDto.only( + top: 10, + bottom: 10, + left: 10, + right: 10, + ), + constraints: const BoxConstraintsDto(maxHeight: 100), + decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), + transform: Matrix4.identity(), + clipBehavior: Clip.antiAlias, + width: 100, + height: 100, + modifiers: const ModifiersDataDto([ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 10), + ])); final mergedBoxSpecAttribute = containerSpecAttribute.merge( BoxSpecAttribute( @@ -127,6 +148,9 @@ void main() { clipBehavior: Clip.antiAliasWithSaveLayer, width: 200, height: 200, + modifiers: const ModifiersDataDto([ + SizedBoxModifierAttribute(width: 20), + ]), ), ); @@ -153,26 +177,35 @@ void main() { ); expect(mergedBoxSpecAttribute.transform, Matrix4.identity()); expect(mergedBoxSpecAttribute.width, 200); + expect( + mergedBoxSpecAttribute.modifiers, + const ModifiersDataDto([ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 20), + ])); }); // equality test('equality', () { final containerSpecAttribute = BoxSpecAttribute( - alignment: Alignment.center, - padding: SpacingDto.only(top: 20, bottom: 20, left: 20, right: 20), - margin: SpacingDto.only( - top: 10, - bottom: 10, - left: 10, - right: 10, - ), - constraints: const BoxConstraintsDto(maxHeight: 100), - decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), - transform: Matrix4.identity(), - clipBehavior: Clip.antiAlias, - width: 100, - height: 100, - ); + alignment: Alignment.center, + padding: SpacingDto.only(top: 20, bottom: 20, left: 20, right: 20), + margin: SpacingDto.only( + top: 10, + bottom: 10, + left: 10, + right: 10, + ), + constraints: const BoxConstraintsDto(maxHeight: 100), + decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), + transform: Matrix4.identity(), + clipBehavior: Clip.antiAlias, + width: 100, + height: 100, + modifiers: const ModifiersDataDto([ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 10), + ])); expect( containerSpecAttribute, @@ -192,6 +225,12 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, + modifiers: const ModifiersDataDto( + [ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 10), + ], + ), ), ), ); @@ -240,6 +279,12 @@ void main() { clipBehavior: Clip.antiAliasWithSaveLayer, width: 200, height: 200, + modifiers: const ModifiersDataDto( + [ + OpacityModifierAttribute(0.4), + SizedBoxModifierAttribute(height: 20, width: 10), + ], + ), ), ), ), diff --git a/packages/mix/test/src/specs/box/box_spec_test.dart b/packages/mix/test/src/specs/box/box_spec_test.dart index 46e2267c5..b36f3aaad 100644 --- a/packages/mix/test/src/specs/box/box_spec_test.dart +++ b/packages/mix/test/src/specs/box/box_spec_test.dart @@ -3,6 +3,8 @@ 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/modifiers/modifiers_data.dart'; +import 'package:mix/src/attributes/modifiers/modifiers_data_dto.dart'; import '../../../helpers/testing_utils.dart'; @@ -21,6 +23,10 @@ void main() { decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), transform: Matrix4.translationValues(10.0, 10.0, 0.0), clipBehavior: Clip.antiAlias, + modifiers: const ModifiersDataDto([ + OpacityModifierAttribute(1), + SizedBoxModifierAttribute(height: 10, width: 10), + ]), width: 300, height: 200, ), @@ -39,6 +45,10 @@ void main() { expect(spec.decoration, const BoxDecoration(color: Colors.blue)); expect(spec.transform, Matrix4.translationValues(10.0, 10.0, 0.0)); + expect(spec.modifiers!.value, [ + const OpacityModifierSpec(1), + const SizedBoxModifierSpec(height: 10, width: 10), + ]); expect(spec.clipBehavior, Clip.antiAlias); }); @@ -55,9 +65,19 @@ void main() { transformAlignment: Alignment.center, width: 300, height: 200, + modifiers: const ModifiersData([ + OpacityModifierSpec(0.5), + SizedBoxModifierSpec(height: 10, width: 10), + ]), ); - final copiedSpec = spec.copyWith(width: 250.0, height: 150.0); + final copiedSpec = spec.copyWith( + width: 250.0, + height: 150.0, + modifiers: const ModifiersData([ + OpacityModifierSpec(1), + ]), + ); expect(copiedSpec.alignment, Alignment.center); expect(copiedSpec.padding, const EdgeInsets.all(16.0)); @@ -75,6 +95,13 @@ void main() { expect(copiedSpec.transform, Matrix4.translationValues(10.0, 10.0, 0.0)); expect(copiedSpec.clipBehavior, Clip.antiAlias); expect(copiedSpec.width, 250.0); + + expect( + copiedSpec.modifiers!.value, + const ModifiersData( + [OpacityModifierSpec(1)], + ).value, + ); expect(copiedSpec.height, 150.0); }); @@ -179,6 +206,10 @@ void main() { clipBehavior: Clip.none, width: 300, height: 200, + modifiers: const ModifiersData([ + OpacityModifierSpec(0.5), + SizedBoxModifierSpec(height: 10, width: 10), + ]), ); final spec2 = BoxSpec( @@ -193,6 +224,10 @@ void main() { clipBehavior: Clip.none, width: 300, height: 200, + modifiers: const ModifiersData([ + OpacityModifierSpec(0.5), + SizedBoxModifierSpec(height: 10, width: 10), + ]), ); expect(spec1, spec2); @@ -217,6 +252,10 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, + modifiers: const ModifiersDataDto([ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 10), + ]), ); final mergedBoxSpecAttribute = containerSpecAttribute.merge( @@ -237,6 +276,9 @@ void main() { clipBehavior: Clip.antiAliasWithSaveLayer, width: 200, height: 200, + modifiers: const ModifiersDataDto([ + SizedBoxModifierAttribute(width: 100), + ]), ), ); @@ -266,6 +308,13 @@ void main() { ); expect(mergedBoxSpecAttribute.transform, Matrix4.identity()); expect(mergedBoxSpecAttribute.width, 200); + expect( + mergedBoxSpecAttribute.modifiers!.value, + [ + const OpacityModifierAttribute(0.5), + const SizedBoxModifierAttribute(height: 10, width: 100), + ], + ); }); }); } diff --git a/packages/mix_generator/lib/src/helpers/type_ref_repository.dart b/packages/mix_generator/lib/src/helpers/type_ref_repository.dart index b796dbd2d..ea4242092 100644 --- a/packages/mix_generator/lib/src/helpers/type_ref_repository.dart +++ b/packages/mix_generator/lib/src/helpers/type_ref_repository.dart @@ -23,6 +23,7 @@ class TypeRefRepository { 'BoxConstraints': 'BoxConstraintsDto', 'Decoration': 'DecorationDto', 'Color': 'ColorDto', + 'ModifiersData': 'ModifiersDataDto', 'AnimatedData': 'AnimatedDataDto', 'TextStyle': 'TextStyleDto', 'ShapeBorder': 'ShapeBorderDto', From ab43fb2d92721def77a86d0dd76c9626f2ff5af7 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Thu, 27 Jun 2024 12:39:58 -0300 Subject: [PATCH 11/27] add modifiers in specs --- examples/todo_list/lib/main.dart | 2 +- .../lib/style/components/checkbox.dart | 26 +++----- .../mix/lib/src/attributes/attributes.dart | 3 + .../modifiers/modifiers_data_dto.dart | 29 --------- ...s_data.dart => widget_modifiers_data.dart} | 4 +- .../modifiers/widget_modifiers_data_dto.dart | 29 +++++++++ ...s_util.dart => widget_modifiers_util.dart} | 13 ++-- packages/mix/lib/src/core/spec.dart | 8 +-- .../src/modifiers/render_widget_modifier.dart | 54 ++++++++++++++-- packages/mix/lib/src/specs/box/box_spec.dart | 12 ++-- .../mix/lib/src/specs/box/box_spec.g.dart | 6 +- .../mix/lib/src/specs/box/box_widget.dart | 50 ++++++++------- .../mix/lib/src/specs/flex/flex_spec.dart | 1 + .../mix/lib/src/specs/flex/flex_spec.g.dart | 15 ++++- .../mix/lib/src/specs/flex/flex_widget.dart | 15 ++++- .../mix/lib/src/specs/icon/icon_spec.dart | 1 + .../mix/lib/src/specs/icon/icon_spec.g.dart | 15 ++++- .../mix/lib/src/specs/icon/icon_widget.dart | 36 ++++++----- .../mix/lib/src/specs/image/image_spec.dart | 1 + .../mix/lib/src/specs/image/image_spec.g.dart | 15 ++++- .../mix/lib/src/specs/image/image_widget.dart | 49 ++++++++------- .../mix/lib/src/specs/stack/stack_spec.dart | 1 + .../mix/lib/src/specs/stack/stack_spec.g.dart | 15 ++++- .../mix/lib/src/specs/stack/stack_widget.dart | 25 +++++--- .../mix/lib/src/specs/text/text_spec.dart | 1 + .../mix/lib/src/specs/text/text_spec.g.dart | 15 ++++- .../mix/lib/src/specs/text/text_widget.dart | 40 ++++++------ .../render_widget_modifier_test.dart | 61 +++++++++++++++++++ .../modifiers/modifiers_data_test.dart | 32 +++++----- .../src/specs/box/box_attribute_test.dart | 20 +++--- .../mix/test/src/specs/box/box_spec_test.dart | 20 +++--- .../test/src/specs/image/image_spec_test.dart | 3 +- .../src/specs/stack/stack_attribute_test.dart | 2 +- .../mix/test/src/widgets/box/box_test.dart | 2 +- .../test/src/widgets/styled_icon_test.dart | 2 +- .../lib/src/helpers/type_ref_repository.dart | 3 +- 36 files changed, 423 insertions(+), 203 deletions(-) delete mode 100644 packages/mix/lib/src/attributes/modifiers/modifiers_data_dto.dart rename packages/mix/lib/src/attributes/modifiers/{modifiers_data.dart => widget_modifiers_data.dart} (62%) create mode 100644 packages/mix/lib/src/attributes/modifiers/widget_modifiers_data_dto.dart rename packages/mix/lib/src/attributes/modifiers/{modifiers_util.dart => widget_modifiers_util.dart} (79%) create mode 100644 packages/mix/test/modifiers/render_widget_modifier_test.dart diff --git a/examples/todo_list/lib/main.dart b/examples/todo_list/lib/main.dart index 0a4e4df2e..36639870f 100644 --- a/examples/todo_list/lib/main.dart +++ b/examples/todo_list/lib/main.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:mix/mix.dart'; -import 'package:todo_list/pages/todo_list_page.dart'; +import 'pages/todo_list_page.dart'; import 'style/design_tokens.dart'; void main() { diff --git a/examples/todo_list/lib/style/components/checkbox.dart b/examples/todo_list/lib/style/components/checkbox.dart index d3c47fc54..aae58f560 100644 --- a/examples/todo_list/lib/style/components/checkbox.dart +++ b/examples/todo_list/lib/style/components/checkbox.dart @@ -30,7 +30,15 @@ class TodoCheckbox extends StatelessWidget { $box.borderRadius(3), scaleEffect(), outlinePattern(), + $icon.size(16), + $icon.color.ref($token.color.surface), + $icon.modifiers.opacity(0), + $icon.modifiers.padding.top(5), + $icon.modifiers.scale(0.5), _CheckboxVariant.checked( + $icon.modifiers.padding.top(0), + $icon.modifiers.scale(2), + $icon.modifiers.opacity(1), $box.color.ref($token.color.primary), $box.border.color.ref($token.color.primary), ), @@ -41,24 +49,8 @@ class TodoCheckbox extends StatelessWidget { .animate( duration: const Duration(milliseconds: 150), ), - child: StyledIcon( + child: const StyledIcon( Icons.check, - style: Style( - $icon.weight(16), - $icon.color.ref($token.color.surface), - $with.opacity(0), - $with.padding.top(5), - _CheckboxVariant.checked( - $with.padding.top(0), - $with.opacity(1), - ), - ) - .applyVariant( - value ? _CheckboxVariant.checked : _CheckboxVariant.unchecked, - ) - .animate( - duration: const Duration(milliseconds: 300), - ), ), ); } diff --git a/packages/mix/lib/src/attributes/attributes.dart b/packages/mix/lib/src/attributes/attributes.dart index 1b754a305..b0ce4de3f 100644 --- a/packages/mix/lib/src/attributes/attributes.dart +++ b/packages/mix/lib/src/attributes/attributes.dart @@ -24,6 +24,9 @@ export 'enum/enum_util.dart'; export 'gap/gap_util.dart'; export 'gap/spacing_side_dto.dart'; export 'gradient/gradient_dto.dart'; +export 'modifiers/widget_modifiers_data.dart'; +export 'modifiers/widget_modifiers_data_dto.dart'; +export 'modifiers/widget_modifiers_util.dart'; export 'nested_style/nested_style_attribute.dart'; export 'nested_style/nested_style_util.dart'; export 'scalars/scalar_util.dart'; diff --git a/packages/mix/lib/src/attributes/modifiers/modifiers_data_dto.dart b/packages/mix/lib/src/attributes/modifiers/modifiers_data_dto.dart deleted file mode 100644 index ff24be7ef..000000000 --- a/packages/mix/lib/src/attributes/modifiers/modifiers_data_dto.dart +++ /dev/null @@ -1,29 +0,0 @@ -import '../../core/core.dart'; -import 'modifiers_data.dart'; - -class ModifiersDataDto extends Dto { - final List value; - - const ModifiersDataDto(this.value); - - @override - ModifiersDataDto merge(ModifiersDataDto? other) { - if (other == null) return this; - final thisMap = AttributeMap(value); - final otherMap = AttributeMap(other.value); - final mergedMap = thisMap.merge(otherMap).values; - - return ModifiersDataDto(mergedMap); - } - - @override - ModifiersData resolve(MixData mix) { - return ModifiersData(value.map((e) => e.resolve(mix)).toList()); - } - - @override - ModifiersData get defaultValue => const ModifiersData([]); - - @override - List get props => [value]; -} diff --git a/packages/mix/lib/src/attributes/modifiers/modifiers_data.dart b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_data.dart similarity index 62% rename from packages/mix/lib/src/attributes/modifiers/modifiers_data.dart rename to packages/mix/lib/src/attributes/modifiers/widget_modifiers_data.dart index d151ce7d6..deb4f86fe 100644 --- a/packages/mix/lib/src/attributes/modifiers/modifiers_data.dart +++ b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_data.dart @@ -1,7 +1,7 @@ import '../../core/modifier.dart'; -class ModifiersData { +class WidgetModifiersData { // ignore: avoid-dynamic final List> value; - const ModifiersData(this.value); + const WidgetModifiersData(this.value); } diff --git a/packages/mix/lib/src/attributes/modifiers/widget_modifiers_data_dto.dart b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_data_dto.dart new file mode 100644 index 000000000..628411454 --- /dev/null +++ b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_data_dto.dart @@ -0,0 +1,29 @@ +import '../../core/core.dart'; +import 'widget_modifiers_data.dart'; + +class WidgetModifiersDataDto extends Dto { + final List value; + + const WidgetModifiersDataDto(this.value); + + @override + WidgetModifiersDataDto merge(WidgetModifiersDataDto? other) { + if (other == null) return this; + final thisMap = AttributeMap(value); + final otherMap = AttributeMap(other.value); + final mergedMap = thisMap.merge(otherMap).values; + + return WidgetModifiersDataDto(mergedMap); + } + + @override + WidgetModifiersData resolve(MixData mix) { + return WidgetModifiersData(value.map((e) => e.resolve(mix)).toList()); + } + + @override + WidgetModifiersData get defaultValue => const WidgetModifiersData([]); + + @override + List get props => [value]; +} diff --git a/packages/mix/lib/src/attributes/modifiers/modifiers_util.dart b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_util.dart similarity index 79% rename from packages/mix/lib/src/attributes/modifiers/modifiers_util.dart rename to packages/mix/lib/src/attributes/modifiers/widget_modifiers_util.dart index 83902103e..f61c00916 100644 --- a/packages/mix/lib/src/attributes/modifiers/modifiers_util.dart +++ b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_util.dart @@ -1,11 +1,12 @@ import '../../core/core.dart'; import '../../modifiers/modifiers.dart'; import '../spacing/spacing_util.dart'; -import 'modifiers_data_dto.dart'; +import 'widget_modifiers_data_dto.dart'; -final class ModifiersDataUtility - extends MixUtility { - late final add = WithModifierUtility((v) => builder(ModifiersDataDto([v]))); +final class ModifierUtility + extends MixUtility { + late final add = + WithModifierUtility((v) => builder(WidgetModifiersDataDto([v]))); late final intrinsicWidth = IntrinsicWidthWidgetUtility(only); late final intrinsicHeight = IntrinsicHeightWidgetUtility(only); @@ -30,9 +31,9 @@ final class ModifiersDataUtility late final sizedBox = SizedBoxModifierUtility(only); late final padding = SpacingUtility(PaddingModifierUtility(only)); - ModifiersDataUtility(super.builder); + ModifierUtility(super.builder); T only(WidgetModifierAttribute attribute) { - return builder(ModifiersDataDto([attribute])); + return builder(WidgetModifiersDataDto([attribute])); } } diff --git a/packages/mix/lib/src/core/spec.dart b/packages/mix/lib/src/core/spec.dart index 989fb0149..d242e8f2c 100644 --- a/packages/mix/lib/src/core/spec.dart +++ b/packages/mix/lib/src/core/spec.dart @@ -2,8 +2,8 @@ import 'package:flutter/foundation.dart'; import '../attributes/animated/animated_data.dart'; import '../attributes/animated/animated_data_dto.dart'; -import '../attributes/modifiers/modifiers_data.dart'; -import '../attributes/modifiers/modifiers_data_dto.dart'; +import '../attributes/modifiers/widget_modifiers_data.dart'; +import '../attributes/modifiers/widget_modifiers_data_dto.dart'; import '../internal/compare_mixin.dart'; import 'attribute.dart'; import 'factory/mix_data.dart'; @@ -12,7 +12,7 @@ import 'utility.dart'; @immutable abstract class Spec> with EqualityMixin { final AnimatedData? animated; - final ModifiersData? modifiers; + final WidgetModifiersData? modifiers; const Spec({this.animated, this.modifiers}); @@ -34,7 +34,7 @@ abstract class Spec> with EqualityMixin { /// The [Self] type represents the concrete implementation of the attribute, while the [Value] type represents the resolvable value. abstract base class SpecAttribute extends StyledAttribute { final AnimatedDataDto? animated; - final ModifiersDataDto? modifiers; + final WidgetModifiersDataDto? modifiers; const SpecAttribute({this.animated, this.modifiers}); diff --git a/packages/mix/lib/src/modifiers/render_widget_modifier.dart b/packages/mix/lib/src/modifiers/render_widget_modifier.dart index d4cdbc527..a90637039 100644 --- a/packages/mix/lib/src/modifiers/render_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/render_widget_modifier.dart @@ -70,6 +70,23 @@ const _defaultOrder = [ OpacityModifierAttribute, ]; +const _defaultOrderSpecs = [ + VisibilityModifierSpec, + SizedBoxModifierSpec, + FractionallySizedBoxModifierSpec, + AlignModifierSpec, + IntrinsicHeightModifierSpec, + IntrinsicWidthModifierSpec, + AspectRatioModifierSpec, + TransformModifierSpec, + ClipOvalModifierSpec, + ClipRRectModifierSpec, + ClipPathModifierSpec, + ClipTriangleModifierSpec, + ClipRectModifierSpec, + OpacityModifierSpec, +]; + class RenderModifiers extends StatelessWidget { const RenderModifiers({ required this.child, @@ -152,6 +169,31 @@ Set resolveModifierSpecs( return orderModifierSpecs(orderOfModifiers, mix, modifiers); } +Set> orderSpecs( + List orderOfModifiers, [ + Set> modifiers = const {}, +]) { + final listOfModifiers = ({ + // Prioritize the order of modifiers provided by the user. + ...orderOfModifiers, + // Add the default order of modifiers. + ..._defaultOrderSpecs, + // Add any remaining modifiers that were not included in the order. + ...modifiers.map((e) => e.type), + }).toList(); + + final specs = >{}; + + for (final modifierType in listOfModifiers) { + // Resolve the modifier and add it to the list of specs. + final modifier = modifiers.where((e) => e.type == modifierType).firstOrNull; + if (modifier == null) continue; + specs.add(modifier as WidgetModifierSpec>); + } + + return specs; +} + Set orderModifierSpecs( List orderOfModifiers, MixData mix, @@ -182,14 +224,12 @@ Set orderModifierSpecs( class RenderInlineModifiers extends StatelessWidget { const RenderInlineModifiers({ - required this.mix, required this.orderOfModifiers, required this.child, required this.spec, super.key, }); - final MixData mix; final Widget child; final List orderOfModifiers; final Spec spec; @@ -198,13 +238,19 @@ class RenderInlineModifiers extends StatelessWidget { Widget build(BuildContext context) { return spec.isAnimated ? RenderAnimatedModifiers( - modifiers: spec.modifiers?.value.toSet() ?? {}, + modifiers: orderSpecs( + orderOfModifiers, + spec.modifiers?.value.toSet() ?? {}, + ), duration: spec.animated!.duration, curve: spec.animated!.curve, child: child, ) : RenderModifiers( - modifiers: spec.modifiers?.value.toSet() ?? {}, + modifiers: orderSpecs( + orderOfModifiers, + spec.modifiers?.value.toSet() ?? {}, + ), child: child, ); } diff --git a/packages/mix/lib/src/specs/box/box_spec.dart b/packages/mix/lib/src/specs/box/box_spec.dart index 115158fca..34d50703e 100644 --- a/packages/mix/lib/src/specs/box/box_spec.dart +++ b/packages/mix/lib/src/specs/box/box_spec.dart @@ -3,10 +3,6 @@ import 'package:flutter/widgets.dart'; import 'package:mix/mix.dart'; import 'package:mix_annotations/mix_annotations.dart'; -import '../../attributes/modifiers/modifiers_data.dart'; -import '../../attributes/modifiers/modifiers_data_dto.dart'; -import '../../attributes/modifiers/modifiers_util.dart'; - part 'box_spec.g.dart'; const _constraints = MixableUtility( @@ -104,7 +100,7 @@ final class BoxSpec extends Spec with _$BoxSpec { super.animated, }); - Widget call({Widget? child}) { + Widget call({Widget? child, List orderOfModifiers = const []}) { return isAnimated ? AnimatedBoxSpecWidget( spec: this, @@ -112,6 +108,10 @@ final class BoxSpec extends Spec with _$BoxSpec { curve: animated!.curve, child: child, ) - : BoxSpecWidget(spec: this, child: child); + : BoxSpecWidget( + spec: this, + orderOfModifiers: orderOfModifiers, + child: child, + ); } } diff --git a/packages/mix/lib/src/specs/box/box_spec.g.dart b/packages/mix/lib/src/specs/box/box_spec.g.dart index 48f706228..c6b4521a8 100644 --- a/packages/mix/lib/src/specs/box/box_spec.g.dart +++ b/packages/mix/lib/src/specs/box/box_spec.g.dart @@ -45,7 +45,7 @@ base mixin _$BoxSpec on Spec { Clip? clipBehavior, double? width, double? height, - ModifiersData? modifiers, + WidgetModifiersData? modifiers, AnimatedData? animated, }) { return BoxSpec( @@ -349,7 +349,7 @@ base class BoxSpecUtility late final height = DoubleUtility((v) => only(height: v)); /// Utility for defining [BoxSpecAttribute.modifiers] - late final modifiers = ModifiersDataUtility((v) => only(modifiers: v)); + late final modifiers = ModifierUtility((v) => only(modifiers: v)); /// Utility for defining [BoxSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); @@ -372,7 +372,7 @@ base class BoxSpecUtility Clip? clipBehavior, double? width, double? height, - ModifiersDataDto? modifiers, + WidgetModifiersDataDto? modifiers, AnimatedDataDto? animated, }) { return builder(BoxSpecAttribute( diff --git a/packages/mix/lib/src/specs/box/box_widget.dart b/packages/mix/lib/src/specs/box/box_widget.dart index dfa5787e7..4344276cf 100644 --- a/packages/mix/lib/src/specs/box/box_widget.dart +++ b/packages/mix/lib/src/specs/box/box_widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/widgets.dart'; import '../../core/factory/mix_provider.dart'; import '../../core/styled_widget.dart'; +import '../../modifiers/render_widget_modifier.dart'; import 'box_spec.dart'; /// A [Container] equivalent widget for applying styles using Mix. @@ -53,39 +54,42 @@ class Box extends StyledWidget { return withMix(context, (context) { final spec = BoxSpec.of(context); - return spec.isAnimated - ? AnimatedBoxSpecWidget( - spec: spec, - duration: spec.animated!.duration, - curve: spec.animated!.curve, - child: child, - ) - : BoxSpecWidget(spec: spec, child: child); + return spec(child: child); }); } } class BoxSpecWidget extends StatelessWidget { - const BoxSpecWidget({required this.spec, super.key, this.child}); + const BoxSpecWidget({ + required this.spec, + super.key, + this.child, + this.orderOfModifiers = const [], + }); final Widget? child; final BoxSpec? spec; + final List orderOfModifiers; @override Widget build(BuildContext context) { - return Container( - alignment: spec?.alignment, - padding: spec?.padding, - decoration: spec?.decoration, - foregroundDecoration: spec?.foregroundDecoration, - width: spec?.width, - height: spec?.height, - constraints: spec?.constraints, - margin: spec?.margin, - transform: spec?.transform, - transformAlignment: spec?.transformAlignment, - clipBehavior: spec?.clipBehavior ?? Clip.none, - child: child, + return RenderInlineModifiers( + orderOfModifiers: orderOfModifiers, + spec: spec ?? const BoxSpec(), + child: Container( + alignment: spec?.alignment, + padding: spec?.padding, + decoration: spec?.decoration, + foregroundDecoration: spec?.foregroundDecoration, + width: spec?.width, + height: spec?.height, + constraints: spec?.constraints, + margin: spec?.margin, + transform: spec?.transform, + transformAlignment: spec?.transformAlignment, + clipBehavior: spec?.clipBehavior ?? Clip.none, + child: child, + ), ); } } @@ -98,10 +102,12 @@ class AnimatedBoxSpecWidget extends ImplicitlyAnimatedWidget { required super.duration, super.curve = Curves.linear, super.onEnd, + this.orderOfModifiers = const [], }); final Widget? child; final BoxSpec spec; + final List orderOfModifiers; @override AnimatedWidgetBaseState createState() => diff --git a/packages/mix/lib/src/specs/flex/flex_spec.dart b/packages/mix/lib/src/specs/flex/flex_spec.dart index 6a4e2bc02..74377de13 100644 --- a/packages/mix/lib/src/specs/flex/flex_spec.dart +++ b/packages/mix/lib/src/specs/flex/flex_spec.dart @@ -46,6 +46,7 @@ final class FlexSpec extends Spec with _$FlexSpec { this.clipBehavior, this.gap, super.animated, + super.modifiers, }); Widget call({List children = const [], required Axis direction}) { diff --git a/packages/mix/lib/src/specs/flex/flex_spec.g.dart b/packages/mix/lib/src/specs/flex/flex_spec.g.dart index 2fb8d07dd..9652105f9 100644 --- a/packages/mix/lib/src/specs/flex/flex_spec.g.dart +++ b/packages/mix/lib/src/specs/flex/flex_spec.g.dart @@ -45,6 +45,7 @@ base mixin _$FlexSpec on Spec { Clip? clipBehavior, double? gap, AnimatedData? animated, + WidgetModifiersData? modifiers, }) { return FlexSpec( crossAxisAlignment: crossAxisAlignment ?? _$this.crossAxisAlignment, @@ -57,6 +58,7 @@ base mixin _$FlexSpec on Spec { clipBehavior: clipBehavior ?? _$this.clipBehavior, gap: gap ?? _$this.gap, animated: animated ?? _$this.animated, + modifiers: modifiers ?? _$this.modifiers, ); } @@ -73,7 +75,7 @@ base mixin _$FlexSpec on Spec { /// /// - [MixHelpers.lerpDouble] for [gap]. - /// For [crossAxisAlignment] and [mainAxisAlignment] and [mainAxisSize] and [verticalDirection] and [direction] and [textDirection] and [textBaseline] and [clipBehavior] and [animated], the interpolation is performed using a step function. + /// For [crossAxisAlignment] and [mainAxisAlignment] and [mainAxisSize] and [verticalDirection] and [direction] and [textDirection] and [textBaseline] and [clipBehavior] and [animated] and [modifiers], the interpolation is performed using a step function. /// If [t] is less than 0.5, the value from the current [FlexSpec] is used. Otherwise, the value /// from the [other] [FlexSpec] is used. /// @@ -97,6 +99,7 @@ base mixin _$FlexSpec on Spec { clipBehavior: t < 0.5 ? _$this.clipBehavior : other.clipBehavior, gap: MixHelpers.lerpDouble(_$this.gap, other.gap, t), animated: t < 0.5 ? _$this.animated : other.animated, + modifiers: t < 0.5 ? _$this.modifiers : other.modifiers, ); } @@ -116,6 +119,7 @@ base mixin _$FlexSpec on Spec { _$this.clipBehavior, _$this.gap, _$this.animated, + _$this.modifiers, ]; FlexSpec get _$this => this as FlexSpec; @@ -150,6 +154,7 @@ final class FlexSpecAttribute extends SpecAttribute { this.clipBehavior, this.gap, super.animated, + super.modifiers, }); /// Resolves to [FlexSpec] using the provided [MixData]. @@ -173,6 +178,7 @@ final class FlexSpecAttribute extends SpecAttribute { clipBehavior: clipBehavior, gap: gap?.resolve(mix), animated: animated?.resolve(mix) ?? mix.animation, + modifiers: modifiers?.resolve(mix), ); } @@ -199,6 +205,7 @@ final class FlexSpecAttribute extends SpecAttribute { clipBehavior: other.clipBehavior ?? clipBehavior, gap: gap?.merge(other.gap) ?? other.gap, animated: animated?.merge(other.animated) ?? other.animated, + modifiers: modifiers?.merge(other.modifiers) ?? other.modifiers, ); } @@ -218,6 +225,7 @@ final class FlexSpecAttribute extends SpecAttribute { clipBehavior, gap, animated, + modifiers, ]; } @@ -267,6 +275,9 @@ base class FlexSpecUtility /// Utility for defining [FlexSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); + /// Utility for defining [FlexSpecAttribute.modifiers] + late final modifiers = ModifierUtility((v) => only(modifiers: v)); + FlexSpecUtility(super.builder); static final self = FlexSpecUtility((v) => v); @@ -284,6 +295,7 @@ base class FlexSpecUtility Clip? clipBehavior, SpacingSideDto? gap, AnimatedDataDto? animated, + WidgetModifiersDataDto? modifiers, }) { return builder(FlexSpecAttribute( crossAxisAlignment: crossAxisAlignment, @@ -296,6 +308,7 @@ base class FlexSpecUtility clipBehavior: clipBehavior, gap: gap, animated: animated, + modifiers: modifiers, )); } } diff --git a/packages/mix/lib/src/specs/flex/flex_widget.dart b/packages/mix/lib/src/specs/flex/flex_widget.dart index d23623610..0bb690f58 100644 --- a/packages/mix/lib/src/specs/flex/flex_widget.dart +++ b/packages/mix/lib/src/specs/flex/flex_widget.dart @@ -3,6 +3,7 @@ import 'package:flutter/widgets.dart'; import '../../core/styled_widget.dart'; +import '../../modifiers/render_widget_modifier.dart'; import '../box/box_spec.dart'; import '../box/box_widget.dart'; import 'flex_spec.dart'; @@ -46,6 +47,7 @@ class StyledFlex extends StyledWidget { return FlexSpecWidget( spec: spec, direction: direction, + orderOfModifiers: orderOfModifiers, children: children, ); }); @@ -58,11 +60,13 @@ class FlexSpecWidget extends StatelessWidget { this.spec, required this.children, required this.direction, + this.orderOfModifiers = const [], }); final List children; final Axis direction; final FlexSpec? spec; + final List orderOfModifiers; List _buildChildren(double? gap) { if (gap == null) return children; @@ -81,8 +85,7 @@ class FlexSpecWidget extends StatelessWidget { @override Widget build(BuildContext context) { final gap = spec?.gap; - - return Flex( + final flexWidget = Flex( direction: direction, mainAxisAlignment: spec?.mainAxisAlignment ?? _defaultFlex.mainAxisAlignment, @@ -93,6 +96,14 @@ class FlexSpecWidget extends StatelessWidget { spec?.verticalDirection ?? _defaultFlex.verticalDirection, children: _buildChildren(gap), ); + + return spec == null + ? flexWidget + : RenderInlineModifiers( + orderOfModifiers: orderOfModifiers, + spec: spec!, + child: flexWidget, + ); } } diff --git a/packages/mix/lib/src/specs/icon/icon_spec.dart b/packages/mix/lib/src/specs/icon/icon_spec.dart index 745d1f2d1..77fe11d92 100644 --- a/packages/mix/lib/src/specs/icon/icon_spec.dart +++ b/packages/mix/lib/src/specs/icon/icon_spec.dart @@ -34,6 +34,7 @@ final class IconSpec extends Spec with _$IconSpec { this.applyTextScaling, this.fill, super.animated, + super.modifiers, }); Widget call(IconData? icon, {String? semanticLabel}) { diff --git a/packages/mix/lib/src/specs/icon/icon_spec.g.dart b/packages/mix/lib/src/specs/icon/icon_spec.g.dart index 84878f37d..2c7ecb979 100644 --- a/packages/mix/lib/src/specs/icon/icon_spec.g.dart +++ b/packages/mix/lib/src/specs/icon/icon_spec.g.dart @@ -45,6 +45,7 @@ base mixin _$IconSpec on Spec { bool? applyTextScaling, double? fill, AnimatedData? animated, + WidgetModifiersData? modifiers, }) { return IconSpec( color: color ?? _$this.color, @@ -57,6 +58,7 @@ base mixin _$IconSpec on Spec { applyTextScaling: applyTextScaling ?? _$this.applyTextScaling, fill: fill ?? _$this.fill, animated: animated ?? _$this.animated, + modifiers: modifiers ?? _$this.modifiers, ); } @@ -74,7 +76,7 @@ base mixin _$IconSpec on Spec { /// - [Color.lerp] for [color]. /// - [MixHelpers.lerpDouble] for [size] and [weight] and [grade] and [opticalSize] and [fill]. - /// For [shadows] and [textDirection] and [applyTextScaling] and [animated], the interpolation is performed using a step function. + /// For [shadows] and [textDirection] and [applyTextScaling] and [animated] and [modifiers], the interpolation is performed using a step function. /// If [t] is less than 0.5, the value from the current [IconSpec] is used. Otherwise, the value /// from the [other] [IconSpec] is used. /// @@ -97,6 +99,7 @@ base mixin _$IconSpec on Spec { t < 0.5 ? _$this.applyTextScaling : other.applyTextScaling, fill: MixHelpers.lerpDouble(_$this.fill, other.fill, t), animated: t < 0.5 ? _$this.animated : other.animated, + modifiers: t < 0.5 ? _$this.modifiers : other.modifiers, ); } @@ -116,6 +119,7 @@ base mixin _$IconSpec on Spec { _$this.applyTextScaling, _$this.fill, _$this.animated, + _$this.modifiers, ]; IconSpec get _$this => this as IconSpec; @@ -150,6 +154,7 @@ final class IconSpecAttribute extends SpecAttribute { this.applyTextScaling, this.fill, super.animated, + super.modifiers, }); /// Resolves to [IconSpec] using the provided [MixData]. @@ -173,6 +178,7 @@ final class IconSpecAttribute extends SpecAttribute { applyTextScaling: applyTextScaling, fill: fill, animated: animated?.resolve(mix) ?? mix.animation, + modifiers: modifiers?.resolve(mix), ); } @@ -199,6 +205,7 @@ final class IconSpecAttribute extends SpecAttribute { applyTextScaling: other.applyTextScaling ?? applyTextScaling, fill: other.fill ?? fill, animated: animated?.merge(other.animated) ?? other.animated, + modifiers: modifiers?.merge(other.modifiers) ?? other.modifiers, ); } @@ -218,6 +225,7 @@ final class IconSpecAttribute extends SpecAttribute { applyTextScaling, fill, animated, + modifiers, ]; } @@ -258,6 +266,9 @@ base class IconSpecUtility /// Utility for defining [IconSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); + /// Utility for defining [IconSpecAttribute.modifiers] + late final modifiers = ModifierUtility((v) => only(modifiers: v)); + IconSpecUtility(super.builder); static final self = IconSpecUtility((v) => v); @@ -275,6 +286,7 @@ base class IconSpecUtility bool? applyTextScaling, double? fill, AnimatedDataDto? animated, + WidgetModifiersDataDto? modifiers, }) { return builder(IconSpecAttribute( color: color, @@ -287,6 +299,7 @@ base class IconSpecUtility applyTextScaling: applyTextScaling, fill: fill, animated: animated, + modifiers: modifiers, )); } } diff --git a/packages/mix/lib/src/specs/icon/icon_widget.dart b/packages/mix/lib/src/specs/icon/icon_widget.dart index 38ba008b4..e4a20f6f9 100644 --- a/packages/mix/lib/src/specs/icon/icon_widget.dart +++ b/packages/mix/lib/src/specs/icon/icon_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../../core/styled_widget.dart'; +import '../../modifiers/render_widget_modifier.dart'; import 'icon_spec.dart'; class StyledIcon extends StyledWidget { @@ -50,28 +51,32 @@ class IconSpecWidget extends StatelessWidget { this.semanticLabel, super.key, this.textDirection, - this.modifierOrder = const [], + this.orderOfModifiers = const [], }); final IconData? icon; final IconSpec? spec; final String? semanticLabel; final TextDirection? textDirection; - final List modifierOrder; + final List orderOfModifiers; @override Widget build(BuildContext context) { - return Icon( - icon, - size: spec?.size, - fill: spec?.fill, - weight: spec?.weight, - grade: spec?.grade, - opticalSize: spec?.opticalSize, - color: spec?.color, - shadows: spec?.shadows, - semanticLabel: semanticLabel, - textDirection: textDirection, + return RenderInlineModifiers( + orderOfModifiers: orderOfModifiers, + spec: spec ?? const IconSpec(), + child: Icon( + icon, + size: spec?.size, + fill: spec?.fill, + weight: spec?.weight, + grade: spec?.grade, + opticalSize: spec?.opticalSize, + color: spec?.color, + shadows: spec?.shadows, + semanticLabel: semanticLabel, + textDirection: textDirection, + ), ); } } @@ -117,7 +122,7 @@ class AnimatedIconSpecWidget extends ImplicitlyAnimatedWidget { super.key, this.semanticLabel, this.textDirection, - this.modifierOrder = const [], + this.orderOfModifiers = const [], super.curve, required super.duration, super.onEnd, @@ -127,7 +132,7 @@ class AnimatedIconSpecWidget extends ImplicitlyAnimatedWidget { final IconSpec spec; final String? semanticLabel; final TextDirection? textDirection; - final List modifierOrder; + final List orderOfModifiers; @override // ignore: library_private_types_in_public_api @@ -158,6 +163,7 @@ class _AnimatedIconSpecState spec: spec, semanticLabel: widget.semanticLabel, textDirection: widget.textDirection, + orderOfModifiers: widget.orderOfModifiers, ); } } diff --git a/packages/mix/lib/src/specs/image/image_spec.dart b/packages/mix/lib/src/specs/image/image_spec.dart index a590f6647..3dd8286a2 100644 --- a/packages/mix/lib/src/specs/image/image_spec.dart +++ b/packages/mix/lib/src/specs/image/image_spec.dart @@ -32,6 +32,7 @@ final class ImageSpec extends Spec with _$ImageSpec { this.filterQuality, this.colorBlendMode, super.animated, + super.modifiers, }); Widget call({ diff --git a/packages/mix/lib/src/specs/image/image_spec.g.dart b/packages/mix/lib/src/specs/image/image_spec.g.dart index c34e6e75a..e044551e2 100644 --- a/packages/mix/lib/src/specs/image/image_spec.g.dart +++ b/packages/mix/lib/src/specs/image/image_spec.g.dart @@ -45,6 +45,7 @@ base mixin _$ImageSpec on Spec { FilterQuality? filterQuality, BlendMode? colorBlendMode, AnimatedData? animated, + WidgetModifiersData? modifiers, }) { return ImageSpec( width: width ?? _$this.width, @@ -57,6 +58,7 @@ base mixin _$ImageSpec on Spec { filterQuality: filterQuality ?? _$this.filterQuality, colorBlendMode: colorBlendMode ?? _$this.colorBlendMode, animated: animated ?? _$this.animated, + modifiers: modifiers ?? _$this.modifiers, ); } @@ -76,7 +78,7 @@ base mixin _$ImageSpec on Spec { /// - [AlignmentGeometry.lerp] for [alignment]. /// - [Rect.lerp] for [centerSlice]. - /// For [repeat] and [fit] and [filterQuality] and [colorBlendMode] and [animated], the interpolation is performed using a step function. + /// For [repeat] and [fit] and [filterQuality] and [colorBlendMode] and [animated] and [modifiers], the interpolation is performed using a step function. /// If [t] is less than 0.5, the value from the current [ImageSpec] is used. Otherwise, the value /// from the [other] [ImageSpec] is used. /// @@ -97,6 +99,7 @@ base mixin _$ImageSpec on Spec { filterQuality: t < 0.5 ? _$this.filterQuality : other.filterQuality, colorBlendMode: t < 0.5 ? _$this.colorBlendMode : other.colorBlendMode, animated: t < 0.5 ? _$this.animated : other.animated, + modifiers: t < 0.5 ? _$this.modifiers : other.modifiers, ); } @@ -116,6 +119,7 @@ base mixin _$ImageSpec on Spec { _$this.filterQuality, _$this.colorBlendMode, _$this.animated, + _$this.modifiers, ]; ImageSpec get _$this => this as ImageSpec; @@ -150,6 +154,7 @@ final class ImageSpecAttribute extends SpecAttribute { this.filterQuality, this.colorBlendMode, super.animated, + super.modifiers, }); /// Resolves to [ImageSpec] using the provided [MixData]. @@ -173,6 +178,7 @@ final class ImageSpecAttribute extends SpecAttribute { filterQuality: filterQuality, colorBlendMode: colorBlendMode, animated: animated?.resolve(mix) ?? mix.animation, + modifiers: modifiers?.resolve(mix), ); } @@ -199,6 +205,7 @@ final class ImageSpecAttribute extends SpecAttribute { filterQuality: other.filterQuality ?? filterQuality, colorBlendMode: other.colorBlendMode ?? colorBlendMode, animated: animated?.merge(other.animated) ?? other.animated, + modifiers: modifiers?.merge(other.modifiers) ?? other.modifiers, ); } @@ -218,6 +225,7 @@ final class ImageSpecAttribute extends SpecAttribute { filterQuality, colorBlendMode, animated, + modifiers, ]; } @@ -258,6 +266,9 @@ base class ImageSpecUtility /// Utility for defining [ImageSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); + /// Utility for defining [ImageSpecAttribute.modifiers] + late final modifiers = ModifierUtility((v) => only(modifiers: v)); + ImageSpecUtility(super.builder); static final self = ImageSpecUtility((v) => v); @@ -275,6 +286,7 @@ base class ImageSpecUtility FilterQuality? filterQuality, BlendMode? colorBlendMode, AnimatedDataDto? animated, + WidgetModifiersDataDto? modifiers, }) { return builder(ImageSpecAttribute( width: width, @@ -287,6 +299,7 @@ base class ImageSpecUtility filterQuality: filterQuality, colorBlendMode: colorBlendMode, animated: animated, + modifiers: modifiers, )); } } diff --git a/packages/mix/lib/src/specs/image/image_widget.dart b/packages/mix/lib/src/specs/image/image_widget.dart index 455a523dc..1d41c909a 100644 --- a/packages/mix/lib/src/specs/image/image_widget.dart +++ b/packages/mix/lib/src/specs/image/image_widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/widgets.dart'; import '../../core/styled_widget.dart'; import '../../internal/constants.dart'; +import '../../modifiers/render_widget_modifier.dart'; import 'image_spec.dart'; class StyledImage extends StyledWidget { @@ -74,7 +75,7 @@ class StyledImage extends StyledWidget { class ImageSpecWidget extends StatelessWidget { const ImageSpecWidget({ super.key, - this.modifierOrder = const [], + this.orderOfModifiers = const [], this.spec, required this.image, this.frameBuilder, @@ -95,7 +96,7 @@ class ImageSpecWidget extends StatelessWidget { final ImageErrorWidgetBuilder? errorBuilder; final String? semanticLabel; final bool excludeFromSemantics; - final List modifierOrder; + final List orderOfModifiers; final bool gaplessPlayback; final bool isAntiAlias; final bool matchTextDirection; @@ -103,26 +104,30 @@ class ImageSpecWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return Image( - image: image, - frameBuilder: frameBuilder, - loadingBuilder: loadingBuilder, - errorBuilder: errorBuilder, - semanticLabel: semanticLabel, - excludeFromSemantics: excludeFromSemantics, - width: spec?.width, - height: spec?.height, - color: spec?.color, - opacity: opacity, - colorBlendMode: spec?.colorBlendMode ?? BlendMode.clear, - fit: spec?.fit, - alignment: spec?.alignment ?? Alignment.center, - repeat: spec?.repeat ?? ImageRepeat.noRepeat, - centerSlice: spec?.centerSlice, - matchTextDirection: matchTextDirection, - gaplessPlayback: gaplessPlayback, - isAntiAlias: isAntiAlias, - filterQuality: spec?.filterQuality ?? FilterQuality.low, + return RenderInlineModifiers( + orderOfModifiers: orderOfModifiers, + spec: spec ?? const ImageSpec(), + child: Image( + image: image, + frameBuilder: frameBuilder, + loadingBuilder: loadingBuilder, + errorBuilder: errorBuilder, + semanticLabel: semanticLabel, + excludeFromSemantics: excludeFromSemantics, + width: spec?.width, + height: spec?.height, + color: spec?.color, + opacity: opacity, + colorBlendMode: spec?.colorBlendMode ?? BlendMode.clear, + fit: spec?.fit, + alignment: spec?.alignment ?? Alignment.center, + repeat: spec?.repeat ?? ImageRepeat.noRepeat, + centerSlice: spec?.centerSlice, + matchTextDirection: matchTextDirection, + gaplessPlayback: gaplessPlayback, + isAntiAlias: isAntiAlias, + filterQuality: spec?.filterQuality ?? FilterQuality.low, + ), ); } } diff --git a/packages/mix/lib/src/specs/stack/stack_spec.dart b/packages/mix/lib/src/specs/stack/stack_spec.dart index 2925b1783..a30ccf43c 100644 --- a/packages/mix/lib/src/specs/stack/stack_spec.dart +++ b/packages/mix/lib/src/specs/stack/stack_spec.dart @@ -22,6 +22,7 @@ final class StackSpec extends Spec with _$StackSpec { this.textDirection, this.clipBehavior, super.animated, + super.modifiers, }); Widget call({List children = const []}) { diff --git a/packages/mix/lib/src/specs/stack/stack_spec.g.dart b/packages/mix/lib/src/specs/stack/stack_spec.g.dart index 68528321b..5ea664d84 100644 --- a/packages/mix/lib/src/specs/stack/stack_spec.g.dart +++ b/packages/mix/lib/src/specs/stack/stack_spec.g.dart @@ -40,6 +40,7 @@ base mixin _$StackSpec on Spec { TextDirection? textDirection, Clip? clipBehavior, AnimatedData? animated, + WidgetModifiersData? modifiers, }) { return StackSpec( alignment: alignment ?? _$this.alignment, @@ -47,6 +48,7 @@ base mixin _$StackSpec on Spec { textDirection: textDirection ?? _$this.textDirection, clipBehavior: clipBehavior ?? _$this.clipBehavior, animated: animated ?? _$this.animated, + modifiers: modifiers ?? _$this.modifiers, ); } @@ -63,7 +65,7 @@ base mixin _$StackSpec on Spec { /// /// - [AlignmentGeometry.lerp] for [alignment]. - /// For [fit] and [textDirection] and [clipBehavior] and [animated], the interpolation is performed using a step function. + /// For [fit] and [textDirection] and [clipBehavior] and [animated] and [modifiers], the interpolation is performed using a step function. /// If [t] is less than 0.5, the value from the current [StackSpec] is used. Otherwise, the value /// from the [other] [StackSpec] is used. /// @@ -79,6 +81,7 @@ base mixin _$StackSpec on Spec { textDirection: t < 0.5 ? _$this.textDirection : other.textDirection, clipBehavior: t < 0.5 ? _$this.clipBehavior : other.clipBehavior, animated: t < 0.5 ? _$this.animated : other.animated, + modifiers: t < 0.5 ? _$this.modifiers : other.modifiers, ); } @@ -93,6 +96,7 @@ base mixin _$StackSpec on Spec { _$this.textDirection, _$this.clipBehavior, _$this.animated, + _$this.modifiers, ]; StackSpec get _$this => this as StackSpec; @@ -117,6 +121,7 @@ final class StackSpecAttribute extends SpecAttribute { this.textDirection, this.clipBehavior, super.animated, + super.modifiers, }); /// Resolves to [StackSpec] using the provided [MixData]. @@ -135,6 +140,7 @@ final class StackSpecAttribute extends SpecAttribute { textDirection: textDirection, clipBehavior: clipBehavior, animated: animated?.resolve(mix) ?? mix.animation, + modifiers: modifiers?.resolve(mix), ); } @@ -156,6 +162,7 @@ final class StackSpecAttribute extends SpecAttribute { textDirection: other.textDirection ?? textDirection, clipBehavior: other.clipBehavior ?? clipBehavior, animated: animated?.merge(other.animated) ?? other.animated, + modifiers: modifiers?.merge(other.modifiers) ?? other.modifiers, ); } @@ -170,6 +177,7 @@ final class StackSpecAttribute extends SpecAttribute { textDirection, clipBehavior, animated, + modifiers, ]; } @@ -195,6 +203,9 @@ base class StackSpecUtility /// Utility for defining [StackSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); + /// Utility for defining [StackSpecAttribute.modifiers] + late final modifiers = ModifierUtility((v) => only(modifiers: v)); + StackSpecUtility(super.builder); static final self = StackSpecUtility((v) => v); @@ -207,6 +218,7 @@ base class StackSpecUtility TextDirection? textDirection, Clip? clipBehavior, AnimatedDataDto? animated, + WidgetModifiersDataDto? modifiers, }) { return builder(StackSpecAttribute( alignment: alignment, @@ -214,6 +226,7 @@ base class StackSpecUtility textDirection: textDirection, clipBehavior: clipBehavior, animated: animated, + modifiers: modifiers, )); } } diff --git a/packages/mix/lib/src/specs/stack/stack_widget.dart b/packages/mix/lib/src/specs/stack/stack_widget.dart index 035df7b47..7a6404f71 100644 --- a/packages/mix/lib/src/specs/stack/stack_widget.dart +++ b/packages/mix/lib/src/specs/stack/stack_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/widgets.dart'; import '../../core/styled_widget.dart'; +import '../../modifiers/render_widget_modifier.dart'; import '../box/box_spec.dart'; import '../box/box_widget.dart'; import 'stack_spec.dart'; @@ -40,20 +41,30 @@ class StyledStack extends StyledWidget { } class StackSpecWidget extends StatelessWidget { - const StackSpecWidget({this.spec, super.key, this.children}); + const StackSpecWidget({ + this.spec, + this.children, + this.orderOfModifiers = const [], + super.key, + }); final List? children; final StackSpec? spec; + final List orderOfModifiers; @override Widget build(BuildContext context) { // The Stack widget is used here, applying the resolved styles from StackSpec. - return Stack( - alignment: spec?.alignment ?? _defaultStack.alignment, - textDirection: spec?.textDirection, - fit: spec?.fit ?? _defaultStack.fit, - clipBehavior: spec?.clipBehavior ?? _defaultStack.clipBehavior, - children: children ?? const [], + return RenderInlineModifiers( + orderOfModifiers: orderOfModifiers, + spec: spec ?? const StackSpec(), + child: Stack( + alignment: spec?.alignment ?? _defaultStack.alignment, + textDirection: spec?.textDirection, + fit: spec?.fit ?? _defaultStack.fit, + clipBehavior: spec?.clipBehavior ?? _defaultStack.clipBehavior, + children: children ?? const [], + ), ); } } diff --git a/packages/mix/lib/src/specs/text/text_spec.dart b/packages/mix/lib/src/specs/text/text_spec.dart index e409d6387..45025deb7 100644 --- a/packages/mix/lib/src/specs/text/text_spec.dart +++ b/packages/mix/lib/src/specs/text/text_spec.dart @@ -54,6 +54,7 @@ final class TextSpec extends Spec with _$TextSpec { this.softWrap, this.directive, super.animated, + super.modifiers, }) : assert( textScaler == null || textScaleFactor == null, 'textScaleFactor is deprecated and cannot be specified when textScaler is specified.', diff --git a/packages/mix/lib/src/specs/text/text_spec.g.dart b/packages/mix/lib/src/specs/text/text_spec.g.dart index 2d5222d37..16e3b4caa 100644 --- a/packages/mix/lib/src/specs/text/text_spec.g.dart +++ b/packages/mix/lib/src/specs/text/text_spec.g.dart @@ -48,6 +48,7 @@ base mixin _$TextSpec on Spec { bool? softWrap, TextDirective? directive, AnimatedData? animated, + WidgetModifiersData? modifiers, }) { return TextSpec( overflow: overflow ?? _$this.overflow, @@ -63,6 +64,7 @@ base mixin _$TextSpec on Spec { softWrap: softWrap ?? _$this.softWrap, directive: directive ?? _$this.directive, animated: animated ?? _$this.animated, + modifiers: modifiers ?? _$this.modifiers, ); } @@ -81,7 +83,7 @@ base mixin _$TextSpec on Spec { /// - [MixHelpers.lerpDouble] for [textScaleFactor]. /// - [MixHelpers.lerpTextStyle] for [style]. - /// For [overflow] and [textAlign] and [textScaler] and [maxLines] and [textWidthBasis] and [textHeightBehavior] and [textDirection] and [softWrap] and [directive] and [animated], the interpolation is performed using a step function. + /// For [overflow] and [textAlign] and [textScaler] and [maxLines] and [textWidthBasis] and [textHeightBehavior] and [textDirection] and [softWrap] and [directive] and [animated] and [modifiers], the interpolation is performed using a step function. /// If [t] is less than 0.5, the value from the current [TextSpec] is used. Otherwise, the value /// from the [other] [TextSpec] is used. /// @@ -108,6 +110,7 @@ base mixin _$TextSpec on Spec { softWrap: t < 0.5 ? _$this.softWrap : other.softWrap, directive: t < 0.5 ? _$this.directive : other.directive, animated: t < 0.5 ? _$this.animated : other.animated, + modifiers: t < 0.5 ? _$this.modifiers : other.modifiers, ); } @@ -130,6 +133,7 @@ base mixin _$TextSpec on Spec { _$this.softWrap, _$this.directive, _$this.animated, + _$this.modifiers, ]; TextSpec get _$this => this as TextSpec; @@ -170,6 +174,7 @@ final class TextSpecAttribute extends SpecAttribute { this.softWrap, this.directive, super.animated, + super.modifiers, }); /// Resolves to [TextSpec] using the provided [MixData]. @@ -196,6 +201,7 @@ final class TextSpecAttribute extends SpecAttribute { softWrap: softWrap, directive: directive?.resolve(mix), animated: animated?.resolve(mix) ?? mix.animation, + modifiers: modifiers?.resolve(mix), ); } @@ -225,6 +231,7 @@ final class TextSpecAttribute extends SpecAttribute { softWrap: other.softWrap ?? softWrap, directive: directive?.merge(other.directive) ?? other.directive, animated: animated?.merge(other.animated) ?? other.animated, + modifiers: modifiers?.merge(other.modifiers) ?? other.modifiers, ); } @@ -247,6 +254,7 @@ final class TextSpecAttribute extends SpecAttribute { softWrap, directive, animated, + modifiers, ]; } @@ -313,6 +321,9 @@ base class TextSpecUtility /// Utility for defining [TextSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); + /// Utility for defining [TextSpecAttribute.modifiers] + late final modifiers = ModifierUtility((v) => only(modifiers: v)); + TextSpecUtility(super.builder); static final self = TextSpecUtility((v) => v); @@ -333,6 +344,7 @@ base class TextSpecUtility bool? softWrap, TextDirectiveDto? directive, AnimatedDataDto? animated, + WidgetModifiersDataDto? modifiers, }) { return builder(TextSpecAttribute( overflow: overflow, @@ -348,6 +360,7 @@ base class TextSpecUtility softWrap: softWrap, directive: directive, animated: animated, + modifiers: modifiers, )); } } diff --git a/packages/mix/lib/src/specs/text/text_widget.dart b/packages/mix/lib/src/specs/text/text_widget.dart index 64a54834b..5facb6ea9 100644 --- a/packages/mix/lib/src/specs/text/text_widget.dart +++ b/packages/mix/lib/src/specs/text/text_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../../core/styled_widget.dart'; +import '../../modifiers/render_widget_modifier.dart'; import 'text_spec.dart'; /// [StyledText] - A styled widget for displaying text with a mix of styles. @@ -77,6 +78,7 @@ class TextSpecWidget extends StatelessWidget { required this.spec, this.semanticsLabel, this.locale, + this.orderOfModifiers = const [], super.key, }); @@ -84,27 +86,31 @@ class TextSpecWidget extends StatelessWidget { final String? semanticsLabel; final Locale? locale; final TextSpec? spec; + final List orderOfModifiers; @override Widget build(BuildContext context) { // The Text widget is used here, applying the resolved styles and properties from TextSpec. - - return Text( - spec?.directive?.apply(text) ?? text, - style: spec?.style, - strutStyle: spec?.strutStyle, - textAlign: spec?.textAlign, - textDirection: spec?.textDirection, - locale: locale, - softWrap: spec?.softWrap, - overflow: spec?.overflow, - // ignore: deprecated_member_use, deprecated_member_use_from_same_package - textScaleFactor: spec?.textScaleFactor, - textScaler: spec?.textScaler, - maxLines: spec?.maxLines, - semanticsLabel: semanticsLabel, - textWidthBasis: spec?.textWidthBasis, - textHeightBehavior: spec?.textHeightBehavior, + return RenderInlineModifiers( + orderOfModifiers: const [], + spec: spec ?? const TextSpec(), + child: Text( + spec?.directive?.apply(text) ?? text, + style: spec?.style, + strutStyle: spec?.strutStyle, + textAlign: spec?.textAlign, + textDirection: spec?.textDirection, + locale: locale, + softWrap: spec?.softWrap, + overflow: spec?.overflow, + // ignore: deprecated_member_use, deprecated_member_use_from_same_package + textScaleFactor: spec?.textScaleFactor, + textScaler: spec?.textScaler, + maxLines: spec?.maxLines, + semanticsLabel: semanticsLabel, + textWidthBasis: spec?.textWidthBasis, + textHeightBehavior: spec?.textHeightBehavior, + ), ); } } diff --git a/packages/mix/test/modifiers/render_widget_modifier_test.dart b/packages/mix/test/modifiers/render_widget_modifier_test.dart new file mode 100644 index 000000000..09df400d9 --- /dev/null +++ b/packages/mix/test/modifiers/render_widget_modifier_test.dart @@ -0,0 +1,61 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +void main() { + group('orderSpecs', () { + test('should order modifiers based on provided order', () { + final orderOfModifiers = [ + ClipOvalModifierSpec, + OpacityModifierSpec, + AlignModifierSpec, + TransformModifierSpec, + ]; + final Set modifiers = { + const OpacityModifierSpec(1), + TransformModifierSpec(transform: Matrix4.rotationX(3)), + const AlignModifierSpec(alignment: Alignment.center), + const ClipOvalModifierSpec(), + }; + + final result = orderSpecs(orderOfModifiers, modifiers); + + expect(result.map((e) => e.type), orderOfModifiers); + }); + + test('should include default order specs', () { + final Set modifiers = { + TransformModifierSpec(transform: Matrix4.rotationX(3)), + const OpacityModifierSpec(1), + const AlignModifierSpec(alignment: Alignment.center), + }; + + final result = orderSpecs([], modifiers); + + expect(result.map((e) => e.type), + [AlignModifierSpec, TransformModifierSpec, OpacityModifierSpec]); + }); + + test('should handle empty modifiers', () { + final orderOfModifiers = [TransformModifierSpec]; + final modifiers = {}; + + final result = orderSpecs(orderOfModifiers, modifiers); + + expect(result, isEmpty); + }); + + test('should handle duplicate modifiers', () { + final orderOfModifiers = [TransformModifierSpec]; + final modifiers = { + const OpacityModifierSpec(1), + const OpacityModifierSpec(0.4), + }; + + final result = orderSpecs(orderOfModifiers, modifiers); + + expect(result.length, 1); + expect(result.first.type, OpacityModifierSpec); + }); + }); +} diff --git a/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart b/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart index 3e3a17e1b..6449a7a36 100644 --- a/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart +++ b/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart @@ -1,15 +1,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; -import 'package:mix/src/attributes/modifiers/modifiers_data.dart'; -import 'package:mix/src/attributes/modifiers/modifiers_data_dto.dart'; +import 'package:mix/src/attributes/modifiers/widget_modifiers_data.dart'; +import 'package:mix/src/attributes/modifiers/widget_modifiers_data_dto.dart'; import '../../../helpers/testing_utils.dart'; void main() { group('ModifiersDataDto', () { test('should create an instance with default values', () { - const dto = ModifiersDataDto([]); + const dto = WidgetModifiersDataDto([]); expect(dto.value, isEmpty); }); @@ -17,13 +17,13 @@ void main() { const modifier1 = TestModifierSpecAttribute(); const modifier2 = TestModifierSpecAttribute(); // ignore: equal_elements_in_set - const dto = ModifiersDataDto([modifier1, modifier2]); + const dto = WidgetModifiersDataDto([modifier1, modifier2]); expect(dto.value, contains(modifier1)); }); test('should merge with another instance', () { - const dto1 = ModifiersDataDto([TestModifierSpecAttribute()]); - const dto2 = ModifiersDataDto([TestModifierSpecAttribute(2)]); + const dto1 = WidgetModifiersDataDto([TestModifierSpecAttribute()]); + const dto2 = WidgetModifiersDataDto([TestModifierSpecAttribute(2)]); final merged = dto1.merge(dto2); expect(merged.value, hasLength(1)); expect(merged.value.first, const TestModifierSpecAttribute(2)); @@ -31,7 +31,7 @@ void main() { test('should resolve to a ModifiersData instance', () { const modifier = TestModifierSpecAttribute(); - const dto = ModifiersDataDto([modifier]); + const dto = WidgetModifiersDataDto([modifier]); final modifiersData = dto.resolve(EmptyMixData); expect(modifiersData.value.length, 1); expect(modifiersData.value.first, const TestModifierSpec()); @@ -39,14 +39,14 @@ void main() { // test equality test('should be equal to another instance', () { - const dto1 = ModifiersDataDto([TestModifierSpecAttribute()]); - const dto2 = ModifiersDataDto([TestModifierSpecAttribute()]); + const dto1 = WidgetModifiersDataDto([TestModifierSpecAttribute()]); + const dto2 = WidgetModifiersDataDto([TestModifierSpecAttribute()]); expect(dto1, equals(dto2)); }); test('should not be equal to another instance', () { - const dto1 = ModifiersDataDto([TestModifierSpecAttribute()]); - const dto2 = ModifiersDataDto([TestModifierSpecAttribute(2)]); + const dto1 = WidgetModifiersDataDto([TestModifierSpecAttribute()]); + const dto2 = WidgetModifiersDataDto([TestModifierSpecAttribute(2)]); expect(dto1, isNot(equals(dto2))); }); }); @@ -56,20 +56,20 @@ void main() { const modifier1 = TestModifierSpec(); const modifier2 = TestModifierSpec(); // ignore: equal_elements_in_set - const modifiersData = ModifiersData([modifier1, modifier2]); + const modifiersData = WidgetModifiersData([modifier1, modifier2]); expect(modifiersData.value, contains(modifier1)); }); // equality test('should be equal to another instance', () { - const modifiersData1 = ModifiersData([TestModifierSpec()]); - const modifiersData2 = ModifiersData([TestModifierSpec()]); + const modifiersData1 = WidgetModifiersData([TestModifierSpec()]); + const modifiersData2 = WidgetModifiersData([TestModifierSpec()]); expect(modifiersData1, equals(modifiersData2)); }); test('should not be equal to another instance', () { - const modifiersData1 = ModifiersData([TestModifierSpec()]); - const modifiersData2 = ModifiersData([]); + const modifiersData1 = WidgetModifiersData([TestModifierSpec()]); + const modifiersData2 = WidgetModifiersData([]); expect(modifiersData1, isNot(equals(modifiersData2))); }); }); diff --git a/packages/mix/test/src/specs/box/box_attribute_test.dart b/packages/mix/test/src/specs/box/box_attribute_test.dart index b4d85613d..32a8a146a 100644 --- a/packages/mix/test/src/specs/box/box_attribute_test.dart +++ b/packages/mix/test/src/specs/box/box_attribute_test.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; -import 'package:mix/src/attributes/modifiers/modifiers_data_dto.dart'; +import 'package:mix/src/attributes/modifiers/widget_modifiers_data_dto.dart'; import '../../../helpers/testing_utils.dart'; @@ -23,7 +23,7 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 10), ]), @@ -54,7 +54,7 @@ void main() { expect(containerSpecAttribute.width, 100); expect( containerSpecAttribute.modifiers, - const ModifiersDataDto([ + const WidgetModifiersDataDto([ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 10), ])); @@ -77,7 +77,7 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 10), ])); @@ -127,7 +127,7 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 10), ])); @@ -148,7 +148,7 @@ void main() { clipBehavior: Clip.antiAliasWithSaveLayer, width: 200, height: 200, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ SizedBoxModifierAttribute(width: 20), ]), ), @@ -179,7 +179,7 @@ void main() { expect(mergedBoxSpecAttribute.width, 200); expect( mergedBoxSpecAttribute.modifiers, - const ModifiersDataDto([ + const WidgetModifiersDataDto([ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 20), ])); @@ -202,7 +202,7 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 10), ])); @@ -225,7 +225,7 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, - modifiers: const ModifiersDataDto( + modifiers: const WidgetModifiersDataDto( [ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 10), @@ -279,7 +279,7 @@ void main() { clipBehavior: Clip.antiAliasWithSaveLayer, width: 200, height: 200, - modifiers: const ModifiersDataDto( + modifiers: const WidgetModifiersDataDto( [ OpacityModifierAttribute(0.4), SizedBoxModifierAttribute(height: 20, width: 10), diff --git a/packages/mix/test/src/specs/box/box_spec_test.dart b/packages/mix/test/src/specs/box/box_spec_test.dart index b36f3aaad..c666ae629 100644 --- a/packages/mix/test/src/specs/box/box_spec_test.dart +++ b/packages/mix/test/src/specs/box/box_spec_test.dart @@ -3,8 +3,8 @@ 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/modifiers/modifiers_data.dart'; -import 'package:mix/src/attributes/modifiers/modifiers_data_dto.dart'; +import 'package:mix/src/attributes/modifiers/widget_modifiers_data.dart'; +import 'package:mix/src/attributes/modifiers/widget_modifiers_data_dto.dart'; import '../../../helpers/testing_utils.dart'; @@ -23,7 +23,7 @@ void main() { decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), transform: Matrix4.translationValues(10.0, 10.0, 0.0), clipBehavior: Clip.antiAlias, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ OpacityModifierAttribute(1), SizedBoxModifierAttribute(height: 10, width: 10), ]), @@ -65,7 +65,7 @@ void main() { transformAlignment: Alignment.center, width: 300, height: 200, - modifiers: const ModifiersData([ + modifiers: const WidgetModifiersData([ OpacityModifierSpec(0.5), SizedBoxModifierSpec(height: 10, width: 10), ]), @@ -74,7 +74,7 @@ void main() { final copiedSpec = spec.copyWith( width: 250.0, height: 150.0, - modifiers: const ModifiersData([ + modifiers: const WidgetModifiersData([ OpacityModifierSpec(1), ]), ); @@ -98,7 +98,7 @@ void main() { expect( copiedSpec.modifiers!.value, - const ModifiersData( + const WidgetModifiersData( [OpacityModifierSpec(1)], ).value, ); @@ -206,7 +206,7 @@ void main() { clipBehavior: Clip.none, width: 300, height: 200, - modifiers: const ModifiersData([ + modifiers: const WidgetModifiersData([ OpacityModifierSpec(0.5), SizedBoxModifierSpec(height: 10, width: 10), ]), @@ -224,7 +224,7 @@ void main() { clipBehavior: Clip.none, width: 300, height: 200, - modifiers: const ModifiersData([ + modifiers: const WidgetModifiersData([ OpacityModifierSpec(0.5), SizedBoxModifierSpec(height: 10, width: 10), ]), @@ -252,7 +252,7 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 10), ]), @@ -276,7 +276,7 @@ void main() { clipBehavior: Clip.antiAliasWithSaveLayer, width: 200, height: 200, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ SizedBoxModifierAttribute(width: 100), ]), ), diff --git a/packages/mix/test/src/specs/image/image_spec_test.dart b/packages/mix/test/src/specs/image/image_spec_test.dart index 4a98034a5..887b94873 100644 --- a/packages/mix/test/src/specs/image/image_spec_test.dart +++ b/packages/mix/test/src/specs/image/image_spec_test.dart @@ -114,8 +114,9 @@ void main() { expect(getValueOf(spec.centerSlice), Rect.zero); expect(getValueOf(spec.filterQuality), FilterQuality.low); expect(getValueOf(spec.colorBlendMode), BlendMode.srcOver); + expect(getValueOf(spec.modifiers), null); expect(getValueOf(spec.animated), const AnimatedData.withDefaults()); - expect(spec.props.length, 10); + expect(spec.props.length, 11); }); }); } diff --git a/packages/mix/test/src/specs/stack/stack_attribute_test.dart b/packages/mix/test/src/specs/stack/stack_attribute_test.dart index f9dee178d..227b87371 100644 --- a/packages/mix/test/src/specs/stack/stack_attribute_test.dart +++ b/packages/mix/test/src/specs/stack/stack_attribute_test.dart @@ -70,7 +70,7 @@ void main() { ); final props = attribute.props; - expect(props.length, 5); + expect(props.length, 6); expect(props[0], Alignment.center); expect(props[1], StackFit.expand); expect(props[2], TextDirection.ltr); diff --git a/packages/mix/test/src/widgets/box/box_test.dart b/packages/mix/test/src/widgets/box/box_test.dart index 2369d8b02..6369a5953 100644 --- a/packages/mix/test/src/widgets/box/box_test.dart +++ b/packages/mix/test/src/widgets/box/box_test.dart @@ -151,7 +151,7 @@ void main() { of: find.byKey(key), matching: find.byType(RenderModifiers), ), - findsOneWidget, + findsNWidgets(2), ); expect( diff --git a/packages/mix/test/src/widgets/styled_icon_test.dart b/packages/mix/test/src/widgets/styled_icon_test.dart index be4f93bea..a5a52724c 100644 --- a/packages/mix/test/src/widgets/styled_icon_test.dart +++ b/packages/mix/test/src/widgets/styled_icon_test.dart @@ -51,7 +51,7 @@ void main() { of: find.byKey(key), matching: find.byType(RenderModifiers), ), - findsOneWidget, + findsNWidgets(2), ); expect( diff --git a/packages/mix_generator/lib/src/helpers/type_ref_repository.dart b/packages/mix_generator/lib/src/helpers/type_ref_repository.dart index ea4242092..899db24ce 100644 --- a/packages/mix_generator/lib/src/helpers/type_ref_repository.dart +++ b/packages/mix_generator/lib/src/helpers/type_ref_repository.dart @@ -16,6 +16,7 @@ class TypeRefRepository { static Map _utilityOverrides = { 'EdgeInsetsGeometry': 'SpacingUtility', 'AnimatedData': 'AnimatedUtility', + 'WidgetModifiersData': 'ModifierUtility', }; static final _dtoMap = { @@ -23,7 +24,7 @@ class TypeRefRepository { 'BoxConstraints': 'BoxConstraintsDto', 'Decoration': 'DecorationDto', 'Color': 'ColorDto', - 'ModifiersData': 'ModifiersDataDto', + 'WidgetModifiersData': 'WidgetModifiersDataDto', 'AnimatedData': 'AnimatedDataDto', 'TextStyle': 'TextStyleDto', 'ShapeBorder': 'ShapeBorderDto', From 7056fcfe0954a44f7f9ee3213a99a59a833840c4 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Thu, 27 Jun 2024 16:43:59 -0300 Subject: [PATCH 12/27] Update melos.yaml --- melos.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/melos.yaml b/melos.yaml index 91c8675b6..877a6c11d 100644 --- a/melos.yaml +++ b/melos.yaml @@ -1,6 +1,6 @@ name: mix_workspace -# sdkPath: .fvm/flutter_sdk +sdkPath: .fvm/flutter_sdk packages: - packages/** - examples/** From 003b7796fb2ec2ef48475db8f3e3f57d222cb7cb Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Thu, 27 Jun 2024 16:47:20 -0300 Subject: [PATCH 13/27] Update widget_modifiers_util.dart --- .../lib/src/attributes/modifiers/widget_modifiers_util.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/mix/lib/src/attributes/modifiers/widget_modifiers_util.dart b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_util.dart index f61c00916..d39feb0db 100644 --- a/packages/mix/lib/src/attributes/modifiers/widget_modifiers_util.dart +++ b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_util.dart @@ -5,9 +5,6 @@ import 'widget_modifiers_data_dto.dart'; final class ModifierUtility extends MixUtility { - late final add = - WithModifierUtility((v) => builder(WidgetModifiersDataDto([v]))); - late final intrinsicWidth = IntrinsicWidthWidgetUtility(only); late final intrinsicHeight = IntrinsicHeightWidgetUtility(only); late final rotate = RotatedBoxWidgetUtility(only); From f4828e6778902ca5fd80b583254636ea44d77fec Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Fri, 28 Jun 2024 10:08:11 -0300 Subject: [PATCH 14/27] rename RenderInlineModifiers to RenderSpecModifiers --- packages/mix/lib/src/modifiers/render_widget_modifier.dart | 4 ++-- packages/mix/lib/src/specs/box/box_widget.dart | 2 +- packages/mix/lib/src/specs/flex/flex_widget.dart | 2 +- packages/mix/lib/src/specs/icon/icon_widget.dart | 2 +- packages/mix/lib/src/specs/image/image_widget.dart | 2 +- packages/mix/lib/src/specs/stack/stack_widget.dart | 2 +- packages/mix/lib/src/specs/text/text_widget.dart | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/mix/lib/src/modifiers/render_widget_modifier.dart b/packages/mix/lib/src/modifiers/render_widget_modifier.dart index a90637039..ad216dde0 100644 --- a/packages/mix/lib/src/modifiers/render_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/render_widget_modifier.dart @@ -222,8 +222,8 @@ Set orderModifierSpecs( return specs.toSet(); } -class RenderInlineModifiers extends StatelessWidget { - const RenderInlineModifiers({ +class RenderSpecModifiers extends StatelessWidget { + const RenderSpecModifiers({ required this.orderOfModifiers, required this.child, required this.spec, diff --git a/packages/mix/lib/src/specs/box/box_widget.dart b/packages/mix/lib/src/specs/box/box_widget.dart index 4344276cf..81cf14743 100644 --- a/packages/mix/lib/src/specs/box/box_widget.dart +++ b/packages/mix/lib/src/specs/box/box_widget.dart @@ -73,7 +73,7 @@ class BoxSpecWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return RenderInlineModifiers( + return RenderSpecModifiers( orderOfModifiers: orderOfModifiers, spec: spec ?? const BoxSpec(), child: Container( diff --git a/packages/mix/lib/src/specs/flex/flex_widget.dart b/packages/mix/lib/src/specs/flex/flex_widget.dart index 0bb690f58..83ad00938 100644 --- a/packages/mix/lib/src/specs/flex/flex_widget.dart +++ b/packages/mix/lib/src/specs/flex/flex_widget.dart @@ -99,7 +99,7 @@ class FlexSpecWidget extends StatelessWidget { return spec == null ? flexWidget - : RenderInlineModifiers( + : RenderSpecModifiers( orderOfModifiers: orderOfModifiers, spec: spec!, child: flexWidget, diff --git a/packages/mix/lib/src/specs/icon/icon_widget.dart b/packages/mix/lib/src/specs/icon/icon_widget.dart index e4a20f6f9..a207b9f8a 100644 --- a/packages/mix/lib/src/specs/icon/icon_widget.dart +++ b/packages/mix/lib/src/specs/icon/icon_widget.dart @@ -62,7 +62,7 @@ class IconSpecWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return RenderInlineModifiers( + return RenderSpecModifiers( orderOfModifiers: orderOfModifiers, spec: spec ?? const IconSpec(), child: Icon( diff --git a/packages/mix/lib/src/specs/image/image_widget.dart b/packages/mix/lib/src/specs/image/image_widget.dart index 1d41c909a..e23d535f4 100644 --- a/packages/mix/lib/src/specs/image/image_widget.dart +++ b/packages/mix/lib/src/specs/image/image_widget.dart @@ -104,7 +104,7 @@ class ImageSpecWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return RenderInlineModifiers( + return RenderSpecModifiers( orderOfModifiers: orderOfModifiers, spec: spec ?? const ImageSpec(), child: Image( diff --git a/packages/mix/lib/src/specs/stack/stack_widget.dart b/packages/mix/lib/src/specs/stack/stack_widget.dart index 7a6404f71..55e9d36d0 100644 --- a/packages/mix/lib/src/specs/stack/stack_widget.dart +++ b/packages/mix/lib/src/specs/stack/stack_widget.dart @@ -55,7 +55,7 @@ class StackSpecWidget extends StatelessWidget { @override Widget build(BuildContext context) { // The Stack widget is used here, applying the resolved styles from StackSpec. - return RenderInlineModifiers( + return RenderSpecModifiers( orderOfModifiers: orderOfModifiers, spec: spec ?? const StackSpec(), child: Stack( diff --git a/packages/mix/lib/src/specs/text/text_widget.dart b/packages/mix/lib/src/specs/text/text_widget.dart index 5facb6ea9..e39755286 100644 --- a/packages/mix/lib/src/specs/text/text_widget.dart +++ b/packages/mix/lib/src/specs/text/text_widget.dart @@ -91,7 +91,7 @@ class TextSpecWidget extends StatelessWidget { @override Widget build(BuildContext context) { // The Text widget is used here, applying the resolved styles and properties from TextSpec. - return RenderInlineModifiers( + return RenderSpecModifiers( orderOfModifiers: const [], spec: spec ?? const TextSpec(), child: Text( From 673fb1334c1739757e9030c4eb3abb47c4769bf5 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Fri, 28 Jun 2024 10:33:51 -0300 Subject: [PATCH 15/27] reusing utilities --- .../modifiers/widget_modifiers_util.dart | 31 ++--------- .../src/modifiers/widget_modifiers_util.dart | 51 +++++++++++-------- 2 files changed, 35 insertions(+), 47 deletions(-) diff --git a/packages/mix/lib/src/attributes/modifiers/widget_modifiers_util.dart b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_util.dart index d39feb0db..e57f5a5aa 100644 --- a/packages/mix/lib/src/attributes/modifiers/widget_modifiers_util.dart +++ b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_util.dart @@ -1,35 +1,12 @@ import '../../core/core.dart'; import '../../modifiers/modifiers.dart'; -import '../spacing/spacing_util.dart'; import 'widget_modifiers_data_dto.dart'; -final class ModifierUtility - extends MixUtility { - late final intrinsicWidth = IntrinsicWidthWidgetUtility(only); - late final intrinsicHeight = IntrinsicHeightWidgetUtility(only); - late final rotate = RotatedBoxWidgetUtility(only); - late final opacity = OpacityUtility(only); - late final clipPath = ClipPathUtility(only); - late final clipRRect = ClipRRectUtility(only); - late final clipOval = ClipOvalUtility(only); - late final clipRect = ClipRectUtility(only); - late final clipTriangle = ClipTriangleUtility(only); - late final visibility = VisibilityUtility(only); - late final show = visibility.on; - late final hide = visibility.off; - late final aspectRatio = AspectRatioUtility(only); - late final flexible = FlexibleModifierUtility(only); - late final expanded = flexible.expanded; - late final transform = TransformUtility(only); - - late final scale = transform.scale; - late final align = AlignWidgetUtility(only); - late final fractionallySizedBox = FractionallySizedBoxModifierUtility(only); - late final sizedBox = SizedBoxModifierUtility(only); - late final padding = SpacingUtility(PaddingModifierUtility(only)); - - ModifierUtility(super.builder); +final class SpecModifierUtility + extends ModifierUtility { + SpecModifierUtility(super.builder); + @override T only(WidgetModifierAttribute attribute) { return builder(WidgetModifiersDataDto([attribute])); } diff --git a/packages/mix/lib/src/modifiers/widget_modifiers_util.dart b/packages/mix/lib/src/modifiers/widget_modifiers_util.dart index ca28f269e..4bebfd998 100644 --- a/packages/mix/lib/src/modifiers/widget_modifiers_util.dart +++ b/packages/mix/lib/src/modifiers/widget_modifiers_util.dart @@ -15,33 +15,44 @@ import 'sized_box_widget_modifier.dart'; import 'transform_widget_modifier.dart'; import 'visibility_widget_modifier.dart'; -class WithModifierUtility - extends MixUtility { - late final intrinsicWidth = IntrinsicWidthWidgetUtility(builder); - late final intrinsicHeight = IntrinsicHeightWidgetUtility(builder); - late final rotate = RotatedBoxWidgetUtility(builder); - late final opacity = OpacityUtility(builder); - late final clipPath = ClipPathUtility(builder); - late final clipRRect = ClipRRectUtility(builder); - late final clipOval = ClipOvalUtility(builder); - late final clipRect = ClipRectUtility(builder); - late final clipTriangle = ClipTriangleUtility(builder); - late final visibility = VisibilityUtility(builder); +abstract class ModifierUtility + extends MixUtility { + late final intrinsicWidth = IntrinsicWidthWidgetUtility(only); + late final intrinsicHeight = IntrinsicHeightWidgetUtility(only); + late final rotate = RotatedBoxWidgetUtility(only); + late final opacity = OpacityUtility(only); + late final clipPath = ClipPathUtility(only); + late final clipRRect = ClipRRectUtility(only); + late final clipOval = ClipOvalUtility(only); + late final clipRect = ClipRectUtility(only); + late final clipTriangle = ClipTriangleUtility(only); + late final visibility = VisibilityUtility(only); late final show = visibility.on; late final hide = visibility.off; - late final aspectRatio = AspectRatioUtility(builder); - late final flexible = FlexibleModifierUtility(builder); + late final aspectRatio = AspectRatioUtility(only); + late final flexible = FlexibleModifierUtility(only); late final expanded = flexible.expanded; - late final transform = TransformUtility(builder); + late final transform = TransformUtility(only); late final scale = transform.scale; - late final align = AlignWidgetUtility(builder); - late final fractionallySizedBox = - FractionallySizedBoxModifierUtility(builder); - late final sizedBox = SizedBoxModifierUtility(builder); - late final padding = SpacingUtility(PaddingModifierUtility(builder).call); + late final align = AlignWidgetUtility(only); + late final fractionallySizedBox = FractionallySizedBoxModifierUtility(only); + late final sizedBox = SizedBoxModifierUtility(only); + late final padding = SpacingUtility(PaddingModifierUtility(only).call); + + ModifierUtility(super.builder); + + T only(WidgetModifierAttribute attribute); +} +class WithModifierUtility + extends ModifierUtility { static final self = WithModifierUtility(MixUtility.selfBuilder); WithModifierUtility(super.builder); + + @override + T only(WidgetModifierAttribute attribute) { + return builder(attribute); + } } From 42bf992405f28adac64a40bc057eaf59b43d9dc8 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Fri, 28 Jun 2024 10:46:05 -0300 Subject: [PATCH 16/27] Update type_ref_repository.dart --- packages/mix_generator/lib/src/helpers/type_ref_repository.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mix_generator/lib/src/helpers/type_ref_repository.dart b/packages/mix_generator/lib/src/helpers/type_ref_repository.dart index 899db24ce..5a70adff8 100644 --- a/packages/mix_generator/lib/src/helpers/type_ref_repository.dart +++ b/packages/mix_generator/lib/src/helpers/type_ref_repository.dart @@ -16,7 +16,7 @@ class TypeRefRepository { static Map _utilityOverrides = { 'EdgeInsetsGeometry': 'SpacingUtility', 'AnimatedData': 'AnimatedUtility', - 'WidgetModifiersData': 'ModifierUtility', + 'WidgetModifiersData': 'SpecModifierUtility', }; static final _dtoMap = { From 3d37519a603353766e0b90e6db416ddd5646eba8 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Fri, 28 Jun 2024 10:48:31 -0300 Subject: [PATCH 17/27] update specs --- packages/mix/lib/src/specs/box/box_spec.g.dart | 2 +- packages/mix/lib/src/specs/flex/flex_spec.g.dart | 2 +- packages/mix/lib/src/specs/icon/icon_spec.g.dart | 2 +- packages/mix/lib/src/specs/image/image_spec.g.dart | 2 +- packages/mix/lib/src/specs/stack/stack_spec.g.dart | 2 +- packages/mix/lib/src/specs/text/text_spec.g.dart | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/mix/lib/src/specs/box/box_spec.g.dart b/packages/mix/lib/src/specs/box/box_spec.g.dart index c6b4521a8..33767445a 100644 --- a/packages/mix/lib/src/specs/box/box_spec.g.dart +++ b/packages/mix/lib/src/specs/box/box_spec.g.dart @@ -349,7 +349,7 @@ base class BoxSpecUtility late final height = DoubleUtility((v) => only(height: v)); /// Utility for defining [BoxSpecAttribute.modifiers] - late final modifiers = ModifierUtility((v) => only(modifiers: v)); + late final modifiers = SpecModifierUtility((v) => only(modifiers: v)); /// Utility for defining [BoxSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); diff --git a/packages/mix/lib/src/specs/flex/flex_spec.g.dart b/packages/mix/lib/src/specs/flex/flex_spec.g.dart index 9652105f9..8c6baadd1 100644 --- a/packages/mix/lib/src/specs/flex/flex_spec.g.dart +++ b/packages/mix/lib/src/specs/flex/flex_spec.g.dart @@ -276,7 +276,7 @@ base class FlexSpecUtility late final animated = AnimatedUtility((v) => only(animated: v)); /// Utility for defining [FlexSpecAttribute.modifiers] - late final modifiers = ModifierUtility((v) => only(modifiers: v)); + late final modifiers = SpecModifierUtility((v) => only(modifiers: v)); FlexSpecUtility(super.builder); diff --git a/packages/mix/lib/src/specs/icon/icon_spec.g.dart b/packages/mix/lib/src/specs/icon/icon_spec.g.dart index 2c7ecb979..d4d7e7277 100644 --- a/packages/mix/lib/src/specs/icon/icon_spec.g.dart +++ b/packages/mix/lib/src/specs/icon/icon_spec.g.dart @@ -267,7 +267,7 @@ base class IconSpecUtility late final animated = AnimatedUtility((v) => only(animated: v)); /// Utility for defining [IconSpecAttribute.modifiers] - late final modifiers = ModifierUtility((v) => only(modifiers: v)); + late final modifiers = SpecModifierUtility((v) => only(modifiers: v)); IconSpecUtility(super.builder); diff --git a/packages/mix/lib/src/specs/image/image_spec.g.dart b/packages/mix/lib/src/specs/image/image_spec.g.dart index e044551e2..8023c57f2 100644 --- a/packages/mix/lib/src/specs/image/image_spec.g.dart +++ b/packages/mix/lib/src/specs/image/image_spec.g.dart @@ -267,7 +267,7 @@ base class ImageSpecUtility late final animated = AnimatedUtility((v) => only(animated: v)); /// Utility for defining [ImageSpecAttribute.modifiers] - late final modifiers = ModifierUtility((v) => only(modifiers: v)); + late final modifiers = SpecModifierUtility((v) => only(modifiers: v)); ImageSpecUtility(super.builder); diff --git a/packages/mix/lib/src/specs/stack/stack_spec.g.dart b/packages/mix/lib/src/specs/stack/stack_spec.g.dart index 5ea664d84..41d0cc8a5 100644 --- a/packages/mix/lib/src/specs/stack/stack_spec.g.dart +++ b/packages/mix/lib/src/specs/stack/stack_spec.g.dart @@ -204,7 +204,7 @@ base class StackSpecUtility late final animated = AnimatedUtility((v) => only(animated: v)); /// Utility for defining [StackSpecAttribute.modifiers] - late final modifiers = ModifierUtility((v) => only(modifiers: v)); + late final modifiers = SpecModifierUtility((v) => only(modifiers: v)); StackSpecUtility(super.builder); diff --git a/packages/mix/lib/src/specs/text/text_spec.g.dart b/packages/mix/lib/src/specs/text/text_spec.g.dart index 16e3b4caa..9c7d9efc2 100644 --- a/packages/mix/lib/src/specs/text/text_spec.g.dart +++ b/packages/mix/lib/src/specs/text/text_spec.g.dart @@ -322,7 +322,7 @@ base class TextSpecUtility late final animated = AnimatedUtility((v) => only(animated: v)); /// Utility for defining [TextSpecAttribute.modifiers] - late final modifiers = ModifierUtility((v) => only(modifiers: v)); + late final modifiers = SpecModifierUtility((v) => only(modifiers: v)); TextSpecUtility(super.builder); From 584385d537650c277f83d0b04ccdacc6a964695c Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Tue, 2 Jul 2024 16:51:20 -0300 Subject: [PATCH 18/27] deprecate --- .../src/modifiers/render_widget_modifier.dart | 139 +++++- packages/mix/lib/src/specs/box/box_spec.dart | 1 + .../mix/lib/src/specs/icon/icon_widget.dart | 42 +- .../widget_modifier_widget_test.dart | 411 ++++++++++++++++ .../widget_modifiers_util_test.dart | 441 ++++++++++++++++++ 5 files changed, 1006 insertions(+), 28 deletions(-) create mode 100644 packages/mix/test/deprecated/widget_modifier_widget_test.dart create mode 100644 packages/mix/test/deprecated/widget_modifiers_util_test.dart diff --git a/packages/mix/lib/src/modifiers/render_widget_modifier.dart b/packages/mix/lib/src/modifiers/render_widget_modifier.dart index ad216dde0..13a8c40d4 100644 --- a/packages/mix/lib/src/modifiers/render_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/render_widget_modifier.dart @@ -17,6 +17,7 @@ import 'transform_widget_modifier.dart'; import 'visibility_widget_modifier.dart'; // Default order of modifiers and their logic: +@Deprecated("Use _defaultSpecsOrder instead") const _defaultOrder = [ // 1. VisibilityModifier: Controls overall visibility. If the widget is set to be invisible, // none of the subsequent decorations are processed, providing an early exit and optimizing performance. @@ -70,35 +71,100 @@ const _defaultOrder = [ OpacityModifierAttribute, ]; -const _defaultOrderSpecs = [ +const _defaultSpecsOrder = [ + // 1. VisibilityModifier: Controls overall visibility. If the widget is set to be invisible, + // none of the subsequent decorations are processed, providing an early exit and optimizing performance. VisibilityModifierSpec, + + // 2. SizedBoxModifier: Explicitly sets the size of the widget before any other transformations are applied. + // This ensures that the widget occupies a predetermined space, which is crucial for layouts that require exact dimensions. SizedBoxModifierSpec, + + // 3. FractionallySizedBoxModifier: Adjusts the widget's size relative to its parent's size, + // allowing for responsive layouts that scale with the parent widget. This modifier is applied after + // explicit sizing to refine the widget's dimensions based on available space. FractionallySizedBoxModifierSpec, + + // 4. AlignModifier: Aligns the widget within its allocated space, which is especially important + // for positioning the widget correctly before applying any transformations that could affect its position. + // Alignment is based on the size constraints established by previous modifiers. AlignModifierSpec, + + // 5. IntrinsicHeightModifier: Adjusts the widget's height to fit its child's intrinsic height, + // ensuring that the widget does not force its children to conform to an unnatural height. This is particularly + // useful for widgets that should size themselves based on content. IntrinsicHeightModifierSpec, + + // 6. IntrinsicWidthModifier: Similar to the IntrinsicHeightModifier, this adjusts the widget's width + // to its child's intrinsic width. This modifier allows for content-driven width adjustments, making it ideal + // for widgets that need to wrap their content tightly. IntrinsicWidthModifierSpec, + + // 7. AspectRatioModifier: Maintains the widget's aspect ratio after sizing adjustments. + // This modifier ensures that the widget scales correctly within its given aspect ratio constraints, + // which is critical for preserving the visual integrity of images and other aspect-sensitive content. AspectRatioModifierSpec, + + // 9. TransformModifier: Applies arbitrary transformations, such as rotation, scaling, and translation. + // Transformations are applied after all sizing and positioning adjustments to modify the widget's appearance + // and position in more complex ways without altering the logical layout. TransformModifierSpec, + + // 10. Clip Modifiers: Applies clipping in various shapes to the transformed widget, shaping the final appearance. + // Clipping is one of the last steps to ensure it is applied to the widget's final size, position, and transformation state. ClipOvalModifierSpec, ClipRRectModifierSpec, ClipPathModifierSpec, ClipTriangleModifierSpec, ClipRectModifierSpec, + + // 11. OpacityModifier: Modifies the widget's opacity as the final decoration step. Applying opacity last ensures + // that it does not influence the layout or transformations, serving purely as a visual effect to alter the transparency + // of the widget and its decorations. OpacityModifierSpec, ]; class RenderModifiers extends StatelessWidget { const RenderModifiers({ required this.child, - required this.modifiers, + //TODO Should be required in the next version + this.modifiers = const >{}, + @Deprecated("Use modifiers parameter") this.mix, + @Deprecated("Use modifiers parameter") this.orderOfModifiers, super.key, }); final Widget child; + final MixData? mix; + final List? orderOfModifiers; final Set> modifiers; + @Deprecated("Should be removed in the next major version") + Widget deprecatedBuild() { + final specs = resolveModifierSpecs(orderOfModifiers!, mix!); + + var current = child; + + for (final spec in specs) { + current = spec.build(current); + } + + return current; + } + @override Widget build(BuildContext context) { + assert( + (mix == null) || (modifiers.isEmpty), + ); + assert( + (orderOfModifiers?.isEmpty ?? true) || (modifiers.isEmpty), + ); + + if (mix != null && orderOfModifiers != null) { + return deprecatedBuild(); + } + var current = child; for (final spec in modifiers) { @@ -111,16 +177,21 @@ class RenderModifiers extends StatelessWidget { class RenderAnimatedModifiers extends ImplicitlyAnimatedWidget { const RenderAnimatedModifiers({ - required this.modifiers, + //TODO Should be required in the next version + this.modifiers = const {}, required this.child, required super.duration, + @Deprecated("Use modifiers parameter") this.mix, + @Deprecated("Use modifiers parameter") + this.orderOfModifiers = const [], super.key, super.curve = Curves.linear, super.onEnd, }); final Widget child; - + final MixData? mix; + final List? orderOfModifiers; final Set> modifiers; @override @@ -133,6 +204,10 @@ class RenderAnimatedModifiersState @override void forEachTween(TweenVisitor visitor) { + if (widget.mix != null && widget.orderOfModifiers != null) { + deprecatedForEachTween(visitor); + } + for (final spec in widget.modifiers) { final specType = spec.runtimeType; final previousSpec = _specs[specType]; @@ -145,8 +220,32 @@ class RenderAnimatedModifiersState } } + @Deprecated('should be deleted in the next major version') + void deprecatedForEachTween(TweenVisitor visitor) { + final specs = resolveModifierSpecs(widget.orderOfModifiers!, widget.mix!); + + for (final spec in specs) { + final specType = spec.runtimeType; + final previousSpec = _specs[specType]; + + _specs[specType] = visitor( + previousSpec, + spec, + (dynamic value) => + ModifierSpecTween(begin: value as WidgetModifierSpec), + ) as ModifierSpecTween; + } + } + @override Widget build(BuildContext context) { + assert( + (widget.mix == null) || (widget.modifiers.isEmpty), + ); + assert( + (widget.orderOfModifiers?.isEmpty ?? true) || (widget.modifiers.isEmpty), + ); + var current = widget.child; for (final spec in _specs.keys) { @@ -158,17 +257,6 @@ class RenderAnimatedModifiersState } } -Set resolveModifierSpecs( - List orderOfModifiers, - MixData mix, -) { - final modifiers = mix.whereType(); - - if (modifiers.isEmpty) return {}; - - return orderModifierSpecs(orderOfModifiers, mix, modifiers); -} - Set> orderSpecs( List orderOfModifiers, [ Set> modifiers = const {}, @@ -177,7 +265,7 @@ Set> orderSpecs( // Prioritize the order of modifiers provided by the user. ...orderOfModifiers, // Add the default order of modifiers. - ..._defaultOrderSpecs, + ..._defaultSpecsOrder, // Add any remaining modifiers that were not included in the order. ...modifiers.map((e) => e.type), }).toList(); @@ -194,9 +282,20 @@ Set> orderSpecs( return specs; } -Set orderModifierSpecs( +Set> resolveModifierSpecs( List orderOfModifiers, MixData mix, +) { + final modifiers = mix.whereType(); + + if (modifiers.isEmpty) return {}; + final orderedModifiers = orderModifierSpecs(orderOfModifiers, modifiers); + + return orderedModifiers.map((e) => e.resolve(mix)).toSet(); +} + +Iterable orderModifierSpecs( + List orderOfModifiers, Iterable modifiers, ) { final modifierMap = AttributeMap(modifiers).toMap(); @@ -210,16 +309,16 @@ Set orderModifierSpecs( ...modifierMap.keys, }.toList().reversed; - final specs = []; + final specs = []; for (final modifierType in listOfModifiers) { // Resolve the modifier and add it to the list of specs. final modifier = modifierMap.remove(modifierType); if (modifier == null) continue; - specs.add(modifier.resolve(mix) as WidgetModifierSpec); + specs.add(modifier); } - return specs.toSet(); + return specs; } class RenderSpecModifiers extends StatelessWidget { diff --git a/packages/mix/lib/src/specs/box/box_spec.dart b/packages/mix/lib/src/specs/box/box_spec.dart index 34d50703e..c94f84e2b 100644 --- a/packages/mix/lib/src/specs/box/box_spec.dart +++ b/packages/mix/lib/src/specs/box/box_spec.dart @@ -106,6 +106,7 @@ final class BoxSpec extends Spec with _$BoxSpec { spec: this, duration: animated!.duration, curve: animated!.curve, + orderOfModifiers: orderOfModifiers, child: child, ) : BoxSpecWidget( diff --git a/packages/mix/lib/src/specs/icon/icon_widget.dart b/packages/mix/lib/src/specs/icon/icon_widget.dart index a207b9f8a..3de0b5134 100644 --- a/packages/mix/lib/src/specs/icon/icon_widget.dart +++ b/packages/mix/lib/src/specs/icon/icon_widget.dart @@ -12,8 +12,15 @@ class StyledIcon extends StyledWidget { super.key, super.inherit = true, this.textDirection, - super.orderOfModifiers = const [], - }); + @Deprecated('Use orderOfModifiers parameter instead') + List modifierOrder = const [], + List orderOfModifiers = const [], + }) : assert(modifierOrder == const [] || + orderOfModifiers == const []), + super( + orderOfModifiers: + orderOfModifiers == const [] ? modifierOrder : orderOfModifiers, + ); final IconData? icon; final String? semanticLabel; @@ -51,8 +58,13 @@ class IconSpecWidget extends StatelessWidget { this.semanticLabel, super.key, this.textDirection, - this.orderOfModifiers = const [], - }); + @Deprecated('Use orderOfModifiers parameter instead') + List modifierOrder = const [], + List orderOfModifiers = const [], + }) : assert(modifierOrder == const [] || + orderOfModifiers == const []), + orderOfModifiers = + orderOfModifiers == const [] ? modifierOrder : orderOfModifiers; final IconData? icon; final IconSpec? spec; @@ -90,8 +102,16 @@ class AnimatedStyledIcon extends StyledWidget { required this.progress, super.inherit, this.textDirection, - super.orderOfModifiers = const [], - }); + @Deprecated('Use orderOfModifiers parameter instead') + List modifierOrder = const [], + List orderOfModifiers = const [], + }) : assert(modifierOrder == const [] || + orderOfModifiers == const []), + super( + orderOfModifiers: orderOfModifiers == const [] + ? modifierOrder + : orderOfModifiers, + ); final AnimatedIconData icon; final String? semanticLabel; @@ -122,11 +142,17 @@ class AnimatedIconSpecWidget extends ImplicitlyAnimatedWidget { super.key, this.semanticLabel, this.textDirection, - this.orderOfModifiers = const [], super.curve, required super.duration, super.onEnd, - }); + @Deprecated('Use orderOfModifiers parameter instead') + List modifierOrder = const [], + List orderOfModifiers = const [], + }) : assert(modifierOrder == const [] || + orderOfModifiers == const []), + orderOfModifiers = orderOfModifiers == const [] + ? modifierOrder + : orderOfModifiers; final IconData? icon; final IconSpec spec; diff --git a/packages/mix/test/deprecated/widget_modifier_widget_test.dart b/packages/mix/test/deprecated/widget_modifier_widget_test.dart new file mode 100644 index 000000000..d7fd49411 --- /dev/null +++ b/packages/mix/test/deprecated/widget_modifier_widget_test.dart @@ -0,0 +1,411 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +import '../helpers/testing_utils.dart'; + +void main() { + final style = Style( + $with.scale(2.0), + $with.opacity(0.5), + $with.visibility.on(), + $with.clipOval(), + $with.aspectRatio(2.0), + const CustomModifierAttribute(), + ); + + final mixData = MixData.create(MockBuildContext(), style); + + group('RenderModifiers', () { + testWidgets('Renders modifiers in the correct order', (tester) async { + await tester.pumpMaterialApp( + RenderModifiers( + mix: mixData, + orderOfModifiers: const [], + child: const Text('child'), + ), + ); + + expect(find.byType(RenderModifiers), findsOneWidget); + + expect( + find.descendant( + of: find.byType(RenderModifiers), + matching: find.byType(Visibility), + ), + findsOneWidget, + ); + + // Similarly, check for AspectRatio, Scale, Clip, and Opacity + // Ensure each widget is a descendant of the previous one in the correct order + final aspectRatioFinder = find.descendant( + of: find.byType(Visibility), + matching: find.byType(AspectRatio), + ); + expect(aspectRatioFinder, findsOneWidget); + + final scaleFinder = find.descendant( + of: aspectRatioFinder, + matching: find.byType(Transform), // Assuming Scale uses Transform + ); + expect(scaleFinder, findsOneWidget); + + final clipFinder = find.descendant( + of: scaleFinder, + matching: find.byType(ClipOval), // Assuming Clip uses ClipOval + ); + expect(clipFinder, findsOneWidget); + + final opacityFinder = find.descendant( + of: clipFinder, + matching: find.byType(Opacity), + ); + expect(opacityFinder, findsOneWidget); + + final customWidgetFinder = find.descendant( + of: opacityFinder, + matching: find.byType(Padding), + ); + + expect(customWidgetFinder, findsOneWidget); + + expect( + find.descendant( + of: customWidgetFinder, + matching: find.text('child'), + ), + findsOneWidget, + ); + }); + + testWidgets('Renders child when no modifiers are provided', (tester) async { + final mixData = MixData.create(MockBuildContext(), Style()); + + await tester.pumpMaterialApp( + RenderModifiers( + mix: mixData, + orderOfModifiers: const [], + child: const Text('child'), + ), + ); + + expect(find.text('child'), findsOneWidget); + expect(find.byType(RenderModifiers), findsOneWidget); + }); + + testWidgets('Renders child when orderOfModifiers is empty', (tester) async { + final mixData = MixData.create(MockBuildContext(), style); + + await tester.pumpMaterialApp( + RenderModifiers( + mix: mixData, + orderOfModifiers: const [], + child: const Text('child'), + ), + ); + + expect(find.text('child'), findsOneWidget); + expect(find.byType(RenderModifiers), findsOneWidget); + }); + + testWidgets( + 'Renders modifiers in the correct order with many overrides', + (tester) async { + await tester.pumpMaterialApp( + RenderModifiers( + mix: mixData, + orderOfModifiers: const [ + ClipOvalModifierAttribute, + AspectRatioModifierAttribute, + TransformModifierAttribute, + OpacityModifierAttribute, + VisibilityModifierAttribute, + ], + child: const Text('child'), + ), + ); + + expect(find.byType(RenderModifiers), findsOneWidget); + + expect( + find.descendant( + of: find.byType(RenderModifiers), + matching: find.byType(ClipOval), + ), + findsOneWidget, + ); + + expect( + find.descendant( + of: find.byType(ClipOval), + matching: find.byType(AspectRatio), + ), + findsOneWidget, + ); + + expect( + find.descendant( + of: find.byType(AspectRatio), + matching: find.byType(Transform), + ), + findsOneWidget, + ); + + expect( + find.descendant( + of: find.byType(Transform), + matching: find.byType(Opacity), + ), + findsOneWidget, + ); + + expect( + find.descendant( + of: find.byType(Opacity), + matching: find.byType(Padding), + ), + findsOneWidget, + ); + + expect( + find.descendant( + of: find.byType(Padding), + matching: find.text('child'), + ), + findsOneWidget, + ); + }, + ); + + // Allow for only a few overrides + testWidgets( + 'Renders modifiers in the correct order with a few overrides', + (tester) async { + await tester.pumpMaterialApp( + RenderModifiers( + mix: mixData, + orderOfModifiers: const [ + ClipOvalModifierAttribute, + AspectRatioModifierAttribute + ], + child: const Text('child'), + ), + ); + + expect(find.byType(RenderModifiers), findsOneWidget); + + expect( + find.descendant( + of: find.byType(RenderModifiers), + matching: find.byType(ClipOval), + ), + findsOneWidget, + ); + + expect( + find.descendant( + of: find.byType(ClipOval), + matching: find.byType(AspectRatio), + ), + findsOneWidget, + ); + + expect( + find.descendant( + of: find.byType(AspectRatio), + matching: find.byType(Visibility), + ), + findsOneWidget, + ); + + expect( + find.descendant( + of: find.byType(Visibility), + matching: find.byType(Transform), + ), + findsOneWidget, + ); + + expect( + find.descendant( + of: find.byType(Transform), + matching: find.byType(Opacity), + ), + findsOneWidget, + ); + + expect( + find.descendant( + of: find.byType(Opacity), + matching: find.byType(Padding), + ), + findsOneWidget, + ); + + expect( + find.descendant( + of: find.byType(Padding), + matching: find.text('child'), + ), + findsOneWidget, + ); + }, + ); + }); + + group('RenderAnimatedModifiers', () { + testWidgets('Renders animated modifiers', (tester) async { + final mixData = MixData.create(MockBuildContext(), style); + + await tester.pumpMaterialApp( + RenderAnimatedModifiers( + mix: mixData, + orderOfModifiers: const [], + duration: const Duration(milliseconds: 300), + child: const Text('child'), + ), + ); + + expect(find.text('child'), findsOneWidget); + expect(find.byType(RenderAnimatedModifiers), findsOneWidget); + + // Trigger animation and pump frames + await tester.pump(const Duration(milliseconds: 150)); + await tester.pump(const Duration(milliseconds: 150)); + }); + }); + + group('Modifiers attributes', () { + testWidgets( + 'should be applied to the first one. The children wont inherit even though the second one is set to inherit', + (tester) async { + const key = Key('box'); + + await tester.pumpWidget( + Box( + style: Style( + $with.scale(2.0), + $with.opacity(0.5), + $with.visibility.on(), + $with.clipOval(), + $with.aspectRatio(2.0), + ), + child: Box( + key: key, + inherit: true, + child: Builder(builder: (context) { + final inheritedMix = Mix.maybeOf(context)!; + + expect(inheritedMix.attributes.length, 0); + + return const SizedBox(); + }), + ), + ), + ); + + expect( + find.descendant( + of: find.byKey(key), + matching: find.byType(Transform), + ), + findsNothing, + ); + + expect( + find.descendant( + of: find.byKey(key), + matching: find.byType(Opacity), + ), + findsNothing, + ); + + expect( + find.descendant( + of: find.byKey(key), + matching: find.byType(RotatedBox), + ), + findsNothing, + ); + + expect( + find.descendant( + of: find.byKey(key), + matching: find.byType(Visibility), + ), + findsNothing, + ); + + expect( + find.descendant( + of: find.byKey(key), + matching: find.byType(AspectRatio), + ), + findsNothing, + ); + }, + ); + + testWidgets( + 'If there are no modifier attributes in Style, RenderModifierAttributes shouldnt exist in the widget tree', + (tester) async { + const key = Key('box'); + + await tester.pumpWidget( + Box( + style: Style($box.color.red(), $box.height(100), $box.width(100)), + key: key, + ), + ); + + expect( + find.ancestor( + of: find.byKey(key), + matching: find.byType(RenderModifiers), + ), + findsNothing, + ); + }, + ); + }); + + group('resolveModifierSpecs', () { + test('Returns empty set when no modifiers are provided', () { + final result = resolveModifierSpecs(const [], EmptyMixData); + expect(result, isEmpty); + }); + + test('Returns resolved modifier specs in the correct order', () { + final style = Style( + $with.scale(2.0), + $with.opacity(0.5), + $with.visibility.on(), + $with.clipOval(), + $with.aspectRatio(2.0), + ); + + final mix = style.of(MockBuildContext()); + final result = resolveModifierSpecs( + [ + ClipOvalModifierAttribute, + AspectRatioModifierAttribute, + TransformModifierAttribute, + OpacityModifierAttribute, + VisibilityModifierAttribute, + ], + mix, + ); + + expect(result, { + const VisibilityModifierSpec(true), + const OpacityModifierSpec(0.5), + TransformModifierSpec( + transform: Matrix4.diagonal3Values(2.0, 2.0, 1.0), + alignment: Alignment.center, + ), + const AspectRatioModifierSpec(2.0), + const ClipOvalModifierSpec(), + }); + }); + }); +} diff --git a/packages/mix/test/deprecated/widget_modifiers_util_test.dart b/packages/mix/test/deprecated/widget_modifiers_util_test.dart new file mode 100644 index 000000000..66f69dc3d --- /dev/null +++ b/packages/mix/test/deprecated/widget_modifiers_util_test.dart @@ -0,0 +1,441 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +import '../empty_widget.dart'; +import '../helpers/testing_utils.dart'; + +void main() { + group('Modifiers: ', () { + const aspectRatio = AspectRatioUtility(UtilityTestAttribute.new); + const flexible = FlexibleModifierUtility(UtilityTestAttribute.new); + const visibility = VisibilityUtility(UtilityTestAttribute.new); + const transform = TransformUtility(UtilityTestAttribute.new); + + const opacity = OpacityUtility(UtilityTestAttribute.new); + const rotate = RotatedBoxWidgetUtility(UtilityTestAttribute.new); + const clipPath = ClipPathUtility(UtilityTestAttribute.new); + const clipRRect = ClipRRectUtility(UtilityTestAttribute.new); + const clipOval = ClipOvalUtility(UtilityTestAttribute.new); + const clipRect = ClipRectUtility(UtilityTestAttribute.new); + const clipTriangle = ClipTriangleUtility(UtilityTestAttribute.new); + final sizedBox = SizedBoxModifierUtility(UtilityTestAttribute.new); + const fractionallySizedBox = + FractionallySizedBoxModifierUtility(UtilityTestAttribute.new); + const intrinsicHeight = + IntrinsicHeightWidgetUtility(UtilityTestAttribute.new); + const intrinsicWidth = + IntrinsicWidthWidgetUtility(UtilityTestAttribute.new); + const align = AlignWidgetUtility(UtilityTestAttribute.new); + + test('aspectRatio creates AspectRatioModifier correctly', () { + final aspectRatioModifier = aspectRatio(2.0); + + expect(aspectRatioModifier.value.aspectRatio, 2.0); + }); + + test('expanded creates FlexibleModifierUtility correctly', () { + const flexible = FlexibleModifierUtility(UtilityTestAttribute.new); + final flexibleModifier = flexible.expanded(); + + expect(flexibleModifier.value.fit, FlexFit.tight); + }); + + test('default flexible creates FlexibleModifierUtility correctly', () { + final flexibleModifier = flexible(); + final widget = flexibleModifier.value + .resolve(EmptyMixData) + .build(const Empty()) as Flexible; + + expect(flexibleModifier.value.fit, null); + expect(widget, isA()); + expect(widget.fit, FlexFit.loose); + expect(widget.flex, 1); + }); + + test('opacity creates OpacityModifier correctly', () { + final opacityModifier = opacity(0.5); + + expect(opacityModifier.value.opacity, 0.5); + }); + + test('rotate creates RotateModifier correctly', () { + final rotateModifier = rotate(2); + + expect(rotateModifier.value.quarterTurns, 2); + }); + + test('rotate90 creates RotateModifier correctly', () { + final rotateModifier = rotate.d90(); + + expect(rotateModifier.value.quarterTurns, 1); + }); + + test('rotate180 creates RotateModifier correctly', () { + final rotateModifier = rotate.d180(); + + expect(rotateModifier.value.quarterTurns, 2); + }); + + test('rotate270 creates RotateModifier correctly', () { + final rotateModifier = rotate.d270(); + + expect(rotateModifier.value.quarterTurns, 3); + }); + + test('clipRRect creates ClipRRectModifier correctly', () { + final clipRRectModifier = + clipRRect(borderRadius: BorderRadius.circular(10.0)); + + final modifier = clipRRectModifier.value; + + expect(modifier.borderRadius, BorderRadius.circular(10.0)); + }); + + test('clipOval creates ClipOvalModifier correctly', () { + final clipOvalModifier = clipOval(); + + expect( + clipOvalModifier.value.resolve(EmptyMixData).build(const Empty()), + isA(), + ); + }); + test('clipPath creates ClipPathModifier correctly', () { + final clipPathModifier = clipPath(); + + expect( + clipPathModifier.value.resolve(EmptyMixData).build(const Empty()), + isA(), + ); + }); + + // clipTriangle + test('clipTriangle creates ClipTriangleModifier correctly', () { + final clipTriangleModifier = clipTriangle(); + + expect( + clipTriangleModifier.value.resolve(EmptyMixData).build(const Empty()), + isA(), + ); + }); + + test('intrinsicHeight creates IntrinsicHeightModifier correctly', () { + final widget = intrinsicHeight() + .value + .resolve(EmptyMixData) + .build(const Empty()) as IntrinsicHeight; + + expect(widget, isA()); + }); + + test('intrinsicWidth creates IntrinsicWidthModifier correctly', () { + final widget = intrinsicWidth() + .value + .resolve(EmptyMixData) + .build(const Empty()) as IntrinsicWidth; + + expect(widget, isA()); + }); + + test('clipRect creates ClipRectModifier correctly', () { + final clipRectModifier = clipRect(); + + expect( + clipRectModifier.value.resolve(EmptyMixData).build(const Empty()), + isA(), + ); + }); + + test('visibility creates VisibilityModifier correctly', () { + final visibilityModifier = visibility.on(); + expect(visibilityModifier.value.visible, true); + }); + + test('transform creates TransformModifier correctly', () { + final transformModifier = transform(Matrix4.identity()); + + expect(transformModifier.value.transform, Matrix4.identity()); + }); + test('sizedBox creates SizedBoxModifier correctly', () { + final sizedBoxModifier = sizedBox(height: 100, width: 100); + + final widget = sizedBoxModifier.value + .resolve(EmptyMixData) + .build(const Empty()) as SizedBox; + + expect(widget.width, 100); + expect(widget.height, 100); + }); + + test( + 'fractionallySizedBox creates FractionallySizedBoxModifier correctly', + () { + final fractionallySizedBoxModifier = fractionallySizedBox( + heightFactor: 0.5, + widthFactor: 0.5, + ); + + final widget = fractionallySizedBoxModifier.value + .resolve(EmptyMixData) + .build(const Empty()) as FractionallySizedBox; + + expect(widget.widthFactor, 0.5); + expect(widget.heightFactor, 0.5); + }, + ); + + // align + test('align creates AlignModifier correctly', () { + final alignModifier = align(alignment: Alignment.center); + + final widget = alignModifier.value + .resolve(EmptyMixData) + .build(const Empty()) as Align; + + expect(widget.alignment, Alignment.center); + }); + }); + group('Applying Modifiers in Style', () { + testWidgets( + 'Applying an intrinsicHeight must add an IntrinsicHeight in widget tree', + (tester) async { + await tester.pumpWidget( + _TestableRenderModifier( + Style( + $with.intrinsicHeight(), + ), + ), + ); + + _expectOneWidgetOfType(); + }, + ); + + testWidgets( + 'Applying a scale must add a ScaleTransition in widget tree', + (tester) async { + await tester.pumpWidget( + _TestableRenderModifier( + Style( + $with.scale(2.0), + ), + ), + ); + + _expectOneWidgetOfType(); + }, + ); + + testWidgets( + 'Applying an opacity must add an Opacity in widget tree', + (tester) async { + await tester.pumpWidget( + _TestableRenderModifier( + Style( + $with.opacity(0.5), + ), + ), + ); + + _expectOneWidgetOfType(); + }, + ); + + testWidgets( + 'Applying a clipPath must add a ClipPath in widget tree', + (tester) async { + await tester.pumpWidget( + _TestableRenderModifier( + Style( + $with.clipPath(), + ), + ), + ); + + _expectOneWidgetOfType(); + }, + ); + + testWidgets( + 'Applying a clipRRect must add a ClipRRect in widget tree', + (tester) async { + await tester.pumpWidget( + _TestableRenderModifier( + Style( + $with.clipRRect(), + ), + ), + ); + + _expectOneWidgetOfType(); + }, + ); + + testWidgets( + 'Applying a clipOval must add a ClipOval in widget tree', + (tester) async { + await tester.pumpWidget( + _TestableRenderModifier( + Style( + $with.clipOval(), + ), + ), + ); + + _expectOneWidgetOfType(); + }, + ); + + testWidgets( + 'Applying a clipRect must add a ClipRect in widget tree', + (tester) async { + await tester.pumpWidget( + _TestableRenderModifier( + Style( + $with.clipRect(), + ), + ), + ); + + _expectOneWidgetOfType(); + }, + ); + + testWidgets( + 'Applying a visibility must add a Visibility widget in the widget tree', + (tester) async { + await tester.pumpWidget( + _TestableRenderModifier( + Style( + $with.visibility.off(), + ), + ), + ); + + _expectOneWidgetOfType(); + }, + ); + + testWidgets( + 'Applying an aspectRatio must add an AspectRatio widget in the widget tree', + (tester) async { + await tester.pumpWidget( + _TestableRenderModifier( + Style( + $with.aspectRatio(2), + ), + ), + ); + + _expectOneWidgetOfType(); + }, + ); + + testWidgets( + 'Applying a flexible must add a Flexible widget in the widget tree', + (tester) async { + await tester.pumpWidget( + Column( + children: [ + _TestableRenderModifier( + Style( + $with.flexible(), + ), + ), + ], + ), + ); + + _expectOneWidgetOfType(); + }, + ); + + testWidgets( + 'Applying a transform must add a Transform widget in the widget tree', + (tester) async { + await tester.pumpWidget( + _TestableRenderModifier( + Style( + $with.transform(Matrix4.identity()), + ), + ), + ); + + _expectOneWidgetOfType(); + }, + ); + + testWidgets( + 'Applying an align must add an Align widget in the widget tree', + (tester) async { + await tester.pumpWidget( + _TestableRenderModifier( + Style( + $with.align(), + ), + ), + ); + + _expectOneWidgetOfType(); + }, + ); + + testWidgets( + 'Applying a fractionallySizedBox must add a FractionallySizedBox widget in the widget tree', + (tester) async { + await tester.pumpWidget( + _TestableRenderModifier( + Style( + $with.fractionallySizedBox(), + ), + ), + ); + + _expectOneWidgetOfType(); + }, + ); + + testWidgets( + 'Applying a sizedBox must add a SizedBox widget in the widget tree', + (tester) async { + await tester.pumpWidget( + _TestableRenderModifier( + Style( + $with.sizedBox(), + ), + ), + ); + + _expectOneWidgetOfType(); + }, + ); + }); +} + +void _expectOneWidgetOfType() { + expect( + find.descendant( + of: find.byType(_TestableRenderModifier), + matching: find.byType(T), + ), + findsOneWidget, + ); +} + +class _TestableRenderModifier extends StatelessWidget { + const _TestableRenderModifier(this.style); + + final Style style; + + @override + Widget build(BuildContext context) { + return RenderModifiers( + orderOfModifiers: const [], + mix: MixData.create( + context, + style, + ), + child: Container(), + ); + } +} From 10cedd9afe5b28ed4aa07d5097aa9602fd291013 Mon Sep 17 00:00:00 2001 From: Leo Farias Date: Wed, 3 Jul 2024 15:13:37 -0400 Subject: [PATCH 19/27] Updates on conditional logic --- melos.yaml | 4 + packages/mix/lib/src/core/attributes_map.dart | 3 +- .../mix/lib/src/core/factory/mix_data.dart | 14 +- packages/mix/lib/src/core/modifier.dart | 1 - packages/mix/lib/src/core/styled_widget.dart | 15 +- .../src/modifiers/render_widget_modifier.dart | 247 ++++++------------ .../render_widget_modifier_test.dart | 24 +- .../src/factory/mix_provider_data_test.dart | 46 ++++ .../widget_modifier_widget_test.dart | 27 +- .../modifiers/widget_modifiers_util_test.dart | 10 +- 10 files changed, 182 insertions(+), 209 deletions(-) diff --git a/melos.yaml b/melos.yaml index 877a6c11d..60d809a22 100644 --- a/melos.yaml +++ b/melos.yaml @@ -43,6 +43,10 @@ scripts: custom_lint_analyze: run: dart pub global activate custom_lint && melos exec --depends-on="mix_lint" custom_lint + mix_exports: + run: melos exec --scope="packages/mix" dart run ./scripts/exports.dart + description: Generate exports for the mix package + analyze: run: | dcm analyze --fatal-warnings --fatal-style --fatal-performance . diff --git a/packages/mix/lib/src/core/attributes_map.dart b/packages/mix/lib/src/core/attributes_map.dart index a983690f0..4d94fca14 100644 --- a/packages/mix/lib/src/core/attributes_map.dart +++ b/packages/mix/lib/src/core/attributes_map.dart @@ -53,7 +53,8 @@ class AttributeMap { Attr? attributeOfType() => _map?[Attr] as Attr?; - Iterable whereType() => _map?.values.whereType() ?? []; + Iterable whereType() => + _map?.values.whereType() ?? []; AttributeMap merge(AttributeMap? other) { return other == null ? this : AttributeMap([...values, ...other.values]); diff --git a/packages/mix/lib/src/core/factory/mix_data.dart b/packages/mix/lib/src/core/factory/mix_data.dart index 3e3d5fe26..f25d00c0f 100644 --- a/packages/mix/lib/src/core/factory/mix_data.dart +++ b/packages/mix/lib/src/core/factory/mix_data.dart @@ -75,8 +75,20 @@ class MixData { return _mergeAttributes(attributes) ?? attributes.last; } + List> + modifiersOf>() { + return modifiers.whereType().toList(); + } + + List> get modifiers { + return _attributes + .whereType() + .map((e) => e.resolve(this)) + .toList(); + } + Iterable whereType() { - return _attributes.whereType(); + return _attributes.whereType(); } bool contains() { diff --git a/packages/mix/lib/src/core/modifier.dart b/packages/mix/lib/src/core/modifier.dart index 853142cdc..f0b24df93 100644 --- a/packages/mix/lib/src/core/modifier.dart +++ b/packages/mix/lib/src/core/modifier.dart @@ -7,7 +7,6 @@ import 'utility.dart'; abstract base class WidgetModifierSpec> extends Spec { - @override const WidgetModifierSpec({super.animated}); static WidgetModifierSpec? lerpValue( diff --git a/packages/mix/lib/src/core/styled_widget.dart b/packages/mix/lib/src/core/styled_widget.dart index 4eeaf462a..768765d58 100644 --- a/packages/mix/lib/src/core/styled_widget.dart +++ b/packages/mix/lib/src/core/styled_widget.dart @@ -1,9 +1,6 @@ import 'package:flutter/widgets.dart'; -import '../modifiers/render_widget_modifier.dart'; -import 'factory/mix_data.dart'; -import 'factory/mix_provider.dart'; -import 'factory/style_mix.dart'; +import '../../mix.dart'; /// An abstract widget for applying custom styles. /// @@ -60,15 +57,21 @@ abstract class StyledWidget extends StatelessWidget { } Widget applyModifiers(MixData mix, Widget child) { + final modifiers = mix + .whereType() + .map((e) => e.resolve(mix)) + .toList(); return mix.isAnimated ? RenderAnimatedModifiers( - modifiers: resolveModifierSpecs(orderOfModifiers, mix), + modifiers: modifiers, + orderOfModifiers: orderOfModifiers, duration: mix.animation!.duration, curve: mix.animation!.curve, child: child, ) : RenderModifiers( - modifiers: resolveModifierSpecs(orderOfModifiers, mix), + orderOfModifiers: orderOfModifiers, + modifiers: modifiers, child: child, ); } diff --git a/packages/mix/lib/src/modifiers/render_widget_modifier.dart b/packages/mix/lib/src/modifiers/render_widget_modifier.dart index 13a8c40d4..f539b77e4 100644 --- a/packages/mix/lib/src/modifiers/render_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/render_widget_modifier.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; -import '../core/attributes_map.dart'; import '../core/factory/mix_data.dart'; import '../core/modifier.dart'; import '../core/spec.dart'; @@ -16,62 +15,7 @@ import 'sized_box_widget_modifier.dart'; import 'transform_widget_modifier.dart'; import 'visibility_widget_modifier.dart'; -// Default order of modifiers and their logic: -@Deprecated("Use _defaultSpecsOrder instead") const _defaultOrder = [ - // 1. VisibilityModifier: Controls overall visibility. If the widget is set to be invisible, - // none of the subsequent decorations are processed, providing an early exit and optimizing performance. - VisibilityModifierAttribute, - - // 2. SizedBoxModifier: Explicitly sets the size of the widget before any other transformations are applied. - // This ensures that the widget occupies a predetermined space, which is crucial for layouts that require exact dimensions. - SizedBoxModifierAttribute, - - // 3. FractionallySizedBoxModifier: Adjusts the widget's size relative to its parent's size, - // allowing for responsive layouts that scale with the parent widget. This modifier is applied after - // explicit sizing to refine the widget's dimensions based on available space. - FractionallySizedBoxModifierAttribute, - - // 4. AlignModifier: Aligns the widget within its allocated space, which is especially important - // for positioning the widget correctly before applying any transformations that could affect its position. - // Alignment is based on the size constraints established by previous modifiers. - AlignModifierAttribute, - - // 5. IntrinsicHeightModifier: Adjusts the widget's height to fit its child's intrinsic height, - // ensuring that the widget does not force its children to conform to an unnatural height. This is particularly - // useful for widgets that should size themselves based on content. - IntrinsicHeightModifierAttribute, - - // 6. IntrinsicWidthModifier: Similar to the IntrinsicHeightModifier, this adjusts the widget's width - // to its child's intrinsic width. This modifier allows for content-driven width adjustments, making it ideal - // for widgets that need to wrap their content tightly. - IntrinsicWidthModifierAttribute, - - // 7. AspectRatioModifier: Maintains the widget's aspect ratio after sizing adjustments. - // This modifier ensures that the widget scales correctly within its given aspect ratio constraints, - // which is critical for preserving the visual integrity of images and other aspect-sensitive content. - AspectRatioModifierAttribute, - - // 9. TransformModifier: Applies arbitrary transformations, such as rotation, scaling, and translation. - // Transformations are applied after all sizing and positioning adjustments to modify the widget's appearance - // and position in more complex ways without altering the logical layout. - TransformModifierAttribute, - - // 10. Clip Modifiers: Applies clipping in various shapes to the transformed widget, shaping the final appearance. - // Clipping is one of the last steps to ensure it is applied to the widget's final size, position, and transformation state. - ClipOvalModifierAttribute, - ClipRRectModifierAttribute, - ClipPathModifierAttribute, - ClipTriangleModifierAttribute, - ClipRectModifierAttribute, - - // 11. OpacityModifier: Modifies the widget's opacity as the final decoration step. Applying opacity last ensures - // that it does not influence the layout or transformations, serving purely as a visual effect to alter the transparency - // of the widget and its decorations. - OpacityModifierAttribute, -]; - -const _defaultSpecsOrder = [ // 1. VisibilityModifier: Controls overall visibility. If the widget is set to be invisible, // none of the subsequent decorations are processed, providing an early exit and optimizing performance. VisibilityModifierSpec, @@ -127,47 +71,24 @@ const _defaultSpecsOrder = [ class RenderModifiers extends StatelessWidget { const RenderModifiers({ required this.child, - //TODO Should be required in the next version - this.modifiers = const >{}, + this.modifiers = const [], @Deprecated("Use modifiers parameter") this.mix, - @Deprecated("Use modifiers parameter") this.orderOfModifiers, + required this.orderOfModifiers, super.key, }); final Widget child; final MixData? mix; - final List? orderOfModifiers; - final Set> modifiers; - - @Deprecated("Should be removed in the next major version") - Widget deprecatedBuild() { - final specs = resolveModifierSpecs(orderOfModifiers!, mix!); - - var current = child; - - for (final spec in specs) { - current = spec.build(current); - } - - return current; - } + final List orderOfModifiers; + final List> modifiers; @override Widget build(BuildContext context) { - assert( - (mix == null) || (modifiers.isEmpty), - ); - assert( - (orderOfModifiers?.isEmpty ?? true) || (modifiers.isEmpty), - ); - - if (mix != null && orderOfModifiers != null) { - return deprecatedBuild(); - } - var current = child; - for (final spec in modifiers) { + final orderedSpecs = _combineModifiers(mix, modifiers, orderOfModifiers); + + for (final spec in orderedSpecs.reversed) { current = spec.build(current); } @@ -178,12 +99,11 @@ class RenderModifiers extends StatelessWidget { class RenderAnimatedModifiers extends ImplicitlyAnimatedWidget { const RenderAnimatedModifiers({ //TODO Should be required in the next version - this.modifiers = const {}, + this.modifiers = const [], required this.child, required super.duration, @Deprecated("Use modifiers parameter") this.mix, - @Deprecated("Use modifiers parameter") - this.orderOfModifiers = const [], + required this.orderOfModifiers, super.key, super.curve = Curves.linear, super.onEnd, @@ -191,8 +111,8 @@ class RenderAnimatedModifiers extends ImplicitlyAnimatedWidget { final Widget child; final MixData? mix; - final List? orderOfModifiers; - final Set> modifiers; + final List orderOfModifiers; + final List> modifiers; @override RenderAnimatedModifiersState createState() => RenderAnimatedModifiersState(); @@ -201,33 +121,24 @@ class RenderAnimatedModifiers extends ImplicitlyAnimatedWidget { class RenderAnimatedModifiersState extends AnimatedWidgetBaseState { final Map _specs = {}; + final List> _modifiers = []; @override - void forEachTween(TweenVisitor visitor) { - if (widget.mix != null && widget.orderOfModifiers != null) { - deprecatedForEachTween(visitor); - } - - for (final spec in widget.modifiers) { - final specType = spec.runtimeType; - final previousSpec = _specs[specType]; - _specs[specType] = visitor( - previousSpec, - spec, - (dynamic value) => - ModifierSpecTween(begin: value as WidgetModifierSpec), - ) as ModifierSpecTween; - } + void initState() { + super.initState(); + + _modifiers.addAll(_combineModifiers( + widget.mix, + widget.modifiers, + widget.orderOfModifiers, + )); } - @Deprecated('should be deleted in the next major version') - void deprecatedForEachTween(TweenVisitor visitor) { - final specs = resolveModifierSpecs(widget.orderOfModifiers!, widget.mix!); - - for (final spec in specs) { + @override + void forEachTween(TweenVisitor visitor) { + for (final spec in _modifiers.reversed) { final specType = spec.runtimeType; final previousSpec = _specs[specType]; - _specs[specType] = visitor( previousSpec, spec, @@ -239,13 +150,6 @@ class RenderAnimatedModifiersState @override Widget build(BuildContext context) { - assert( - (widget.mix == null) || (widget.modifiers.isEmpty), - ); - assert( - (widget.orderOfModifiers?.isEmpty ?? true) || (widget.modifiers.isEmpty), - ); - var current = widget.child; for (final spec in _specs.keys) { @@ -257,65 +161,33 @@ class RenderAnimatedModifiersState } } -Set> orderSpecs( - List orderOfModifiers, [ - Set> modifiers = const {}, -]) { - final listOfModifiers = ({ - // Prioritize the order of modifiers provided by the user. - ...orderOfModifiers, - // Add the default order of modifiers. - ..._defaultSpecsOrder, - // Add any remaining modifiers that were not included in the order. - ...modifiers.map((e) => e.type), - }).toList(); - - final specs = >{}; - - for (final modifierType in listOfModifiers) { - // Resolve the modifier and add it to the list of specs. - final modifier = modifiers.where((e) => e.type == modifierType).firstOrNull; - if (modifier == null) continue; - specs.add(modifier as WidgetModifierSpec>); - } - - return specs; -} - -Set> resolveModifierSpecs( - List orderOfModifiers, - MixData mix, -) { - final modifiers = mix.whereType(); - - if (modifiers.isEmpty) return {}; - final orderedModifiers = orderModifierSpecs(orderOfModifiers, modifiers); - - return orderedModifiers.map((e) => e.resolve(mix)).toSet(); +@visibleForTesting +List> resolveModifierSpecs( + List orderOfModifiers, MixData mix) { + return orderModifiers(orderOfModifiers, mix.modifiers); } -Iterable orderModifierSpecs( +@visibleForTesting +List> orderModifiers( List orderOfModifiers, - Iterable modifiers, + List> modifiers, ) { - final modifierMap = AttributeMap(modifiers).toMap(); - - final listOfModifiers = { + final listOfModifiers = ({ // Prioritize the order of modifiers provided by the user. ...orderOfModifiers, // Add the default order of modifiers. ..._defaultOrder, // Add any remaining modifiers that were not included in the order. - ...modifierMap.keys, - }.toList().reversed; + ...modifiers.map((e) => e.type), + }).toList(); - final specs = []; + final specs = >[]; for (final modifierType in listOfModifiers) { // Resolve the modifier and add it to the list of specs. - final modifier = modifierMap.remove(modifierType); + final modifier = modifiers.where((e) => e.type == modifierType).firstOrNull; if (modifier == null) continue; - specs.add(modifier); + specs.add(modifier as WidgetModifierSpec>); } return specs; @@ -335,21 +207,18 @@ class RenderSpecModifiers extends StatelessWidget { @override Widget build(BuildContext context) { + final modifiers = spec.modifiers?.value ?? []; return spec.isAnimated ? RenderAnimatedModifiers( - modifiers: orderSpecs( - orderOfModifiers, - spec.modifiers?.value.toSet() ?? {}, - ), + modifiers: modifiers, + orderOfModifiers: orderOfModifiers, duration: spec.animated!.duration, curve: spec.animated!.curve, child: child, ) : RenderModifiers( - modifiers: orderSpecs( - orderOfModifiers, - spec.modifiers?.value.toSet() ?? {}, - ), + modifiers: modifiers, + orderOfModifiers: orderOfModifiers, child: child, ); } @@ -367,3 +236,37 @@ class ModifierSpecTween extends Tween { WidgetModifierSpec lerp(double t) => WidgetModifierSpec.lerpValue(begin, end, t)!; } + +List _normalizeOrderedTypes(MixData? mix, List? orderedTypes) { + orderedTypes ??= []; + if (mix == null) return orderedTypes; + + final modifierAttributes = mix.whereType(); + + final specs = [...orderedTypes]; + + for (int i = 0; i < orderedTypes.length; i++) { + final type = orderedTypes[i]; + // Resolve the modifier and replace the attribute type with the spec type. + final modifier = + modifierAttributes.where((e) => e.runtimeType == type).firstOrNull; + if (modifier != null) { + specs[i] = modifier.resolve(mix).runtimeType; + } + } + + return specs; +} + +List> _combineModifiers(MixData? mix, + List> modifiers, List orderOfModifiers) { + final normalizedModifiers = _normalizeOrderedTypes(mix, orderOfModifiers); + + final mergedModifiers = [...modifiers]; + + if (mix != null) { + mergedModifiers.addAll(mix.modifiers); + } + + return orderModifiers(normalizedModifiers, mergedModifiers); +} diff --git a/packages/mix/test/modifiers/render_widget_modifier_test.dart b/packages/mix/test/modifiers/render_widget_modifier_test.dart index 09df400d9..05508d59e 100644 --- a/packages/mix/test/modifiers/render_widget_modifier_test.dart +++ b/packages/mix/test/modifiers/render_widget_modifier_test.dart @@ -11,26 +11,26 @@ void main() { AlignModifierSpec, TransformModifierSpec, ]; - final Set modifiers = { + final modifiers = [ const OpacityModifierSpec(1), TransformModifierSpec(transform: Matrix4.rotationX(3)), const AlignModifierSpec(alignment: Alignment.center), const ClipOvalModifierSpec(), - }; + ]; - final result = orderSpecs(orderOfModifiers, modifiers); + final result = orderModifiers(orderOfModifiers, modifiers); - expect(result.map((e) => e.type), orderOfModifiers); + expect(result.map((e) => e.type).toList(), orderOfModifiers); }); test('should include default order specs', () { - final Set modifiers = { + final modifiers = [ TransformModifierSpec(transform: Matrix4.rotationX(3)), const OpacityModifierSpec(1), const AlignModifierSpec(alignment: Alignment.center), - }; + ]; - final result = orderSpecs([], modifiers); + final result = orderModifiers([], modifiers); expect(result.map((e) => e.type), [AlignModifierSpec, TransformModifierSpec, OpacityModifierSpec]); @@ -38,21 +38,21 @@ void main() { test('should handle empty modifiers', () { final orderOfModifiers = [TransformModifierSpec]; - final modifiers = {}; + final modifiers = []; - final result = orderSpecs(orderOfModifiers, modifiers); + final result = orderModifiers(orderOfModifiers, modifiers); expect(result, isEmpty); }); test('should handle duplicate modifiers', () { final orderOfModifiers = [TransformModifierSpec]; - final modifiers = { + final modifiers = [ const OpacityModifierSpec(1), const OpacityModifierSpec(0.4), - }; + ]; - final result = orderSpecs(orderOfModifiers, modifiers); + final result = orderModifiers(orderOfModifiers, modifiers); expect(result.length, 1); expect(result.first.type, OpacityModifierSpec); diff --git a/packages/mix/test/src/factory/mix_provider_data_test.dart b/packages/mix/test/src/factory/mix_provider_data_test.dart index de338c601..d74eb058a 100644 --- a/packages/mix/test/src/factory/mix_provider_data_test.dart +++ b/packages/mix/test/src/factory/mix_provider_data_test.dart @@ -1,5 +1,6 @@ // ignore_for_file: non_constant_identifier_names +import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; @@ -107,6 +108,51 @@ void main() { ); }); + test( + 'modifiersOf returns a list of resolved WidgetModifierSpec of the specified type', + () { + final style = Style( + $with.scale(2.0), + $with.opacity(0.5), + $with.visibility.on(), + $with.clipOval(), + $with.aspectRatio(2.0), + ); + + final mixData = MixData.create(MockBuildContext(), style); + + final modifiers = mixData.modifiersOf(); + + expect(modifiers.length, 5); + + final scaleModifiers = mixData.modifiersOf(); + expect(scaleModifiers, [ + TransformModifierSpec( + transform: Matrix4.diagonal3Values(2.0, 2.0, 1.0), + alignment: Alignment.center, + ), + ]); + + final opacityModifiers = mixData.modifiersOf(); + expect(opacityModifiers, [const OpacityModifierSpec(0.5)]); + + final visibilityModifiers = mixData.modifiersOf(); + expect(visibilityModifiers, [const VisibilityModifierSpec(true)]); + + final clipModifiers = mixData.modifiersOf(); + expect(clipModifiers, [const ClipOvalModifierSpec()]); + + final aspectRatioModifiers = + mixData.modifiersOf(); + expect(aspectRatioModifiers, [const AspectRatioModifierSpec(2.0)]); + + final customModifiers = mixData.modifiersOf(); + expect(customModifiers, isEmpty); + + final nonExistentModifiers = mixData.modifiersOf(); + expect(nonExistentModifiers, isEmpty); + }); + group('applyContextToVisualAttributes', () { test( 'must return the same Style that was inputted when there is not ContextVariant in the Style (simple variant)', diff --git a/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart b/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart index 6446669da..809af9fe8 100644 --- a/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart +++ b/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart @@ -20,7 +20,8 @@ void main() { testWidgets('Renders modifiers in the correct order', (tester) async { await tester.pumpMaterialApp( RenderModifiers( - modifiers: resolveModifierSpecs(const [], mixData), + modifiers: mixData.modifiers, + orderOfModifiers: const [], child: const Text('child'), ), ); @@ -82,7 +83,8 @@ void main() { await tester.pumpMaterialApp( RenderModifiers( - modifiers: resolveModifierSpecs(const [], mixData), + modifiers: mixData.modifiers, + orderOfModifiers: const [], child: const Text('child'), ), ); @@ -96,7 +98,8 @@ void main() { await tester.pumpMaterialApp( RenderModifiers( - modifiers: resolveModifierSpecs(const [], mixData), + modifiers: mixData.modifiers, + orderOfModifiers: const [], child: const Text('child'), ), ); @@ -110,13 +113,15 @@ void main() { (tester) async { await tester.pumpMaterialApp( RenderModifiers( - modifiers: resolveModifierSpecs([ + modifiers: mixData.modifiers, + orderOfModifiers: const [ ClipOvalModifierAttribute, AspectRatioModifierAttribute, TransformModifierAttribute, OpacityModifierAttribute, VisibilityModifierAttribute, - ], mixData), + ], + mix: mixData, child: const Text('child'), ), ); @@ -179,9 +184,12 @@ void main() { (tester) async { await tester.pumpMaterialApp( RenderModifiers( - modifiers: resolveModifierSpecs( - [ClipOvalModifierAttribute, AspectRatioModifierAttribute], - mixData), + mix: mixData, + modifiers: mixData.modifiers, + orderOfModifiers: const [ + ClipOvalModifierAttribute, + AspectRatioModifierAttribute + ], child: const Text('child'), ), ); @@ -253,7 +261,8 @@ void main() { await tester.pumpMaterialApp( RenderAnimatedModifiers( - modifiers: resolveModifierSpecs(const [], mixData), + modifiers: mixData.modifiers, + orderOfModifiers: const [], duration: const Duration(milliseconds: 300), child: const Text('child'), ), diff --git a/packages/mix/test/src/modifiers/widget_modifiers_util_test.dart b/packages/mix/test/src/modifiers/widget_modifiers_util_test.dart index d7ec04819..2c87c29c4 100644 --- a/packages/mix/test/src/modifiers/widget_modifiers_util_test.dart +++ b/packages/mix/test/src/modifiers/widget_modifiers_util_test.dart @@ -429,14 +429,10 @@ class _TestableRenderModifier extends StatelessWidget { @override Widget build(BuildContext context) { + final mix = MixData.create(MockBuildContext(), style); return RenderModifiers( - modifiers: resolveModifierSpecs( - const [], - MixData.create( - context, - style, - ), - ), + modifiers: mix.modifiers, + orderOfModifiers: const [], child: Container(), ); } From 2ad7be5af7b6914b6bcedddeca14dd80419f3292 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Tue, 9 Jul 2024 08:40:43 -0300 Subject: [PATCH 20/27] Update widget_modifier_widget_test.dart --- packages/mix/test/deprecated/widget_modifier_widget_test.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/mix/test/deprecated/widget_modifier_widget_test.dart b/packages/mix/test/deprecated/widget_modifier_widget_test.dart index d7fd49411..bf279e6e5 100644 --- a/packages/mix/test/deprecated/widget_modifier_widget_test.dart +++ b/packages/mix/test/deprecated/widget_modifier_widget_test.dart @@ -1,3 +1,5 @@ +// ignore_for_file: deprecated_member_use_from_same_package + import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; From 1488c6089fc0480ba947e372d7fc94d0a093101a Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Tue, 9 Jul 2024 12:13:31 -0300 Subject: [PATCH 21/27] lint fixes --- packages/mix/lib/src/core/factory/mix_data.dart | 17 +++++++++-------- packages/mix/lib/src/core/styled_widget.dart | 8 +++++--- .../src/modifiers/render_widget_modifier.dart | 15 +++++++++++---- .../deprecated/widget_modifiers_util_test.dart | 2 ++ .../modifiers/modifiers_data_test.dart | 2 -- .../modifiers/widget_modifier_widget_test.dart | 2 ++ .../test/src/specs/box/box_attribute_test.dart | 1 - .../mix/test/src/specs/box/box_spec_test.dart | 2 -- 8 files changed, 29 insertions(+), 20 deletions(-) diff --git a/packages/mix/lib/src/core/factory/mix_data.dart b/packages/mix/lib/src/core/factory/mix_data.dart index f25d00c0f..4f734698c 100644 --- a/packages/mix/lib/src/core/factory/mix_data.dart +++ b/packages/mix/lib/src/core/factory/mix_data.dart @@ -1,3 +1,4 @@ +// ignore_for_file: avoid-dynamic import 'package:flutter/widgets.dart'; import '../../attributes/animated/animated_data.dart'; @@ -59,6 +60,13 @@ class MixData { @visibleForTesting AttributeMap get attributes => _attributes; + List> get modifiers { + return _attributes + .whereType() + .map((e) => e.resolve(this)) + .toList(); + } + MixData toInheritable() { final inheritableAttributes = _attributes.values.where( (attr) => attr is! WidgetModifierAttribute, @@ -80,15 +88,8 @@ class MixData { return modifiers.whereType().toList(); } - List> get modifiers { - return _attributes - .whereType() - .map((e) => e.resolve(this)) - .toList(); - } - Iterable whereType() { - return _attributes.whereType(); + return _attributes.whereType(); } bool contains() { diff --git a/packages/mix/lib/src/core/styled_widget.dart b/packages/mix/lib/src/core/styled_widget.dart index 768765d58..1fa95e541 100644 --- a/packages/mix/lib/src/core/styled_widget.dart +++ b/packages/mix/lib/src/core/styled_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/widgets.dart'; -import '../../mix.dart'; +import '../modifiers/modifiers.dart'; +import 'core.dart'; /// An abstract widget for applying custom styles. /// @@ -61,17 +62,18 @@ abstract class StyledWidget extends StatelessWidget { .whereType() .map((e) => e.resolve(mix)) .toList(); + return mix.isAnimated ? RenderAnimatedModifiers( modifiers: modifiers, - orderOfModifiers: orderOfModifiers, duration: mix.animation!.duration, + orderOfModifiers: orderOfModifiers, curve: mix.animation!.curve, child: child, ) : RenderModifiers( - orderOfModifiers: orderOfModifiers, modifiers: modifiers, + orderOfModifiers: orderOfModifiers, child: child, ); } diff --git a/packages/mix/lib/src/modifiers/render_widget_modifier.dart b/packages/mix/lib/src/modifiers/render_widget_modifier.dart index f539b77e4..35cdf8238 100644 --- a/packages/mix/lib/src/modifiers/render_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/render_widget_modifier.dart @@ -163,7 +163,9 @@ class RenderAnimatedModifiersState @visibleForTesting List> resolveModifierSpecs( - List orderOfModifiers, MixData mix) { + List orderOfModifiers, + MixData mix, +) { return orderModifiers(orderOfModifiers, mix.modifiers); } @@ -187,6 +189,7 @@ List> orderModifiers( // Resolve the modifier and add it to the list of specs. final modifier = modifiers.where((e) => e.type == modifierType).firstOrNull; if (modifier == null) continue; + // ignore: avoid-unnecessary-type-casts specs.add(modifier as WidgetModifierSpec>); } @@ -208,11 +211,12 @@ class RenderSpecModifiers extends StatelessWidget { @override Widget build(BuildContext context) { final modifiers = spec.modifiers?.value ?? []; + return spec.isAnimated ? RenderAnimatedModifiers( modifiers: modifiers, - orderOfModifiers: orderOfModifiers, duration: spec.animated!.duration, + orderOfModifiers: orderOfModifiers, curve: spec.animated!.curve, child: child, ) @@ -258,8 +262,11 @@ List _normalizeOrderedTypes(MixData? mix, List? orderedTypes) { return specs; } -List> _combineModifiers(MixData? mix, - List> modifiers, List orderOfModifiers) { +List> _combineModifiers( + MixData? mix, + List> modifiers, + List orderOfModifiers, +) { final normalizedModifiers = _normalizeOrderedTypes(mix, orderOfModifiers); final mergedModifiers = [...modifiers]; diff --git a/packages/mix/test/deprecated/widget_modifiers_util_test.dart b/packages/mix/test/deprecated/widget_modifiers_util_test.dart index 66f69dc3d..d1dd975f6 100644 --- a/packages/mix/test/deprecated/widget_modifiers_util_test.dart +++ b/packages/mix/test/deprecated/widget_modifiers_util_test.dart @@ -1,3 +1,5 @@ +// ignore_for_file: deprecated_member_use_from_same_package + import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; diff --git a/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart b/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart index 6449a7a36..c23f62db1 100644 --- a/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart +++ b/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart @@ -1,8 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; -import 'package:mix/src/attributes/modifiers/widget_modifiers_data.dart'; -import 'package:mix/src/attributes/modifiers/widget_modifiers_data_dto.dart'; import '../../../helpers/testing_utils.dart'; diff --git a/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart b/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart index 809af9fe8..5f9aa76d2 100644 --- a/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart +++ b/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart @@ -1,3 +1,5 @@ +// ignore_for_file: deprecated_member_use_from_same_package + import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; diff --git a/packages/mix/test/src/specs/box/box_attribute_test.dart b/packages/mix/test/src/specs/box/box_attribute_test.dart index 32a8a146a..9300e36e8 100644 --- a/packages/mix/test/src/specs/box/box_attribute_test.dart +++ b/packages/mix/test/src/specs/box/box_attribute_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/attributes/modifiers/widget_modifiers_data_dto.dart'; import '../../../helpers/testing_utils.dart'; diff --git a/packages/mix/test/src/specs/box/box_spec_test.dart b/packages/mix/test/src/specs/box/box_spec_test.dart index c666ae629..6680ef1b9 100644 --- a/packages/mix/test/src/specs/box/box_spec_test.dart +++ b/packages/mix/test/src/specs/box/box_spec_test.dart @@ -3,8 +3,6 @@ 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/modifiers/widget_modifiers_data.dart'; -import 'package:mix/src/attributes/modifiers/widget_modifiers_data_dto.dart'; import '../../../helpers/testing_utils.dart'; From a4acf4fb338b992ce06351b1f3ee63c9f1eb7bc1 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Tue, 9 Jul 2024 12:14:21 -0300 Subject: [PATCH 22/27] Update text_style_token_test.dart --- packages/mix/test/src/theme/tokens/text_style_token_test.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/mix/test/src/theme/tokens/text_style_token_test.dart b/packages/mix/test/src/theme/tokens/text_style_token_test.dart index 091061fac..ff1d3556f 100644 --- a/packages/mix/test/src/theme/tokens/text_style_token_test.dart +++ b/packages/mix/test/src/theme/tokens/text_style_token_test.dart @@ -1,5 +1,3 @@ -import 'dart:ui'; - import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; From a950171856e8da6e6dfa96109d7dc7310dbcd311 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Tue, 9 Jul 2024 17:40:26 -0300 Subject: [PATCH 23/27] fix animations --- .../src/modifiers/render_widget_modifier.dart | 19 ++---- .../widget_modifier_widget_test.dart | 66 +++++++++++++++++++ .../pressable/pressable_widget_test.dart | 48 +++++++++++--- 3 files changed, 110 insertions(+), 23 deletions(-) diff --git a/packages/mix/lib/src/modifiers/render_widget_modifier.dart b/packages/mix/lib/src/modifiers/render_widget_modifier.dart index 35cdf8238..ff6477d1b 100644 --- a/packages/mix/lib/src/modifiers/render_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/render_widget_modifier.dart @@ -97,7 +97,7 @@ class RenderModifiers extends StatelessWidget { } class RenderAnimatedModifiers extends ImplicitlyAnimatedWidget { - const RenderAnimatedModifiers({ + RenderAnimatedModifiers({ //TODO Should be required in the next version this.modifiers = const [], required this.child, @@ -107,12 +107,13 @@ class RenderAnimatedModifiers extends ImplicitlyAnimatedWidget { super.key, super.curve = Curves.linear, super.onEnd, - }); + }) : _appliedModifiers = _combineModifiers(mix, modifiers, orderOfModifiers); final Widget child; final MixData? mix; final List orderOfModifiers; final List> modifiers; + final List> _appliedModifiers; @override RenderAnimatedModifiersState createState() => RenderAnimatedModifiersState(); @@ -121,22 +122,10 @@ class RenderAnimatedModifiers extends ImplicitlyAnimatedWidget { class RenderAnimatedModifiersState extends AnimatedWidgetBaseState { final Map _specs = {}; - final List> _modifiers = []; - - @override - void initState() { - super.initState(); - - _modifiers.addAll(_combineModifiers( - widget.mix, - widget.modifiers, - widget.orderOfModifiers, - )); - } @override void forEachTween(TweenVisitor visitor) { - for (final spec in _modifiers.reversed) { + for (final spec in widget._appliedModifiers.reversed) { final specType = spec.runtimeType; final previousSpec = _specs[specType]; _specs[specType] = visitor( diff --git a/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart b/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart index 5f9aa76d2..a17fd4e79 100644 --- a/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart +++ b/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart @@ -277,6 +277,34 @@ void main() { await tester.pump(const Duration(milliseconds: 150)); await tester.pump(const Duration(milliseconds: 150)); }); + + testWidgets('Renders animated modifiers', (tester) async { + await tester.pumpWidget( + const _TestableAnimatedModifiers(), + ); + + final gestureFinder = find.byType(GestureDetector); + expect(gestureFinder, findsOneWidget); + + final finder = find.byType(Opacity); + expect(finder, findsOneWidget); + + final finderSizedBox = find.byType(SizedBox); + expect(finderSizedBox, findsOneWidget); + + await tester.tap(gestureFinder); + await tester.pump(); + + await tester.pump(const Duration(milliseconds: 100)); + expect(tester.widget(finder).opacity, 0.5); + expect(tester.widget(finderSizedBox).height, 25); + expect(tester.widget(finderSizedBox).width, 25); + + await tester.pump(const Duration(milliseconds: 100)); + expect(tester.widget(finder).opacity, 0.0); + expect(tester.widget(finderSizedBox).height, 0); + expect(tester.widget(finderSizedBox).width, 0); + }); }); group('Modifiers attributes', () { @@ -413,3 +441,41 @@ void main() { }); }); } + +class _TestableAnimatedModifiers extends StatefulWidget { + const _TestableAnimatedModifiers(); + + @override + State<_TestableAnimatedModifiers> createState() => + _TestableAnimatedModifiersState(); +} + +class _TestableAnimatedModifiersState + extends State<_TestableAnimatedModifiers> { + bool _isActive = true; + + void _handleToggle() { + setState(() { + _isActive = !_isActive; + }); + } + + @override + Widget build(BuildContext context) { + return Pressable( + onPress: _handleToggle, + child: RenderAnimatedModifiers( + duration: const Duration(milliseconds: 200), + orderOfModifiers: const [], + modifiers: [ + OpacityModifierSpec(_isActive ? 1.0 : 0.0), + SizedBoxModifierSpec( + height: _isActive ? 50.0 : 0.0, + width: _isActive ? 50.0 : 0.0, + ), + ], + child: Container(), + ), + ); + } +} diff --git a/packages/mix/test/src/widgets/pressable/pressable_widget_test.dart b/packages/mix/test/src/widgets/pressable/pressable_widget_test.dart index fe66ea3a9..5015eb66b 100644 --- a/packages/mix/test/src/widgets/pressable/pressable_widget_test.dart +++ b/packages/mix/test/src/widgets/pressable/pressable_widget_test.dart @@ -208,12 +208,41 @@ void main() { expect(wasPressed, isTrue); }); + testWidgets('animates correctly on hover', (WidgetTester tester) async { + await tester.pumpMaterialApp( + PressableBox( + unpressDelay: const Duration(milliseconds: 200), + style: Style( + $with.opacity(1.0), + $on.hover( + $with.opacity(0.0), + ), + ).animate( + duration: const Duration(milliseconds: 200), + ), + child: const Box(), + ), + ); + + final finder = find.byType(Opacity); + expect(finder, findsOneWidget); + + Opacity opacityWidget = tester.widget(finder); + expect(opacityWidget.opacity, 1.0); + + await tester.hover(finder); + await tester.pump(const Duration(milliseconds: 100)); + + opacityWidget = tester.widget(finder); + expect(opacityWidget.opacity, 0.5); + }); + testWidgets(r'must change to attributes in $on.hover variant when hovered', (WidgetTester tester) async { await pumpTestCase( tester: tester, condition: $on.hover, - action: () => tester.hover(find.byType(PressableBox)), + action: () => tester.hoverAndSettle(find.byType(PressableBox)), ); }); @@ -246,7 +275,7 @@ void main() { await pumpTestCase( tester: tester, condition: ($on.longPress | $on.hover), - action: () => tester.hover(find.byType(PressableBox)), + action: () => tester.hoverAndSettle(find.byType(PressableBox)), ); }); @@ -311,7 +340,7 @@ void main() { duration: const Duration(milliseconds: 250), condition: ($on.hover | $on.press), action: () async { - await tester.hover(find.byType(PressableBox)); + await tester.hoverAndSettle(find.byType(PressableBox)); }, ); }); @@ -363,7 +392,7 @@ void main() { tester: tester, condition: ($on.longPress | $on.press), action: () async { - await tester.hover(find.byType(PressableBox)); + await tester.hoverAndSettle(find.byType(PressableBox)); }, finalExpectedOpacity: 0.5, ); @@ -377,7 +406,7 @@ void main() { duration: const Duration(milliseconds: 250), condition: ($on.hover & $on.press), action: () async { - await tester.hover(find.byType(PressableBox)); + await tester.hoverAndSettle(find.byType(PressableBox)); await tester.pump(); await tester.tap(find.byType(PressableBox)); await tester.pump(); @@ -392,7 +421,7 @@ void main() { tester: tester, condition: ($on.hover & $on.longPress), action: () async { - await tester.hover(find.byType(PressableBox)); + await tester.hoverAndSettle(find.byType(PressableBox)); await tester.pump(); // Custom way to long press @@ -414,6 +443,11 @@ void main() { } extension WidgetTesterExt on WidgetTester { + Future hoverAndSettle(Finder finder) async { + await hover(finder); + await pumpAndSettle(); + } + Future hover(Finder finder) async { FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; @@ -424,8 +458,6 @@ extension WidgetTesterExt on WidgetTester { await pump(); await gesture.moveTo(getCenter(finder)); - await pumpAndSettle(); - addTearDown(gesture.removePointer); } } From f295ae2996f67cf8dd83e7c8de7c332cfa9df061 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Wed, 10 Jul 2024 15:31:54 -0300 Subject: [PATCH 24/27] fix animation --- .../src/modifiers/render_widget_modifier.dart | 19 +++++ .../widget_modifier_widget_test.dart | 70 +++++++++++++++---- 2 files changed, 75 insertions(+), 14 deletions(-) diff --git a/packages/mix/lib/src/modifiers/render_widget_modifier.dart b/packages/mix/lib/src/modifiers/render_widget_modifier.dart index ff6477d1b..dec02f50f 100644 --- a/packages/mix/lib/src/modifiers/render_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/render_widget_modifier.dart @@ -125,6 +125,25 @@ class RenderAnimatedModifiersState @override void forEachTween(TweenVisitor visitor) { + cleanUpSpecs(); + updateModifiersSpecs(visitor); + } + + Map cleanUpSpecs() { + final difference = _specs.keys + .toSet() + .difference(widget._appliedModifiers.map((e) => e.runtimeType).toSet()); + + if (difference.isNotEmpty) { + for (var e in difference) { + _specs.remove(e); + } + } + + return _specs; + } + + void updateModifiersSpecs(TweenVisitor visitor) { for (final spec in widget._appliedModifiers.reversed) { final specType = spec.runtimeType; final previousSpec = _specs[specType]; diff --git a/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart b/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart index a17fd4e79..e4d994014 100644 --- a/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart +++ b/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart @@ -280,7 +280,20 @@ void main() { testWidgets('Renders animated modifiers', (tester) async { await tester.pumpWidget( - const _TestableAnimatedModifiers(), + _TestableAnimatedModifiers( + (isActive) => RenderAnimatedModifiers( + duration: const Duration(milliseconds: 200), + orderOfModifiers: const [], + modifiers: [ + OpacityModifierSpec(isActive ? 1.0 : 0.0), + SizedBoxModifierSpec( + height: isActive ? 50.0 : 0.0, + width: isActive ? 50.0 : 0.0, + ), + ], + child: Container(), + ), + ), ); final gestureFinder = find.byType(GestureDetector); @@ -305,6 +318,42 @@ void main() { expect(tester.widget(finderSizedBox).height, 0); expect(tester.widget(finderSizedBox).width, 0); }); + + testWidgets('Renders animated modifiers', (tester) async { + gestureFinder() => find.byType(GestureDetector); + + await tester.pumpWidget( + _TestableAnimatedModifiers( + (isActive) => RenderAnimatedModifiers( + duration: const Duration(milliseconds: 200), + orderOfModifiers: const [], + modifiers: [ + const OpacityModifierSpec(0.0), + if (!isActive) + TransformModifierSpec(transform: Matrix4.rotationZ(0.5)), + ], + child: Container(), + ), + ), + ); + + expect(find.byType(Opacity), findsOneWidget); + expect(find.byType(Transform), findsNothing); + + await tester.tap(gestureFinder()); + await tester.pump(); + + expect(find.byType(Opacity), findsOneWidget); + expect(find.byType(Transform), findsOneWidget); + await tester.pumpAndSettle(const Duration(milliseconds: 200)); + + await tester.tap(gestureFinder()); + await tester.pump(); + + expect(find.byType(Opacity), findsOneWidget); + expect(find.byType(Transform), findsNothing); + await tester.pumpAndSettle(const Duration(milliseconds: 200)); + }); }); group('Modifiers attributes', () { @@ -443,7 +492,11 @@ void main() { } class _TestableAnimatedModifiers extends StatefulWidget { - const _TestableAnimatedModifiers(); + const _TestableAnimatedModifiers( + this.child, + ); + + final Widget Function(bool) child; @override State<_TestableAnimatedModifiers> createState() => @@ -464,18 +517,7 @@ class _TestableAnimatedModifiersState Widget build(BuildContext context) { return Pressable( onPress: _handleToggle, - child: RenderAnimatedModifiers( - duration: const Duration(milliseconds: 200), - orderOfModifiers: const [], - modifiers: [ - OpacityModifierSpec(_isActive ? 1.0 : 0.0), - SizedBoxModifierSpec( - height: _isActive ? 50.0 : 0.0, - width: _isActive ? 50.0 : 0.0, - ), - ], - child: Container(), - ), + child: widget.child(_isActive), ); } } From e3f8bc96d3f593dfdd4283d7cba821ef43f75b10 Mon Sep 17 00:00:00 2001 From: Leo Farias Date: Wed, 10 Jul 2024 15:21:32 -0400 Subject: [PATCH 25/27] Moved updateSpecs to didUpdateWidget --- .../lib/src/modifiers/render_widget_modifier.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/mix/lib/src/modifiers/render_widget_modifier.dart b/packages/mix/lib/src/modifiers/render_widget_modifier.dart index dec02f50f..35201af45 100644 --- a/packages/mix/lib/src/modifiers/render_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/render_widget_modifier.dart @@ -123,9 +123,19 @@ class RenderAnimatedModifiersState extends AnimatedWidgetBaseState { final Map _specs = {}; + @override + void didUpdateWidget(covariant RenderAnimatedModifiers oldWidget) { + if (oldWidget.modifiers != widget.modifiers || + oldWidget.mix != widget.mix || + oldWidget.orderOfModifiers != widget.orderOfModifiers) { + cleanUpSpecs(); + } + + super.didUpdateWidget(oldWidget); + } + @override void forEachTween(TweenVisitor visitor) { - cleanUpSpecs(); updateModifiersSpecs(visitor); } From 37c6f7a7fc346d21a86bc74cc67f1cd48b2b18d5 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Wed, 10 Jul 2024 16:43:09 -0300 Subject: [PATCH 26/27] Update widget_modifier_widget_test.dart --- .../mix/test/src/modifiers/widget_modifier_widget_test.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart b/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart index e4d994014..b06b17eaf 100644 --- a/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart +++ b/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart @@ -319,7 +319,8 @@ void main() { expect(tester.widget(finderSizedBox).width, 0); }); - testWidgets('Renders animated modifiers', (tester) async { + testWidgets('Transition correctly when there is conditional specs', + (tester) async { gestureFinder() => find.byType(GestureDetector); await tester.pumpWidget( From 15474dd225443a9c931cd8f58c80a8077172d59f Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Thu, 11 Jul 2024 12:00:42 -0300 Subject: [PATCH 27/27] rename wrap to modifiers --- .../todo_list/lib/style/components/checkbox.dart | 12 ++++++------ packages/mix/lib/src/core/spec.dart | 3 +++ packages/mix/lib/src/specs/box/box_spec.g.dart | 2 +- packages/mix/lib/src/specs/flex/flex_spec.g.dart | 2 +- packages/mix/lib/src/specs/icon/icon_spec.g.dart | 2 +- packages/mix/lib/src/specs/image/image_spec.g.dart | 2 +- packages/mix/lib/src/specs/stack/stack_spec.g.dart | 2 +- packages/mix/lib/src/specs/text/text_spec.g.dart | 2 +- 8 files changed, 15 insertions(+), 12 deletions(-) diff --git a/examples/todo_list/lib/style/components/checkbox.dart b/examples/todo_list/lib/style/components/checkbox.dart index aae58f560..dd0dd1c45 100644 --- a/examples/todo_list/lib/style/components/checkbox.dart +++ b/examples/todo_list/lib/style/components/checkbox.dart @@ -32,13 +32,13 @@ class TodoCheckbox extends StatelessWidget { outlinePattern(), $icon.size(16), $icon.color.ref($token.color.surface), - $icon.modifiers.opacity(0), - $icon.modifiers.padding.top(5), - $icon.modifiers.scale(0.5), + $icon.wrap.opacity(0), + $icon.wrap.padding.top(5), + $icon.wrap.scale(0.5), _CheckboxVariant.checked( - $icon.modifiers.padding.top(0), - $icon.modifiers.scale(2), - $icon.modifiers.opacity(1), + $icon.wrap.padding.top(0), + $icon.wrap.scale(2), + $icon.wrap.opacity(1), $box.color.ref($token.color.primary), $box.border.color.ref($token.color.primary), ), diff --git a/packages/mix/lib/src/core/spec.dart b/packages/mix/lib/src/core/spec.dart index d242e8f2c..7df22de68 100644 --- a/packages/mix/lib/src/core/spec.dart +++ b/packages/mix/lib/src/core/spec.dart @@ -1,4 +1,5 @@ import 'package:flutter/foundation.dart'; +import 'package:mix_annotations/mix_annotations.dart'; import '../attributes/animated/animated_data.dart'; import '../attributes/animated/animated_data_dto.dart'; @@ -12,6 +13,8 @@ import 'utility.dart'; @immutable abstract class Spec> with EqualityMixin { final AnimatedData? animated; + + @MixableProperty(utilities: [MixableUtility(alias: 'wrap')]) final WidgetModifiersData? modifiers; const Spec({this.animated, this.modifiers}); diff --git a/packages/mix/lib/src/specs/box/box_spec.g.dart b/packages/mix/lib/src/specs/box/box_spec.g.dart index 33767445a..0a373d908 100644 --- a/packages/mix/lib/src/specs/box/box_spec.g.dart +++ b/packages/mix/lib/src/specs/box/box_spec.g.dart @@ -349,7 +349,7 @@ base class BoxSpecUtility late final height = DoubleUtility((v) => only(height: v)); /// Utility for defining [BoxSpecAttribute.modifiers] - late final modifiers = SpecModifierUtility((v) => only(modifiers: v)); + late final wrap = SpecModifierUtility((v) => only(modifiers: v)); /// Utility for defining [BoxSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); diff --git a/packages/mix/lib/src/specs/flex/flex_spec.g.dart b/packages/mix/lib/src/specs/flex/flex_spec.g.dart index 8c6baadd1..d1c8deade 100644 --- a/packages/mix/lib/src/specs/flex/flex_spec.g.dart +++ b/packages/mix/lib/src/specs/flex/flex_spec.g.dart @@ -276,7 +276,7 @@ base class FlexSpecUtility late final animated = AnimatedUtility((v) => only(animated: v)); /// Utility for defining [FlexSpecAttribute.modifiers] - late final modifiers = SpecModifierUtility((v) => only(modifiers: v)); + late final wrap = SpecModifierUtility((v) => only(modifiers: v)); FlexSpecUtility(super.builder); diff --git a/packages/mix/lib/src/specs/icon/icon_spec.g.dart b/packages/mix/lib/src/specs/icon/icon_spec.g.dart index d4d7e7277..abab8837f 100644 --- a/packages/mix/lib/src/specs/icon/icon_spec.g.dart +++ b/packages/mix/lib/src/specs/icon/icon_spec.g.dart @@ -267,7 +267,7 @@ base class IconSpecUtility late final animated = AnimatedUtility((v) => only(animated: v)); /// Utility for defining [IconSpecAttribute.modifiers] - late final modifiers = SpecModifierUtility((v) => only(modifiers: v)); + late final wrap = SpecModifierUtility((v) => only(modifiers: v)); IconSpecUtility(super.builder); diff --git a/packages/mix/lib/src/specs/image/image_spec.g.dart b/packages/mix/lib/src/specs/image/image_spec.g.dart index 8023c57f2..4018a1fc9 100644 --- a/packages/mix/lib/src/specs/image/image_spec.g.dart +++ b/packages/mix/lib/src/specs/image/image_spec.g.dart @@ -267,7 +267,7 @@ base class ImageSpecUtility late final animated = AnimatedUtility((v) => only(animated: v)); /// Utility for defining [ImageSpecAttribute.modifiers] - late final modifiers = SpecModifierUtility((v) => only(modifiers: v)); + late final wrap = SpecModifierUtility((v) => only(modifiers: v)); ImageSpecUtility(super.builder); diff --git a/packages/mix/lib/src/specs/stack/stack_spec.g.dart b/packages/mix/lib/src/specs/stack/stack_spec.g.dart index 41d0cc8a5..49acd8db8 100644 --- a/packages/mix/lib/src/specs/stack/stack_spec.g.dart +++ b/packages/mix/lib/src/specs/stack/stack_spec.g.dart @@ -204,7 +204,7 @@ base class StackSpecUtility late final animated = AnimatedUtility((v) => only(animated: v)); /// Utility for defining [StackSpecAttribute.modifiers] - late final modifiers = SpecModifierUtility((v) => only(modifiers: v)); + late final wrap = SpecModifierUtility((v) => only(modifiers: v)); StackSpecUtility(super.builder); diff --git a/packages/mix/lib/src/specs/text/text_spec.g.dart b/packages/mix/lib/src/specs/text/text_spec.g.dart index 9c7d9efc2..5bb1242cf 100644 --- a/packages/mix/lib/src/specs/text/text_spec.g.dart +++ b/packages/mix/lib/src/specs/text/text_spec.g.dart @@ -322,7 +322,7 @@ base class TextSpecUtility late final animated = AnimatedUtility((v) => only(animated: v)); /// Utility for defining [TextSpecAttribute.modifiers] - late final modifiers = SpecModifierUtility((v) => only(modifiers: v)); + late final wrap = SpecModifierUtility((v) => only(modifiers: v)); TextSpecUtility(super.builder);