widget_modifier 0.1.1 widget_modifier: ^0.1.1 copied to clipboard
A collection of built-in flutter widget modifiers, which flatten the widget tree and make it easier to read
A collection of built-in flutter widget modifiers, which flatten the widget tree and make it easier to read.
Credit: Original idea and the widget_modifier
base code is a modified version of nested
widget by @remi_rousselet
Problem #
AspectRatio(
aspectRatio: 16 / 8,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: DecoratedBox(
decoration: BoxDecoration(
color: const Color(0xFFe1e4e3),
borderRadius: BorderRadius.circular(20),
),
child: Padding(
padding: const EdgeInsets.all(20),
child: Row(),
),
),
)
This is a typical flutter widget. With four nested widgets, the code is pretty hard to read. Your eyes need to skim to Padding
, AspectRatio
, DecoratedBox
, and Padding
to reach out to the actual child widget. This process is unnecessary and consumes your brain's power to do.
Of course, no one in the real world does this. Instead, we use a big friendly (or maybe not) guy - Container
. The previous widget becomes this.
AspectRatio(
aspectRatio: 16 / 8,
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(
color: const Color(0xFFe1e4e3),
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.all(20),
child: Row(),
),
)
But why do we still have AspectRatio
widget? Because Container
doesn't have aspectRatio
field. It means Container
is not flexible to provide all kinds of configurations you need.
To fix this, widget_modifier
introduces a new set of APIs to "flatten" your widget.
Usage #
Widget modifier have two API styles: declarative
and cascading
Declarative #
Declarative modifier version of the above example widget
Modifier(
modifiers: [
AspectRatioModifier(
aspectRatio: 16 / 8,
),
PaddingModifier(
padding: const EdgeInsets.symmetric(horizontal: 20),
),
DecoratedBoxModifier(
decoration: BoxDecoration(
color: const Color(0xFFe1e4e3),
borderRadius: BorderRadius.circular(20),
),
),
PaddingModifier(
padding: const EdgeInsets.all(20),
)
],
child: Row(),
),
modifiers
: aList<SingleChildModifier>
basically the wrapper for a typicalWidget
which has one childchild
: child ofModifier
The declarative modifier will apply from the bottom element to the top element of the modifiers
list.
Pros & Cons
Pros:
- Easy to detect where the root
child
widget - Nice code format
- Work well with editor code folding
Cons:
- Counter-intuitive at first
Cascading #
Cascading modifier version of the above example widget
Row()
.modified()
.wrapWith(PaddingModifier(
padding: const EdgeInsets.all(20),
))
.wrapWith(DecoratedBoxModifier(
decoration: BoxDecoration(
color: const Color(0xFFe1e4e3),
borderRadius: BorderRadius.circular(20),
),
))
.wrapWith(PaddingModifier(
padding: const EdgeInsets.symmetric(horizontal: 20),
))
.wrapWith(AspectRatioModifier(
aspectRatio: 16 / 8,
))
modified()
: extenstion turn your widget toModifier
wrapWith()
: insertSingleChildModifier
to the beginning of themodifiers
listwrapWithAll()
: insertList<SingleChildModifier>
to the beginning of themodifiers
list
The cascading modifier will apply from the top wrapWith
/wrapWithAll
element to the bottom.
Pros & Cons
Pros:
- Easy to detect where the root
child
widget - Intuitive to read
Cons:
- Code format is sometimes ugly
Custom modifier #
If you want to make a custom modifier check out SingleChildStatelessModifier
/SingleChildStatefulModifier
equivalents version StatelessWidget
/StatefulWidget
Caution
⚠️ You need to override buildWithChild
instead of build
method
⚠️ buildWithChild
is like build
but has another child
parameter which is the widget Modifier
given to SingleChildModifier
⚠️ modifierKey
is the key of the actual widget inside the modifier
SingleChildStatelessModifier #
class ConstrainedBoxModifier extends SingleChildStatelessModifier {
ConstrainedBoxModifier({
Key? key,
Widget? child,
super.modifierKey,
required this.constraints,
}) : assert(constraints.debugAssertIsValid()),
super(key: key, child: child);
final BoxConstraints constraints;
@override
Widget buildWithChild(BuildContext context, Widget? child) {
return ConstrainedBox(
key: modifierKey,
constraints: constraints,
child: child,
);
}
}
SingleChildStatefulModifier #
You need to implements SingleChildStateMixin
mixin in State
class of SingleChildStatefulModifier
class StatefulBuilderModifier extends SingleChildStatefulModifier {
const StatefulBuilderModifier({
super.key,
super.modifierKey,
required this.builder,
});
final SingleChildStatefulWidgetBuilder builder;
@override
State<StatefulWidget> createState() => _StatefulBuilderModifierState();
}
class _StatefulBuilderModifierState extends State<StatefulBuilderModifier>
with SingleChildStateMixin<StatefulBuilderModifier> {
@override
Widget buildWithChild(BuildContext context, Widget child) {
return widget.builder(context, setState, child);
}
}
Editor support #
You can easily convert existing widget to Modifier
with my extension. See more at: