Widget Modifiers
Modifiers wrap a widget with another widget (like Transform, Padding, or Opacity) to add effects without changing the widget tree. Use the .wrap() method with a ModifierMix class.
How Modifiers Work
Use .wrap() with a modifier mix class to wrap a widget:
import 'package:flutter/material.dart';
import 'package:mix/mix.dart';
final style = BoxStyler()
.color(Colors.red)
.size(100, 100)
.wrap(.opacity(0.4));This produces a widget tree equivalent to Opacity(opacity: 0.4, child: Container(...)).
Built-in Modifiers
| ModifierMix Class | Description | Wrapper Widget |
|---|---|---|
.opacity(value) | Sets opacity | Opacity |
.padding(insets) | Adds padding | Padding |
.align(alignment) | Sets alignment | Align |
.aspectRatio(ratio) | Constrains aspect ratio | AspectRatio |
.flexible(flex: flex, fit: fit) | Makes flexible in Flex layouts | Flexible |
.transform(matrix) | Applies transform | Transform |
.visibility(visible: visible) | Shows/hides widget | Visibility |
.clipRect() | Clips to rectangle | ClipRect |
.clipRRect(borderRadius: radius) | Clips to rounded rectangle | ClipRRect |
.clipOval() | Clips to oval | ClipOval |
Usage Examples
import 'package:flutter/material.dart';
import 'package:mix/mix.dart';
// Combine multiple modifiers
final cardStyle = BoxStyler()
.color(Colors.white)
.size(200, 100)
.wrap(.opacity(0.9))
.wrap(.padding(.all(16)))
.wrap(.align(alignment: .center));
// Use transform methods for scale/rotate
final buttonStyle = BoxStyler()
.color(Colors.blue)
.paddingAll(16)
.scale(1.0)
.onHovered(BoxStyler().scale(1.1));Modifier Ordering
Modifiers are applied from innermost to outermost in the order they are chained. This means the last modifier in the chain becomes the outermost wrapper:
// Result: Align → Opacity → Padding → Box
final style = BoxStyler()
.wrap(.padding(.all(8))) // innermost wrapper
.wrap(.opacity(0.5)) // middle wrapper
.wrap(.align(alignment: .center)); // outermost wrapperThe order of modifiers matters. Padding applied before opacity will have its padding inside the opacity effect, while padding after opacity will be outside.
Creating Custom Modifiers
Create custom modifiers with two classes:
- WidgetModifier - Builds the wrapper widget
- ModifierMix - Handles merging and resolving
Step 1: Create the WidgetModifier
import 'package:flutter/widgets.dart';
import 'package:mix/mix.dart';
/// Modifier that applies opacity to its child.
final class OpacityModifier extends WidgetModifier<OpacityModifier> {
/// Opacity value between 0.0 and 1.0 (inclusive).
final double opacity;
const OpacityModifier([double? opacity]) : opacity = opacity ?? 1.0;
@override
OpacityModifier copyWith({double? opacity}) {
return OpacityModifier(opacity ?? this.opacity);
}
@override
OpacityModifier lerp(OpacityModifier? other, double t) {
if (other == null) return this;
// Linear interpolation for smooth animations
return OpacityModifier(MixOps.lerp(opacity, other.opacity, t)!);
}
@override
List<Object?> get props => [opacity];
@override
Widget build(Widget child) {
return Opacity(opacity: opacity, child: child);
}
}Key methods to implement:
| Method | Purpose |
|---|---|
copyWith | Creates a copy with optional property updates |
lerp | Linear interpolation for animations between two modifier states |
props | List of properties for equality comparison |
build | Constructs the wrapper widget |
Step 2: Create the ModifierMix
The ModifierMix class makes the modifier mergeable and resolvable. It uses Prop<T> for properties to support tokens and lazy resolution.
import 'package:flutter/widgets.dart';
import 'package:mix/mix.dart';
/// Mix class for applying opacity modifications.
class OpacityModifierMix extends ModifierMix<OpacityModifier> {
final Prop<double>? opacity;
const OpacityModifierMix.create({this.opacity});
/// Convenience constructor with direct value
OpacityModifierMix({double? opacity})
: this.create(opacity: Prop.maybe(opacity));
@override
OpacityModifier resolve(BuildContext context) {
return OpacityModifier(opacity?.resolveProp(context));
}
@override
OpacityModifierMix merge(OpacityModifierMix? other) {
if (other == null) return this;
return OpacityModifierMix.create(
opacity: MixOps.merge(opacity, other.opacity),
);
}
@override
List<Object?> get props => [opacity];
}Key methods:
| Method | Purpose |
|---|---|
resolve | Resolves Prop values using BuildContext and creates the final WidgetModifier |
merge | Combines two ModifierMix instances, with other’s values taking precedence |
props | List of properties for equality comparison |
Step 3: Use the Custom Modifier
To use your custom modifier in a style, use the .wrap() method:
import 'package:flutter/material.dart';
import 'package:mix/mix.dart';
final style = BoxStyler()
.color(Colors.red)
.size(100, 100)
.wrap(.opacity(0.4));
// With tokens
final $opacityToken = DoubleToken('custom.opacity');
final tokenStyle = BoxStyler()
.color(Colors.blue)
.wrap(.opacity(Prop.token($opacityToken)));Best Practices
- Use modifiers for transforms, visibility, layout adjustments, and clipping
- Use styler methods (not modifiers) for colors, borders, shadows
- Avoid deeply nested modifier chains
- Implement
lerpproperly for smooth animations