ElevatedButton, TextButton and OutlinedButton gradient

Viewed 2635

Before it was easy to make a gradient button with the default ButtonThemeData... But now I can't figure out how to properly make a custom gradient button using the new MaterialButtons.

I am trying to make a three custom gradient button which must use the ButtonStyle defined in the AppTheme (splashColor, elevation, ...).

ElevatedButton

GradientElevatedButton that uses the ElevatedButtonThemeData with a gradient background

TextButton

GradientTextButton that uses the TextButtonThemeData with a gradient text

OutlinedButton

GradientOutlinedButton that uses the OutlinedButtonThemeData with a gradient border and a gradient text

Already tried

I have tried to wrap an ElevatedButton with a ShaderMask but it covers the ink animations so it doesn't accomplish my goals.

2 Answers
  • ElevatedButton

    enter image description here

    DecoratedBox(
      decoration: BoxDecoration(gradient: LinearGradient(colors: [Colors.red, Colors.blue])),
      child: ElevatedButton(
        onPressed: () {},
        style: ElevatedButton.styleFrom(primary: Colors.transparent),
        child: Text('Elevated Button'),
      ),
    )
    

  • OutlinedButton

    enter image description here

    For OutlinedButton, you need to do some extra steps. Create a class (null-safe):

    class MyOutlinedButton extends StatelessWidget {
      final VoidCallback onPressed;
      final Widget child;
      final ButtonStyle? style;
      final Gradient? gradient;
      final double thickness;
    
      const MyOutlinedButton({
        Key? key,
        required this.onPressed,
        required this.child,
        this.style,
        this.gradient,
        this.thickness = 2,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return DecoratedBox(
          decoration: BoxDecoration(gradient: gradient),
          child: Container(
            color: Colors.white,
            margin: EdgeInsets.all(thickness),
            child: OutlinedButton(
              onPressed: onPressed,
              style: style,
              child: child,
            ),
          ),
        );
      }
    }
    

    Usage:

    MyOutlinedButton(
      onPressed: () {},
      gradient: LinearGradient(colors: [Colors.indigo, Colors.pink]),
      child: Text('OutlinedButton'),
    )
    

I don't know if it helps, but I just did it by myself this way. Still don't get why they have to make things more complicated.

import 'package:flutter/material.dart';
import 'package:my_app/src/utils/style_helper.dart';
import 'package:my_app/src/utils/themes.dart';

class SecondaryButtonState extends State<SecondaryButton> {

  setPressed(bool value) {
    setState(() {
      widget.pressed = value;
    });
  }

  getDecoration() {
    if (widget.disabled) {
      return BoxDecoration(
        borderRadius: BorderRadius.circular(SH.BORDER_RADIUS),
        color: ThemeColors.secondaryLight,
      );
    }
    if (!widget.pressed) {
      return BoxDecoration(
        borderRadius: BorderRadius.circular(16),
        gradient: LinearGradient(
            begin: FractionalOffset.topCenter,
            end: FractionalOffset.bottomCenter,
            colors: [ThemeColors.gradient, ThemeColors.secondary]),
      );
    } else {
      return BoxDecoration(
          borderRadius: BorderRadius.circular(SH.BORDER_RADIUS),
          color: ThemeColors.secondary);
    }
  }

  void onTapDown(TapDownDetails details) {
    if (!widget.disabled) {
      setPressed(true);
    }
  }

  void onTapUp(TapUpDetails details) {
    if (!widget.disabled) {
      setPressed(false);
      widget.onPressed();
    }
  }

  TextStyle getTextStyle(context, Set<MaterialState> states) {
    return Theme.of(context)
        .textButtonTheme
        .style
        .textStyle
        .resolve(states)
        .copyWith();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: onTapDown,
      onTapUp: onTapUp,
      child: Container(
          width: 400,
          decoration: getDecoration(),
          padding: EdgeInsets.symmetric(vertical: SH.PC, horizontal: SH.P3),
          child: Row(
              mainAxisSize: MainAxisSize.min,
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                widget.icon != null ? widget.icon : Container(),
                Text(
                  widget.label,
                  style: Theme.of(context)
                      .elevatedButtonTheme
                      .style
                      .textStyle
                      .resolve(null)
                      .copyWith(),
                ),
              ])),
    );
  }
}

class SecondaryButton extends StatefulWidget {
  SecondaryButton(
      {@required this.onPressed,
      @required this.label,
      this.icon,
      this.disabled = false});

  final GestureTapCallback onPressed;
  final String label;
  bool disabled;
  final Icon icon;
  bool pressed = false;

  @override
  State<StatefulWidget> createState() {
    return SecondaryButtonState();
  }
}

Related