Found this cool way to write UI in flutter

So I was asking gpt for productivity boost advice then he give something like this

extension WidgetX on Widget {
  // Padding
  Widget padding(final EdgeInsets padding) =>
      Padding(padding: padding, child: this);
}

This is honesly a game changer for me instead of doing nested widget define 100 object one on the other it’s just a nightmare to deal with man

Loot at this cleaner aproach


class AddExerciseButton extends ConsumerWidget {
  const AddExerciseButton({
    super.key,
  });

  @override
  Widget build(final BuildContext context, final WidgetRef ref) {
    final AppLocalizations l10n = context.l10n;
    final Color workoutColor = ref.watch(workoutProvider).workout.color;
    final ColorTheme theme = context.customTheme<ColorTheme>();
    return GestureDetector(
      onTap: () {
        Navigator.of(context).pushNamed(Screens.addExercise.route);
      },
      child: Text(
        l10n.addExercise,
        style: TextStyle(
          fontFamily: l10n.fontFamily,
          fontWeight: FontWeight.w600,
          fontSize: FontSize.normal,
          color: theme.white,
        ),
        textAlign: TextAlign.center,
      )
          .paddingAll(Spacing.normal)
          .paddingVertical(Spacing.xxSmall)
          .backgroundColor(workoutColor)
          .circular(Spacing.normal)
          .infiniteWidth(),
    );
  }
}

This is way better no bloat no deeply wrapped widget mess, ez switch widget with parent with even grand parent no problem it’s just a life changing

Remi beat you to it by six years: functional_widget | Dart package

1 Like

Bro the whole point was about the method chaining and how it’s saved me so much time this package just make a widget functional which is good but you still gonna have nesting hell

Actually, this looks more similar to what Younesse is doing:

Coming from the SwiftUI world, I found modifiers difficult there as it could be difficult to determine what modifiers were available for what widget. Flutter’s “nesting hell” and having everything available documented in the constructor has its benefits.

Wasn’t there a proposal or something to officially add Widget modifiers to Flutter?

2 Likes

Honestly, it’s quite ez to add them here is mine:

import 'package:flutter/material.dart';
import 'package:my_utils/extensions/border_radius_extension.dart';

extension WidgetX on Widget {
  Widget showIf(final bool condition) =>
      condition ? this : const SizedBox.shrink();

  Widget showIfNot(final bool condition) =>
      !condition ? this : const SizedBox.shrink();

  Widget showIfOr(final bool condition, final Widget alternative) =>
      condition ? this : alternative;

  Widget showIfElse(
    final bool condition,
    final Widget trueWidget,
    final Widget falseWidget,
  ) => condition ? trueWidget : falseWidget;

  Widget showIfNotNull<T>(final T? value) =>
      value != null ? this : const SizedBox.shrink();

  Widget showIfEmpty<T>(final Iterable<T> collection) =>
      collection.isEmpty ? this : const SizedBox.shrink();

  Widget showIfNotEmpty<T>(final Iterable<T> collection) =>
      collection.isNotEmpty ? this : const SizedBox.shrink();

  Widget showIfZero(final num value) =>
      value == 0 ? this : const SizedBox.shrink();

  Widget showIfNotZero(final num value) =>
      value != 0 ? this : const SizedBox.shrink();

  // Padding
  Widget padding(final EdgeInsets padding) =>
      Padding(padding: padding, child: this);
  Widget paddingAll(final double value) =>
      Padding(padding: EdgeInsets.all(value), child: this);
  Widget paddingSymmetric({
    final double vertical = 0.0,
    final double horizontal = 0.0,
  }) => Padding(
    padding: EdgeInsets.symmetric(vertical: vertical, horizontal: horizontal),
    child: this,
  );

  Widget paddingVertical(final double value) => Padding(
    padding: EdgeInsets.symmetric(vertical: value),
    child: this,
  );
  Widget paddingHorizontal(final double value) => Padding(
    padding: EdgeInsets.symmetric(horizontal: value),
    child: this,
  );

  Widget paddingStart(final double value) => Padding(
    padding: EdgeInsetsDirectional.only(start: value),
    child: this,
  );

  Widget paddingEnd(final double value) => Padding(
    padding: EdgeInsetsDirectional.only(end: value),
    child: this,
  );

  // Centering & Alignment
  Widget center() => Center(child: this);
  Widget align([final Alignment alignment = Alignment.center]) =>
      Align(alignment: alignment, child: this);
  Widget alignTopLeft() => Align(alignment: Alignment.topLeft, child: this);
  Widget alignCenter() => Align(child: this);

  Widget alignTopCenter() => Align(alignment: Alignment.topCenter, child: this);
  Widget alignTopRight() => Align(alignment: Alignment.topRight, child: this);
  Widget alignCenterLeft() =>
      Align(alignment: Alignment.centerLeft, child: this);
  Widget alignCenterRight() =>
      Align(alignment: Alignment.centerRight, child: this);
  Widget alignBottomLeft() =>
      Align(alignment: Alignment.bottomLeft, child: this);
  Widget alignBottomCenter() =>
      Align(alignment: Alignment.bottomCenter, child: this);
  Widget alignBottomRight() =>
      Align(alignment: Alignment.bottomRight, child: this);

  // Flex
  Widget expand({final int flex = 1}) => Expanded(flex: flex, child: this);
  Widget flexible({final int flex = 1, final FlexFit fit = FlexFit.loose}) =>
      Flexible(flex: flex, fit: fit, child: this);

  // Sizing
  Widget width(final double value) => SizedBox(width: value, child: this);
  Widget height(final double value) => SizedBox(height: value, child: this);
  Widget size({final double? height, final double? width}) =>
      SizedBox(height: height, width: width, child: this);
  Widget square(final double size) =>
      SizedBox(width: size, height: size, child: this);

  Widget infiniteWidth() => SizedBox(width: double.infinity, child: this);
  Widget infiniteHeight() => SizedBox(height: double.infinity, child: this);

  // Interaction
  Widget onTap(final VoidCallback onTap) =>
      GestureDetector(onTap: onTap, child: this);
  Widget onLongPress(final VoidCallback onLongPress) =>
      GestureDetector(onLongPress: onLongPress, child: this);

  // Clipping & Styling
  Widget borderRadius(final BorderRadius radius) =>
      ClipRRect(borderRadius: radius, child: this);
  Widget circular(final double radius) => ClipRRect(
    borderRadius: BorderRadius.all(Radius.circular(radius)),
    child: this,
  );
  Widget opacity(final double value) => Opacity(opacity: value, child: this);

  Widget circle({final Clip clipBehavior = Clip.antiAlias}) =>
      ClipRRect(borderRadius: BorderRadiusX.circle, child: this);

  // Sliver utils
  Widget toSliverBoxAdapter() => SliverToBoxAdapter(child: this);

  // Visibility & Animation
  Widget visible(final bool visible) =>
      Visibility(visible: visible, child: this);
  Widget offstage(final bool offstage) =>
      Offstage(offstage: offstage, child: this);
  Widget ignorePointer() => IgnorePointer(child: this);
  Widget absorbPointer() => AbsorbPointer(child: this);
  Widget decoration(final Decoration decoration) =>
      DecoratedBox(decoration: decoration, child: this);
  Widget decorated({
    final Color? color,
    final DecorationImage? image,
    final BoxBorder? border,
    final BorderRadius? borderRadius,
    final List<BoxShadow>? boxShadow,
    final Gradient? gradient,
    final BoxShape shape = BoxShape.rectangle,
    final BlendMode? backgroundBlendMode,
  }) => DecoratedBox(
    decoration: BoxDecoration(
      color: color,
      image: image,
      border: border,
      borderRadius: borderRadius,
      boxShadow: boxShadow,
      gradient: gradient,
      shape: shape,
      backgroundBlendMode: backgroundBlendMode,
    ),
    child: this,
  );

  Widget scale(final double scale) =>
      Transform.scale(scale: scale, child: this);
  Widget scaleX(final double scale) =>
      Transform.scale(scaleX: scale, child: this);
  Widget scaleY(final double scale) =>
      Transform.scale(scaleY: scale, child: this);
  Widget scaleXY({
    required final double scaleX,
    required final double scaleY,
  }) => Transform.scale(scaleX: scaleX, scaleY: scaleY, child: this);

  Widget backgroundColor(final Color color) =>
      ColoredBox(color: color, child: this);
}

1 Like

Its just preference, nesting vs chaining. For the record the flutter team did consider adding this officially but decided not to. More details here

2 Likes

For single child widgets I prefer chaining for widget like GestureDetector with multiple parameter just nest it man