Can I override MaterialBanner widget to change its height?

Viewed 134

I was dealing with the ScaffoldMessenger.of(context).showMaterialBanner method and it needs a MaterialBanner widget as parameter.

Goal

In my case I need to put an empty MaterialBanner widget but with a custom height (less than default).

In few words override the original MaterialBanner build method.

What I tried

so I thought that I had to extend a StatefulWidget implementing MaterialBanner:

class MiniBanner extends StatefulWidget implements MaterialBanner {
  const MiniBanner({Key? key}) : super(key: key);

  @override
  List<Widget> get actions => [const SizedBox()];

  @override
  Animation<double>? get animation => null;

  @override
  Color? get backgroundColor => Colors.amber;

  @override
  Widget get content => const SizedBox();

  @override
  TextStyle? get contentTextStyle => null;

  @override
  double? get elevation => 0;

  @override
  bool get forceActionsBelow => false;

  @override
  Widget? get leading => null;

  @override
  EdgeInsetsGeometry? get leadingPadding => EdgeInsets.zero;

  @override
  VoidCallback? get onVisible => null;

  @override
  OverflowBarAlignment get overflowAlignment => OverflowBarAlignment.end;

  @override
  EdgeInsetsGeometry? get padding => EdgeInsets.zero;

  @override
  MaterialBanner withAnimation(Animation<double> newAnimation, {Key? fallbackKey}) {
    return MaterialBanner(
      key: key ?? fallbackKey,
      content: content,
      contentTextStyle: contentTextStyle,
      actions: actions,
      elevation: elevation,
      leading: leading,
      backgroundColor: backgroundColor,
      padding: padding,
      leadingPadding: leadingPadding,
      forceActionsBelow: forceActionsBelow,
      overflowAlignment: overflowAlignment,
      animation: newAnimation,
      onVisible: onVisible,
    );
  }

  @override
  State<MiniBanner> createState() => _MiniBannerState();
}

class _MiniBannerState extends State<MiniBanner> {
  @override
  Widget build(BuildContext context) {
    // I want the code to go here to build a smaller MaterialBanner
    return Container(
      color: widget.backgroundColor,
      height: 10,
    );
  }
}

But my code keep going to the original MaterialBanner method. Maybe I'm missing some knowledge about extending stateful widgets.

1 Answers

Solved

The main issue was the return type of the "withAnimation" method. It has to be the same type of your custom widget. So:

class MiniBanner extends StatefulWidget implements MaterialBanner {
  const MiniBanner({
    Key key,
    this.content = const SizedBox(),
    this.contentTextStyle,
    this.actions = const [SizedBox()],
    this.elevation = 0,
    this.leading,
    this.backgroundColor = Colors.red, //custom default color
    this.padding = EdgeInsets.zero,
    this.leadingPadding = EdgeInsets.zero,
    this.forceActionsBelow = false,
    this.overflowAlignment = OverflowBarAlignment.end,
    this.animation,
    this.onVisible,
  })  : assert(elevation == null || elevation >= 0.0),
        assert(content != null),
        assert(actions != null),
        assert(forceActionsBelow != null),
        super(key: key);

  @override
  final List<Widget> actions;

  @override
  final Animation<double> animation;

  @override
  final Color backgroundColor;

  @override
  final Widget content;

  @override
  final TextStyle contentTextStyle;

  @override
  final double elevation;

  @override
  final bool forceActionsBelow;

  @override
  final Widget leading;

  @override
  final EdgeInsetsGeometry leadingPadding;

  @override
  final VoidCallback onVisible;

  @override
  final OverflowBarAlignment overflowAlignment;

  @override
  final EdgeInsetsGeometry padding;

  @override
  MiniBanner withAnimation(Animation<double> newAnimation, {Key fallbackKey}) {
    return MiniBanner(
      key: key ?? fallbackKey,
      content: content,
      contentTextStyle: contentTextStyle,
      actions: actions,
      elevation: elevation,
      leading: leading,
      backgroundColor: backgroundColor,
      padding: padding,
      leadingPadding: leadingPadding,
      forceActionsBelow: forceActionsBelow,
      overflowAlignment: overflowAlignment,
      animation: newAnimation,
      onVisible: onVisible,
    );
  }

  @override
  State<MiniBanner> createState() => MiniBannerState();
}

class MiniBannerState extends State<MiniBanner> {
  @override
  Widget build(BuildContext context) {
    // Now our MiniBanner correctly build this method
    return Container(
      color: widget.backgroundColor,
      height: 10,
    );
  }
}

Important note

This code is not null sound safety so if you have a Flutter application with null sound safety enabled you have to change some syntax, like adding ? after the nullable properties and so on.

UPDATE

If you want to really emulate the original MaterialBanner with all the animations, you have to copy almost the entire original "build" method. Then, to custom your widget, you just have to edit the assignment of materialBanner variable, example:

// other parts of code copied from original build method above

Widget materialBanner = Container(
  color: widget.backgroundColor,
  height: 10,
);

// other parts of code copied from original build method below
Related