Skip to content

Commit

Permalink
feat: inline modifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
tilucasoli committed Jun 28, 2024
1 parent 2368f65 commit 18f7343
Show file tree
Hide file tree
Showing 15 changed files with 426 additions and 91 deletions.
2 changes: 1 addition & 1 deletion melos.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: mix_workspace

sdkPath: .fvm/flutter_sdk
# sdkPath: .fvm/flutter_sdk
packages:
- packages/**
- examples/**
Expand Down
7 changes: 7 additions & 0 deletions packages/mix/lib/src/attributes/modifiers/modifiers_data.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import '../../core/modifier.dart';

class ModifiersData {
// ignore: avoid-dynamic
final List<WidgetModifierSpec<dynamic>> value;
const ModifiersData(this.value);
}
29 changes: 29 additions & 0 deletions packages/mix/lib/src/attributes/modifiers/modifiers_data_dto.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import '../../core/core.dart';
import 'modifiers_data.dart';

class ModifiersDataDto extends Dto<ModifiersData> {
final List<WidgetModifierAttribute> 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<Object?> get props => [value];
}
38 changes: 38 additions & 0 deletions packages/mix/lib/src/attributes/modifiers/modifiers_util.dart
Original file line number Diff line number Diff line change
@@ -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<T extends Attribute>
extends MixUtility<T, ModifiersDataDto> {
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]));
}
}
8 changes: 6 additions & 2 deletions packages/mix/lib/src/core/spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -10,8 +12,9 @@ import 'utility.dart';
@immutable
abstract class Spec<T extends Spec<T>> with EqualityMixin {
final AnimatedData? animated;
final ModifiersData? modifiers;

const Spec({this.animated});
const Spec({this.animated, this.modifiers});

Type get type => T;

Expand All @@ -31,8 +34,9 @@ abstract class Spec<T extends Spec<T>> with EqualityMixin {
/// The [Self] type represents the concrete implementation of the attribute, while the [Value] type represents the resolvable value.
abstract base class SpecAttribute<Value> extends StyledAttribute {
final AnimatedDataDto? animated;
final ModifiersDataDto? modifiers;

const SpecAttribute({this.animated});
const SpecAttribute({this.animated, this.modifiers});

Value resolve(MixData mix);

Expand Down
6 changes: 2 additions & 4 deletions packages/mix/lib/src/core/styled_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
}
Expand Down
66 changes: 49 additions & 17 deletions packages/mix/lib/src/modifiers/render_widget_modifier.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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<Type> orderOfModifiers;
final Set<WidgetModifierSpec<dynamic>> 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);
}

Expand All @@ -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<Type> orderOfModifiers;

final Set<WidgetModifierSpec<dynamic>> modifiers;

@override
RenderAnimatedModifiersState createState() => RenderAnimatedModifiersState();
Expand All @@ -120,12 +116,9 @@ class RenderAnimatedModifiersState

@override
void forEachTween(TweenVisitor<dynamic> 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,
Expand Down Expand Up @@ -155,6 +148,15 @@ Set<WidgetModifierSpec> resolveModifierSpecs(
final modifiers = mix.whereType<WidgetModifierAttribute>();

if (modifiers.isEmpty) return {};

return orderModifierSpecs(orderOfModifiers, mix, modifiers);
}

Set<WidgetModifierSpec> orderModifierSpecs(
List<Type> orderOfModifiers,
MixData mix,
Iterable<WidgetModifierAttribute> modifiers,
) {
final modifierMap = AttributeMap<WidgetModifierAttribute>(modifiers).toMap();

final listOfModifiers = {
Expand All @@ -178,6 +180,36 @@ Set<WidgetModifierSpec> 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<Type> 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<WidgetModifierSpec> {
/// Creates an [EdgeInsetsGeometry] tween.
///
Expand Down
5 changes: 5 additions & 0 deletions packages/mix/lib/src/specs/box/box_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -96,6 +100,7 @@ final class BoxSpec extends Spec<BoxSpec> with _$BoxSpec {
this.clipBehavior,
this.width,
this.height,
super.modifiers,
super.animated,
});

Expand Down
15 changes: 14 additions & 1 deletion packages/mix/lib/src/specs/box/box_spec.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 18f7343

Please sign in to comment.