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 WidgetModifierConfig class.
How Modifiers Work
Use .wrap() with a modifier config 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
| Modifier | 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(.scale(1.1));Modifier Ordering
Built-in modifiers follow a predefined ordering defined by the framework, regardless of the order you chain them. Mix enforces a specific modifier order to ensure consistent widget tree structure:
- Context & Behavior —
Flexible,Visibility,IconTheme,DefaultTextStyle - Size Establishment —
SizedBox,FractionallySizedBox,IntrinsicWidth/Height - Positioning —
Align,Padding,AspectRatio - Visual Effects —
Opacity,Transform,ClipRect/RRect/Oval
// These two produce the same widget tree — chain order has no effect
final a = BoxStyler()
.wrap(.opacity(0.5))
.wrap(.padding(.all(8)));
final b = BoxStyler()
.wrap(.padding(.all(8)))
.wrap(.opacity(0.5));The framework reorders modifiers according to its internal ordering rules. If you need explicit control over ordering, create a custom modifier with a specific position in the tree.
Creating Custom Modifiers
Create custom modifiers with two classes:
- WidgetModifier - Builds the wrapper widget
- WidgetModifierConfig (via
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 extends ModifierMix<T> and gets wrapped in a WidgetModifierConfig when passed to .wrap(). 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(MixOps.resolve(context, opacity));
}
@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 instances, with the other’s values taking precedence |
props | List of properties for equality comparison |
Step 3: Use the Custom Modifier
Pass an instance of your ModifierMix class to .wrap():
import 'package:flutter/material.dart';
import 'package:mix/mix.dart';
// Pass the custom ModifierMix instance to .wrap()
final style = BoxStyler()
.color(Colors.red)
.size(100, 100)
.wrap(OpacityModifierMix(opacity: 0.4));Mix ships with built-in modifier shorthands like .wrap(.opacity(0.4)). When you create a custom modifier, you use .wrap(YourModifierMix(...)) with the full class name instead.
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
Related
- Styling with Mix — fluent Styler API basics
- Box —
BoxStylerAPI reference - FlexBox —
FlexBoxStylerAPI reference