Moein Hosseini
Moein Hosseini

Reputation: 1596

How to add a gradient to a Button in Flutter?

Is there a way to change the ElevatedButton background color to a gradient?

Upvotes: 76

Views: 101282

Answers (15)

Basith P
Basith P

Reputation: 47

You can wrap it with a Container and apply the gradient. Then set the backgroundColor of ElevatedButton to Colors.transparent

Container(
  decoration: const BoxDecoration(
    gradient: <Gradient here>,
  ),
  child: ElevatedButton(
    style: ElevatedButton.styleFrom(
      backgroundColor: Colors.transparent, // set this
    ),
    onPressed: () {},
    child: const Text('Text'),
  ),
),

Upvotes: 0

丁晓宇
丁晓宇

Reputation: 21

Here is a good solution that can include theme color configuration simultaneously and also inherit most of the properties of ButtonThemeData.

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.10.6, on macOS 14.2.1 23C71 darwin-arm64, locale zh-Hans-CN)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.0.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2022.3)
[✓] VS Code (version 1.85.2)
[✓] Connected device (3 available)
[✓] Network resources

button.dart

// ignore_for_file: overridden_fields

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

import 'button_theme.dart';

class GradientMaterialButton extends MaterialButton {
  const GradientMaterialButton({
    super.key,
    this.fillGradient,
    this.shadowColor,
    double? elevation,
    required super.onPressed,
    super.onLongPress,
    super.onHighlightChanged,
    super.mouseCursor,
    super.textTheme,
    super.textColor,
    super.disabledTextColor,
    super.colorBrightness = Brightness.dark,
    super.splashColor,
    super.padding,
    super.visualDensity,
    super.shape,
    super.clipBehavior = Clip.none,
    super.focusNode,
    super.autofocus = false,
    super.animationDuration,
    super.minWidth,
    super.height,
    super.enableFeedback = true,
    super.child,
  })  : usedElevation = elevation,
        materialTapTargetSize = MaterialTapTargetSize.shrinkWrap,
        color = Colors.transparent,
        disabledColor = Colors.transparent,
        focusColor = Colors.transparent,
        hoverColor = Colors.transparent,
        highlightColor = Colors.transparent,
        elevation = 0.0,
        focusElevation = 0.0,
        hoverElevation = 0.0,
        highlightElevation = 0.0,
        disabledElevation = 0.0;

  final Gradient? fillGradient;

  final Color? shadowColor;

  final double? usedElevation;

  @override
  final MaterialTapTargetSize materialTapTargetSize;
  @override
  final Color color;
  @override
  final Color disabledColor;
  @override
  final Color focusColor;
  @override
  final Color hoverColor;
  @override
  final Color highlightColor;
  @override
  final double elevation;
  @override
  final double focusElevation;
  @override
  final double hoverElevation;
  @override
  final double highlightElevation;
  @override
  final double disabledElevation;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context).extension<GradientMaterialButtonThemeData>();
    final background = Material(
      color: Colors.transparent,
      clipBehavior: Clip.antiAlias,
      shape: shape ?? ButtonTheme.of(context).shape,
      elevation: usedElevation ?? theme?.elevation ?? 0.0,
      shadowColor: shadowColor ?? theme?.shadowColor,
      child: AnimatedContainer(
        duration: animationDuration ?? kThemeChangeDuration,
        decoration: BoxDecoration(
          gradient: fillGradient ?? theme?.fillGradient,
        ),
      ),
    );
    return Stack(
      clipBehavior: Clip.none,
      children: [
        Positioned.fill(child: background),
        super.build(context),
      ],
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<Gradient>('fillGradient', fillGradient, defaultValue: null));
    properties.add(ColorProperty('shadowColor', shadowColor, defaultValue: null));
    properties.add(DoubleProperty('usedElevation', usedElevation, defaultValue: null));
  }
}

button_theme.dart

import 'dart:ui' as ui;

import 'package:flutter/material.dart';

class GradientMaterialButtonThemeData extends ThemeExtension<GradientMaterialButtonThemeData> {
  GradientMaterialButtonThemeData({
    this.fillGradient,
    this.shadowColor,
    this.elevation,
  });

  final Gradient? fillGradient;

  final Color? shadowColor;

  final double? elevation;

  @override
  ThemeExtension<GradientMaterialButtonThemeData> copyWith({
    Gradient? fillGradient,
    Color? shadowColor,
    double? elevation,
  }) =>
      GradientMaterialButtonThemeData(
        fillGradient: fillGradient ?? this.fillGradient,
        shadowColor: shadowColor ?? this.shadowColor,
        elevation: elevation ?? this.elevation,
      );

  @override
  ThemeExtension<GradientMaterialButtonThemeData> lerp(
    covariant GradientMaterialButtonThemeData? other,
    double t,
  ) =>
      GradientMaterialButtonThemeData(
        fillGradient: Gradient.lerp(fillGradient, other?.fillGradient, t),
        shadowColor: Color.lerp(shadowColor, other?.shadowColor, t),
        elevation: ui.lerpDouble(elevation, other?.elevation, t),
      );
}

main.dart

import 'package:flutter/material.dart';

import 'button_theme.dart';
import 'button.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Button Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
        buttonTheme: ButtonThemeData(
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
        ),
        textTheme: const TextTheme(
          labelLarge: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w500),
        ),
        extensions: [
          GradientMaterialButtonThemeData(
            elevation: 5,
            shadowColor: const Color(0x52007DBF),
            fillGradient: const LinearGradient(colors: [Color(0xFF359EEB), Color(0xFF007DBF)]),
          ),
        ],
      ),
      home: const MyHomePage(title: 'Button Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            GradientMaterialButton(
              // elevation: 5,
              // shadowColor: Colors.red,
              // fillGradient: const LinearGradient(colors: [Colors.red, Colors.blue]),
              onPressed: () {},
              child: const Text('GradientMaterialButton'),
            ),
          ],
        ),
      ),
    );
  }
}

Upvotes: 0

CopsOnRoad
CopsOnRoad

Reputation: 268304

Update:

Since RaisedButton and OutlineButton are deprecated, you should use ElevatedButton and OutlinedButton.

  • ElevatedButton

    enter image description here

    Container(
      height: 44.0,
      decoration: BoxDecoration(gradient: LinearGradient(colors: [Colors.pink, Colors.green])),
      child: ElevatedButton(
        onPressed: () {},
        style: ElevatedButton.styleFrom(backgroundColor: Colors.transparent, shadowColor: Colors.transparent),
        child: Text('Elevated Button'),
      ),
    )
    

  • OutlinedButton

    enter image description here

    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({
        super.key,
        required this.onPressed,
        required this.child,
        this.style,
        this.gradient,
        this.thickness = 2,
      });
    
      @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'),
    )
    

Upvotes: 30

HoRiz
HoRiz

Reputation: 798

Result -

enter image description here


Code for CustomGradientButton.dart

class CustomGradientButton extends StatelessWidget {
  final Gradient gradient;
  final VoidCallback onPressed;
  final String text;

  const CustomGradientButton({
    Key? key,
    required this.gradient,
    required this.onPressed,
    required this.text,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 45.0, // Adjust the height as needed
      child: ElevatedButton(
        onPressed: onPressed,
        style: ButtonStyle(
          shape: MaterialStateProperty.all<RoundedRectangleBorder>(
            RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(80.0),
            ),
          ),
          padding: MaterialStateProperty.all<EdgeInsetsGeometry>(
            const EdgeInsets.all(0.0),
          ),
          backgroundColor: MaterialStateProperty.all<Color>(Colors.transparent),
          overlayColor: MaterialStateProperty.all<Color>(Colors.transparent),
          foregroundColor: MaterialStateProperty.all<Color>(Colors.white),
          minimumSize: MaterialStateProperty.all<Size>(
            const Size(88.0, 36.0),
          ),
          elevation: MaterialStateProperty.all<double>(0.0),
          textStyle: MaterialStateProperty.all<TextStyle>(
            TextStyle(color: Colors.white),
          ),
        ),
        child: Ink(
          decoration: BoxDecoration(
            gradient: gradient,
            borderRadius: BorderRadius.all(Radius.circular(80.0)),
          ),
          child: Container(
            alignment: Alignment.center,
            child: Text(text),
          ),
        ),
      ),
    );
  }
}

How to call it

CustomGradientButton(
                    gradient: LinearGradient(
                      colors: [Colors.blue, Colors.green],
                      begin: Alignment.topLeft,
                      end: Alignment.bottomRight,
                    ),
                    onPressed: () {
                      // Perform some action when the button is pressed
                      print('Button pressed!');
                    },
                    text: 'OK',
                  ),

Upvotes: 0

Mr commit
Mr commit

Reputation: 76

This what worker for me. RaisedButton being deprecated i used ElevatedButton. So this should be the way to go.

import 'package:flutter/material.dart';

class MainButton extends StatelessWidget {
  final Widget thechild;
  final double width;
  final double height;
  final Function theaction;

  const MainButton({
    Key key,
    @required this.thechild,
    this.width = double.infinity,
    this.height = 50.0,
    this.theaction,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: theaction,
      style: ElevatedButton.styleFrom(
        primary: Colors.transparent,
        elevation: 4.0,
        minimumSize: Size(88.0, 45.0),
        padding: const EdgeInsets.all(0.0),
      ),
      child: Ink(
        decoration: const BoxDecoration(
          gradient: LinearGradient(
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
              stops: [
                0.1,
                0.8,
                0.9
              ],
              colors: [
                Color.fromARGB(255, 186, 252, 244),
                Color.fromARGB(255, 55, 183, 230),
                Color.fromARGB(255, 49, 175, 230),
              ]),
          borderRadius: BorderRadius.all(Radius.circular(8.0)),
        ),
        child: Container(
          constraints: const BoxConstraints(minWidth: 88.0, minHeight: 45.0),
          alignment: Alignment.center,
          child: thechild,
        ),
      ),
    );
  }
}

Usage

Import first

import '../widgets/mainButton.dart';

then

MainButton(
                        thechild: Text(AppLocalization.translate('loginbtn'),
                            textAlign: TextAlign.center,
                            style: TextStyle(
                                color: Colors.white,
                                fontWeight: FontWeight.bold,
                                fontSize: 20)),
                        theaction: () {},
                      ),

Upvotes: 0

Niket Tongare
Niket Tongare

Reputation: 632

DecoratedBox(
    decoration: ShapeDecoration(
        shape:
            RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
        gradient: RadialGradient(
            radius: 2, colors: [Colors.white, Colors.redAccent]),),
     child:RaisedButton(),
),

Add Gradient To Any Widget Using DecoratedBox As Parent Widget

Example

Upvotes: -1

ztvmark
ztvmark

Reputation: 1448

Docs last example https://api.flutter.dev/flutter/material/RaisedButton-class.html

RaisedButton(
  onPressed: () {},
  textColor: Colors.white,
  padding: const EdgeInsets.all(0.0),
  child: Container(
    decoration: const BoxDecoration(
      gradient: LinearGradient(
        colors: <Color>[
          Color(0xFF0D47A1),
          Color(0xFF1976D2),
          Color(0xFF42A5F5),
        ],
      ),
    ),
    padding: const EdgeInsets.all(10.0),
    child: const Text(
      'Gradient Button',
      style: TextStyle(fontSize: 20)
    ),
  ),
);

Upvotes: 2

bonnyz
bonnyz

Reputation: 13558

All the solution above are not really working without some minor artifacts or issues (e.g. missing ripple effect, unwanted borders, not respecting the theme's minWidth for buttons).

The solution below has none of the above issues (the critical part is to use the Ink widget to retain the ripple capabilities over the gradient):

RaisedButton(
  onPressed: () { },
  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(80.0)),
  padding: const EdgeInsets.all(0.0),
  child: Ink(
    decoration: const BoxDecoration(
      gradient: myGradient,
      borderRadius: BorderRadius.all(Radius.circular(80.0)),
    ),
    child: Container(
      constraints: const BoxConstraints(minWidth: 88.0, minHeight: 36.0), // min sizes for Material buttons
      alignment: Alignment.center,
      child: const Text(
        'OK',
        textAlign: TextAlign.center,
      ),
    ),
  ),
)

enter image description here

Upvotes: 99

Leonardo
Leonardo

Reputation: 2235

The Flutter API doc has an example of how to render a RaisedButton with gradient background - see here https://api.flutter.dev/flutter/material/RaisedButton-class.html

Widget gradientButton = Container(
  child: RaisedButton(
    onPressed: () { },
    textColor: Colors.white,
    padding: const EdgeInsets.all(0.0),
    child: Container(
      width: 300,
      decoration: new BoxDecoration(
        gradient: new LinearGradient(
          colors: [
            Color.fromARGB(255, 148, 231, 225),
            Color.fromARGB(255, 62, 182, 226)
          ],
        )
      ),
      padding: const EdgeInsets.all(10.0),
      child: Text(
        "Gradient Button",
        textAlign: TextAlign.center,
      ),
    ),
  ),
);

Gradient Button

Upvotes: 8

Fatima Hossny
Fatima Hossny

Reputation: 1297

You can use a more easier way by using RawMaterialButton from material.dart , you can make it's shape as rounded or circle too . here is a simple example of this .

  Card(
    elevation: 7,
    child: Container(
      width: 120.0,
      height: 75.0,
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.bottomLeft,
          end: Alignment.topRight,
          colors: <Color>[
            Colors.blue,
            Colors.red,
          ],
        ),
      ),
      child: RawMaterialButton(
        onPressed: () {},
        splashColor: Colors.grey,
        child: Text(
          "Button",
          style: TextStyle(color: Colors.white, fontSize: 20.0),
        ),
      ),
    ),
  ),

Upvotes: 1

GaboBrandX
GaboBrandX

Reputation: 2685

I know this question is a bit old.. But I've found myself with this requirement and I wanted to share my solution. It uses a Card and animates the elevation when the button is pressed.

import 'package:flutter/material.dart';

class GradientButton extends StatefulWidget {
  final String label;
  final VoidCallback onPressed;
  final Gradient gradient;
  final double elevation;
  final double height;
  final TextStyle labelStyle;

  GradientButton({
    @required this.label,
    @required this.onPressed,
    @required this.gradient,
    this.elevation,
    this.height,
    this.labelStyle,
  })  : assert(label != null && onPressed != null),
        assert(gradient != null);

  @override
  _GradientButtonState createState() => _GradientButtonState();
}

class _GradientButtonState extends State<GradientButton> with TickerProviderStateMixin {
  AnimationController _animationController;
  Animation _animation;

  elevateUp(TapDownDetails details) {
    _animationController.forward();
  }

  elevateDown() {
    _animationController.reverse();
  }

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(duration: Duration(milliseconds: 50), vsync: this);
    _animation = Tween(begin: widget.elevation ?? 2.0, end: 12.0).animate(_animationController);
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation,
      builder: (c, w) {
        return GestureDetector(
          onTapDown: elevateUp,
          onTapCancel: elevateDown,
          onTapUp: (value) {
            elevateDown();
            widget.onPressed();
          },
          child: Card(
            elevation: _animation.value,
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(25.0),
            ),
            child: Container(
              width: double.infinity,
              height: widget.height ?? 50.0,
              decoration: BoxDecoration(
                gradient: widget.gradient,
                borderRadius: BorderRadius.circular(25.0),
              ),
              child: Center(
                child: Text(
                  widget.label,
                  style: widget.labelStyle ?? Theme.of(context).textTheme.button,
                ),
              ),
            ),
          ),
        );
      },
    );
  }
}

There's room for improving it (maybe you don't want those rounded borders by default), but hope can be useful for some of you :D

Upvotes: 0

Krishna Lohiya
Krishna Lohiya

Reputation: 11

Refer Below -

RaisedButton(
     onPressed: () {},
     textColor: Colors.white,
     padding: const EdgeInsets.all(0.0),
     shape:RoundedRectangleBorder(borderRadius: BorderRadius.circular(80.0)),
     child: Container(
       decoration: const BoxDecoration(
         gradient: LinearGradient(
           colors: <Color>[
             Color(0xFF0D47A1),
             Color(0xFF1976D2),
             Color(0xFF42A5F5),
           ],
         ),
         borderRadius: BorderRadius.all(Radius.circular(80.0))
       ),
       padding: const EdgeInsets.fromLTRB(20, 10, 20, 10),
       child: const Text(
           'Gradient Button',
           style: TextStyle(fontSize: 20)
       ),
     ),
   )

Upvotes: 16

Muhammad Mustafa
Muhammad Mustafa

Reputation: 187

Simply make one more container as a child set the decoration of that container and make gradient color as you want

Then after this use RaisedButton as the child of the above container same as with MaterialButton also

   child: Container(
      decoration: BoxDecoration(
        gradient: LinearGradient(
            colors: [Colors.red, Colors.blue],
            begin: FractionalOffset(0.0, 0.0),
            end: FractionalOffset(0.5, 0.0),
            stops: [0.0, 1.0],
            tileMode: TileMode.clamp),
      ),
      child: RaisedButton(
        color: Colors.transparent,
        child: Text("Ask Permssion"),
        onPressed: () {
          askPermission();
        },
      )),

Output:

GradientRaisedButton

Upvotes: 5

Shyju M
Shyju M

Reputation: 9943

Gradient package is available at pub store which supports few predefined gradients

You can create the gradient button as

GradientButton(
                 child: Text('Gradient'),
                 callback: () {},
                 gradient: Gradients.backToFuture,
           ),

The package have GradientCard, GradientProgressIndicator, GradientButton, CircularGradientButton and the GradientText

Gradient Widgets

Upvotes: 9

Vamsi Krishna
Vamsi Krishna

Reputation: 31352

You can create a custom one yourself

class RaisedGradientButton extends StatelessWidget {
  final Widget child;
  final Gradient gradient;
  final double width;
  final double height;
  final Function onPressed;

  const RaisedGradientButton({
    Key key,
    @required this.child,
    this.gradient,
    this.width = double.infinity,
    this.height = 50.0,
    this.onPressed,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: width,
      height: 50.0,
      decoration: BoxDecoration(gradient: gradient, boxShadow: [
        BoxShadow(
          color: Colors.grey[500],
          offset: Offset(0.0, 1.5),
          blurRadius: 1.5,
        ),
      ]),
      child: Material(
        color: Colors.transparent,
        child: InkWell(
            onTap: onPressed,
            child: Center(
              child: child,
            )),
      ),
    );
  }
}

And use it anywhere as follows:

RaisedGradientButton(
  child: Text(
    'Button',
    style: TextStyle(color: Colors.white),
  ),
  gradient: LinearGradient(
    colors: <Color>[Colors.green, Colors.black],
  ),
  onPressed: (){
    print('button clicked');
  }
),

Custom button

You can further play around with the shadow and rounded borders by editing the Container's decoration property until it matches your spec.

Upvotes: 71

Related Questions