widget_modifier 0.1.1 copy "widget_modifier: ^0.1.1" to clipboard
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

logo

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: a List<SingleChildModifier> basically the wrapper for a typical Widget which has one child
  • child: child of Modifier

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 to Modifier
  • wrapWith(): insert SingleChildModifier to the beginning of the modifiers list
  • wrapWithAll(): insert List<SingleChildModifier> to the beginning of the modifiers 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:

26
likes
90
pub points
0%
popularity

Publisher

unverified uploader

A collection of built-in flutter widget modifiers, which flatten the widget tree and make it easier to read

Repository (GitHub)
View/report issues

Documentation

API reference

License

BSD-3-Clause (LICENSE)

Dependencies

flutter, vector_math

More

Packages that depend on widget_modifier