Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: MixThemeData can alter default order of modifiers #380

Merged
merged 5 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading