Skip to content

Commit

Permalink
feat: MixThemeData can alter default order of modifiers (#380)
Browse files Browse the repository at this point in the history
  • Loading branch information
tilucasoli authored Jul 18, 2024
1 parent cd47413 commit bbae869
Show file tree
Hide file tree
Showing 17 changed files with 190 additions and 157 deletions.
2 changes: 1 addition & 1 deletion packages/mix/lib/src/core/styled_widget.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:flutter/widgets.dart';

import '../modifiers/modifiers.dart';
import '../modifiers/internal/render_widget_modifier.dart';
import 'core.dart';

/// An abstract widget for applying custom styles.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// ignore_for_file: avoid-dynamic

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

import '../core/factory/mix_data.dart';
import '../core/modifier.dart';
import '../core/spec.dart';
import 'modifiers.dart';
import '../../core/core.dart';
import '../../theme/theme.dart';
import '../modifiers.dart';

const _defaultOrder = [
// 1. FlexibleModifier: When the widget is used inside a Row, Column, or Flex widget, it will
Expand Down Expand Up @@ -89,64 +89,116 @@ class RenderModifiers extends StatelessWidget {

@override
Widget build(BuildContext context) {
var current = child;
return _RenderModifiers(
modifiers: _combineModifiers(mix, modifiers, orderOfModifiers).reversed,
child: child,
);
}
}

final orderedSpecs = _combineModifiers(mix, modifiers, orderOfModifiers);
class _RenderModifiers extends StatelessWidget {
const _RenderModifiers({required this.child, required this.modifiers});

for (final spec in orderedSpecs.reversed) {
final Widget child;
final Iterable<WidgetModifierSpec<dynamic>> modifiers;

@override
Widget build(BuildContext context) {
var current = child;

for (final spec in modifiers) {
current = spec.build(current);
}

return current;
}
}

class RenderAnimatedModifiers extends ImplicitlyAnimatedWidget {
RenderAnimatedModifiers({
class RenderAnimatedModifiers extends StatelessWidget {
const RenderAnimatedModifiers({
super.key,
//TODO Should be required in the next version
this.modifiers = const [],
required this.child,
required super.duration,
required this.duration,
@Deprecated("Use modifiers parameter") this.mix,
required this.orderOfModifiers,
super.key,
super.curve = Curves.linear,
super.onEnd,
}) : _appliedModifiers = _combineModifiers(mix, modifiers, orderOfModifiers);
this.curve = Curves.linear,
this.onEnd,
});

final Widget child;
final MixData? mix;
final List<Type> orderOfModifiers;
final List<WidgetModifierSpec<dynamic>> modifiers;
final List<WidgetModifierSpec<dynamic>> _appliedModifiers;
final Duration duration;
final Curve curve;
final VoidCallback? onEnd;

@override
RenderAnimatedModifiersState createState() => RenderAnimatedModifiersState();
Widget build(BuildContext context) {
return _RenderAnimatedModifiers(
modifiers: _combineModifiers(
mix,
modifiers,
orderOfModifiers,
defaultOrder: MixTheme.maybeOf(context)?.defaultOrderOfModifiers,
).reversed.toList(),
duration: duration,
curve: curve,
onEnd: onEnd,
child: child,
);
}
}

class RenderAnimatedModifiersState
extends AnimatedWidgetBaseState<RenderAnimatedModifiers> {
class _RenderAnimatedModifiers extends ImplicitlyAnimatedWidget {
const _RenderAnimatedModifiers({
//TODO Should be required in the next version
this.modifiers = const [],
required this.child,
required super.duration,
super.curve = Curves.linear,
super.onEnd,
});

final Widget child;
final List<WidgetModifierSpec<dynamic>> modifiers;

@override
_RenderAnimatedModifiersState createState() =>
_RenderAnimatedModifiersState();
}

class _RenderAnimatedModifiersState
extends AnimatedWidgetBaseState<_RenderAnimatedModifiers> {
final Map<Type, ModifierSpecTween> _specs = {};

Iterable<Type> _typeOfModifiers = [];

@override
void initState() {
super.initState();
updateTypeOfAppliedModifiers();
}

@override
void didUpdateWidget(covariant RenderAnimatedModifiers oldWidget) {
void didUpdateWidget(covariant _RenderAnimatedModifiers oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.modifiers != widget.modifiers ||
oldWidget.mix != widget.mix ||
oldWidget.orderOfModifiers != widget.orderOfModifiers) {
if (!listEquals(oldWidget.modifiers, widget.modifiers)) {
updateTypeOfAppliedModifiers();
cleanUpSpecs();
}
}

@override
void forEachTween(TweenVisitor<dynamic> visitor) {
updateModifiersSpecs(visitor);
updateTypeOfAppliedModifiers() {
_typeOfModifiers = widget.modifiers.map((e) => e.runtimeType);
}

Map<Type, ModifierSpecTween> cleanUpSpecs() {
final difference = _specs.keys
.toSet()
.difference(widget._appliedModifiers.map((e) => e.runtimeType).toSet());
.difference(widget.modifiers.map((e) => e.runtimeType).toSet());

if (difference.isNotEmpty) {
for (var e in difference) {
Expand All @@ -157,8 +209,13 @@ class RenderAnimatedModifiersState
return _specs;
}

@override
void forEachTween(TweenVisitor<dynamic> visitor) {
updateModifiersSpecs(visitor);
}

void updateModifiersSpecs(TweenVisitor<dynamic> visitor) {
for (final spec in widget._appliedModifiers.reversed) {
for (final spec in widget.modifiers) {
final specType = spec.runtimeType;
final previousSpec = _specs[specType];
_specs[specType] = visitor(
Expand All @@ -174,50 +231,15 @@ class RenderAnimatedModifiersState
Widget build(BuildContext context) {
var current = widget.child;

for (final spec in _specs.keys) {
final evaluatedSpec = _specs[spec]!.evaluate(animation);
for (final modifier in _typeOfModifiers) {
final evaluatedSpec = _specs[modifier]!.evaluate(animation);
current = evaluatedSpec.build(current);
}

return current;
}
}

@visibleForTesting
List<WidgetModifierSpec<dynamic>> resolveModifierSpecs(
List<Type> orderOfModifiers,
MixData mix,
) {
return orderModifiers(orderOfModifiers, mix.modifiers);
}

@visibleForTesting
List<WidgetModifierSpec<dynamic>> orderModifiers(
List<Type> orderOfModifiers,
List<WidgetModifierSpec<dynamic>> modifiers,
) {
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.
...modifiers.map((e) => e.type),
}).toList();

final specs = <WidgetModifierSpec<dynamic>>[];

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;
// ignore: avoid-unnecessary-type-casts
specs.add(modifier as WidgetModifierSpec<WidgetModifierSpec<dynamic>>);
}

return specs;
}

class RenderSpecModifiers extends StatelessWidget {
const RenderSpecModifiers({
required this.orderOfModifiers,
Expand Down Expand Up @@ -287,8 +309,9 @@ List<Type> _normalizeOrderedTypes(MixData? mix, List<Type>? orderedTypes) {
List<WidgetModifierSpec<dynamic>> _combineModifiers(
MixData? mix,
List<WidgetModifierSpec<dynamic>> modifiers,
List<Type> orderOfModifiers,
) {
List<Type> orderOfModifiers, {
List<Type>? defaultOrder,
}) {
final normalizedModifiers = _normalizeOrderedTypes(mix, orderOfModifiers);

final mergedModifiers = [...modifiers];
Expand All @@ -297,5 +320,37 @@ List<WidgetModifierSpec<dynamic>> _combineModifiers(
mergedModifiers.addAll(mix.modifiers);
}

return orderModifiers(normalizedModifiers, mergedModifiers);
return orderModifiers(
normalizedModifiers,
mergedModifiers,
defaultOrder: defaultOrder,
);
}

@visibleForTesting
List<WidgetModifierSpec<dynamic>> orderModifiers(
List<Type> orderOfModifiers,
List<WidgetModifierSpec<dynamic>> modifiers, {
List<Type>? defaultOrder,
}) {
final listOfModifiers = ({
// Prioritize the order of modifiers provided by the user.
...orderOfModifiers,
// Add the default order of modifiers.
...defaultOrder ?? _defaultOrder,
// Add any remaining modifiers that were not included in the order.
...modifiers.map((e) => e.type),
}).toList();

final specs = <WidgetModifierSpec<dynamic>>[];

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;
// ignore: avoid-unnecessary-type-casts
specs.add(modifier as WidgetModifierSpec<WidgetModifierSpec<dynamic>>);
}

return specs;
}
1 change: 0 additions & 1 deletion packages/mix/lib/src/modifiers/modifiers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export 'intrinsic_widget_modifier.dart';
export 'modifiers.dart';
export 'opacity_widget_modifier.dart';
export 'padding_widget_modifier.dart';
export 'render_widget_modifier.dart';
export 'rotated_box_widget_modifier.dart';
export 'sized_box_widget_modifier.dart';
export 'transform_widget_modifier.dart';
Expand Down
2 changes: 1 addition & 1 deletion packages/mix/lib/src/specs/box/box_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +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 '../../modifiers/internal/render_widget_modifier.dart';
import 'box_spec.dart';

/// A [Container] equivalent widget for applying styles using Mix.
Expand Down
2 changes: 1 addition & 1 deletion packages/mix/lib/src/specs/flex/flex_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import 'package:flutter/widgets.dart';

import '../../core/styled_widget.dart';
import '../../modifiers/render_widget_modifier.dart';
import '../../modifiers/internal/render_widget_modifier.dart';
import '../box/box_spec.dart';
import '../box/box_widget.dart';
import 'flex_spec.dart';
Expand Down
2 changes: 1 addition & 1 deletion packages/mix/lib/src/specs/icon/icon_widget.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';

import '../../core/styled_widget.dart';
import '../../modifiers/render_widget_modifier.dart';
import '../../modifiers/internal/render_widget_modifier.dart';
import 'icon_spec.dart';

class StyledIcon extends StyledWidget {
Expand Down
2 changes: 1 addition & 1 deletion packages/mix/lib/src/specs/image/image_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:flutter/widgets.dart';

import '../../core/styled_widget.dart';
import '../../internal/constants.dart';
import '../../modifiers/render_widget_modifier.dart';
import '../../modifiers/internal/render_widget_modifier.dart';
import 'image_spec.dart';

class StyledImage extends StyledWidget {
Expand Down
2 changes: 1 addition & 1 deletion packages/mix/lib/src/specs/stack/stack_widget.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:flutter/widgets.dart';

import '../../core/styled_widget.dart';
import '../../modifiers/render_widget_modifier.dart';
import '../../modifiers/internal/render_widget_modifier.dart';
import '../box/box_spec.dart';
import '../box/box_widget.dart';
import 'stack_spec.dart';
Expand Down
2 changes: 1 addition & 1 deletion packages/mix/lib/src/specs/text/text_widget.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';

import '../../core/styled_widget.dart';
import '../../modifiers/render_widget_modifier.dart';
import '../../modifiers/internal/render_widget_modifier.dart';
import 'text_spec.dart';

/// [StyledText] - A styled widget for displaying text with a mix of styles.
Expand Down
Loading

0 comments on commit bbae869

Please sign in to comment.