This tutorial shows you how to use the FilledButton
widget in Flutter, including how to set custom style for the buttons.
Filled buttons are intended for buttons that should have the most visual impact after the FloatingActionButton
. Therefore, it should be used for actions that complete a flow such as 'Submit', 'Save', or 'Confirm'. A filled button usually contains a label text. It can also have an icon. In addition, there is also a variant called filled tonal buttons. A filled tonal button is suitable for a button which needs more emphasis than an OutlinedButton
, but less emphasis than a FilledButton
. For example, it's suitable for the 'Next' button in an onboarding flow.
Flutter already provides a widget called FilledButton
which makes it easier to create a filled button. Below are the usage examples which include how to customize the appearance of the buttons.
Using FilledButton
There are some constructors that you can use including factory
and const
constructors. Let's start with the main constructor.
const FilledButton({
Key? key,
required VoidCallback? onPressed,
VoidCallback? onLongPress,
ValueChanged<bool>? onHover,
ValueChanged<bool>? onFocusChange,
ButtonStyle? style,
FocusNode? focusNode,
bool autofocus = false,
Clip clipBehavior = Clip.none,
MaterialStatesController? statesController,
required Widget? child,
})
As you can see, there are some named arguments. First, you have to pass a Widget
as the child
argument which will be set as the content of the button. The other required argument is onPressed
, for which you have to pass a callback function to handle the events when the user presses the button. It's allowed to pass a null value as the onPressed
argument which causes the button to be disabled.
Basic Usage
Below is the basic usage that only passes the required arguments.
FilledButton(
onPressed: () {
print('pressed');
},
child: const Text('Woolha.com'),
)
Output:
Handle Long Press
In some cases, you may need to handle when the user performs a long press. It can be done by passing a function as the onLongPress
argument.
FilledButton(
onLongPress: () {
print('long pressed');
},
// other arguments
)
Handle On Hover
You can also detect when a pointer enters or exits the button by passing a function as the onHover
argument. The passed function must have a parameter whose type is bool
. When the pointer enters the button area, it will be called with true
as the argument value. The argument value will be false
when the pointer exits the button area. It doesn't work on mobile platforms, but it works on the web platform.
FilledButton(
onHover: (bool isOnHover) {
print('on hover: $isOnHover');
},
// other arguments
)
Set Button Style
Using the above constructor, Flutter will create a standard filled button (non-tonal). If you do not define any style, the default style depends on the values from the ThemeData
. For non-tonal buttons, it will use the primary
color for the background and the onPrimary
color for the foreground. For text style, it will be obtained from the textTheme.labelLarge
. You can read the logic from the defaultStyleOf
method of FilledButton
class.
Set General ThemeData
There are several ways to change the style. As I have written above, the default style is obtained from the ThemeData
. Therefore, modifying the ThemeData
also affects the style of filled buttons. Keep in mind that if you customize the ThemeData
, it may also affect other widgets, not only filled buttons. In other words, it changes the global style of the Flutter application. For non-tonal buttons, the related ThemeData
properties are:
textTheme.labelLarge
: text stylecolorScheme.primary
: background colorcolorScheme.onPrimary
: color for foreground and overlaycolorScheme.onSurface
: color when the button is disabled.colorScheme.shadow
: shadow color
To do it, you have to pass a custom ThemeData
as the theme
argument. This should only be done if you want the customization to be applied to all kinds of widgets. For example, if you want to change the primary color used in your Flutter application.
MaterialApp(
theme: ThemeData.light().copyWith(
textTheme: const TextTheme(
labelLarge: TextStyle(
fontStyle: FontStyle.italic,
),
),
colorScheme: ThemeData.light().colorScheme.copyWith(
primary: Colors.teal,
onPrimary: Colors.white,
// secondaryContainer: const Color.fromARGB(255, 217, 231, 203), // for tonal
// onSecondaryContainer: const Color.fromARGB(255, 80, 80, 80), // for tonal
),
),
// other arguments
);
Output:
Set FilledButtonThemeData
Another alternative is to set a global style for all filled buttons. When creating or copying a ThemeData
, you can pass a ButtonStyle
as the filledButtonTheme
argument. The ButtonStyle
can be constructed using FilledButton.styleFrom
static method. With this way, the styles of other kinds of widgets are not affected. In addition, it allows more customization since you can also change the shape, size, and alignment in a more convenient way. The style defined as FilledButtonThemeData
will override the general ThemeData
style.
MaterialApp(
filledButtonTheme: FilledButtonThemeData(
style: FilledButton.styleFrom(
// ButtonStyle arguments
),
),
// other arguments
);
The ButtonStyle
itself has a lot of properties to define a custom style. Common customizations such as how to change the color, text, and shape are going to be explained in the next section which explains how to change the individual button style, since it also uses the ButtonStyle
class.
Set Individual ButtonStyle
If you want to apply a custom ButtonStyle
to particular buttons, you can pass a ButtonStyle
as the style
argument to the constructor of FilledButton
. You can utilize the FilledButton.styleFrom
static method to build the . The style set on a specific button will override the style from FilledButtonThemeData
or ThemeData
.
FilledButton(
style: FilledButton.styleFrom(
// ButtonStyle arguments
),
// other arguments
)
Below are common customizations that you might want to apply
Set Colors
To set the background color, you pass a Color
value as the backgroundColor
argument of the ButtonStyle
. For the color of the text and the icon, you can set it using the foregroundColor
argument. When the button is disabled, you can set different background and foreground colors using disabledBackgroundColor
and disabledForegroundColor
arguments respectively.
FilledButton(
style: FilledButton.styleFrom(
backgroundColor: Colors.teal,
foregroundColor: Colors.yellow,
disabledBackgroundColor: Colors.grey,
disabledForegroundColor: Colors.black,
// other arguments
)
Output when enabled:
Output when disabled:
Set Text Style
To change the text style, you can pass a TextStyle
instance as the textStyle
argument.
FilledButton(
style: FilledButton.styleFrom(
textStyle: const TextStyle(
fontStyle: FontStyle.italic,
),
),
// other arguments
)
Output:
Set Shape
By default, Flutter uses StadiumBorder
for the shape, you can change it by passing an OutlinedBorder
instance as the shape
argument.
FilledButton(
style: FilledButton.styleFrom(
shape: const ContinuousRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20))
),
),
// other arguments
)
Output:
Below is the list of named parameters you can pass to FilledButton.styleFrom
static method.
Color? foregroundColor
: Foreground color when enabled.Color? backgroundColor
: Background color when enabled.Color? disabledForegroundColor
: Foreground color when disabled.Color? disabledBackgroundColor
: Background color when disabled.Color? shadowColor
: The shadow color of the button.Color? surfaceTintColor
: The color of the surface tint.double? elevation
: The elevation of the button.TextStyle? textStyle
: The style forText
widget.EdgeInsetsGeometry? padding
: The padding between the button's boundary and its child.Size? minimumSize
: The minimum size of the button.Size? fixedSize
: The size of the button.Size? maximumSize
: The maximum size of the button.BorderSide? side
: The outline of the button.OutlinedBorder? shape
: The shape of the button.MouseCursor? enabledMouseCursor
: The mouse cursor when enabled.MouseCursor? disabledMouseCursor
: The mouse cursor when disabled.VisualDensity? visualDensity
: How compact is the button's layout.MaterialTapTargetSize? tapTargetSize
: yyy.Duration? animationDuration
: The minimum size of the area within which the button can be pressed.bool? enableFeedback
: Whether to enable acoustic and/or haptic feedback.AlignmentGeometry? alignment
: The alignment of thechild
.InteractiveInkFeatureFactory? splashFactory
:InkWell
splash factory for showing ink splashes when tapped.
Using FilledButton.icon
It's also possible to create a filled button by passing the icon
and label
as separate arguments by using the following constructor.
factory FilledButton.icon({
Key? key,
required VoidCallback? onPressed,
VoidCallback? onLongPress,
ValueChanged<bool>? onHover,
ValueChanged<bool>? onFocusChange,
ButtonStyle? style,
FocusNode? focusNode,
bool? autofocus,
Clip? clipBehavior,
MaterialStatesController? statesController,
required Widget icon,
required Widget label,
})
FilledButton.icon(
onPressed: () {
print('pressed');
},
icon: const Icon(Icons.web),
label: const Text('Woolha.com'),
)
Output:
Using FilledButton.tonal
There is a named const
constructor for creating filled tonal buttons. The list of arguments are the same as the main constructor. The difference is the default style. It uses colorScheme.secondaryContainer
and colorScheme.onSecondaryContainer
as the default background and foreground/overlay color respectively. However, if you define the colors with a ButtonStyle
using either FilledButtonThemeData
or set it directly to the button, the result can be the same as using the main constructor. In other words, the only difference between tonal and non-tonal is the default colors.
const FilledButton.tonal({
Key? key,
required VoidCallback? onPressed,
VoidCallback? onLongPress,
ValueChanged<bool>? onHover,
ValueChanged<bool>? onFocusChange,
ButtonStyle? style,
FocusNode? focusNode,
bool autofocus = false,
Clip clipBehavior = Clip.none,
MaterialStatesController? statesController,
required Widget? child,
})
FilledButton.tonal(
onPressed: () {
print('pressed');
},
child: const Text('Woolha.com'),
)
Output:
Using FilledButton.tonalIcon
If you prefer to create a tonal button by passing the icon
and label
widgets, you can use the constructor below.
factory FilledButton.tonalIcon({
Key? key,
required VoidCallback? onPressed,
VoidCallback? onLongPress,
ValueChanged<bool>? onHover,
ValueChanged<bool>? onFocusChange,
ButtonStyle? style,
FocusNode? focusNode,
bool? autofocus,
Clip? clipBehavior,
MaterialStatesController? statesController,
required Widget icon,
required Widget label,
})
FilledButton.tonalIcon(
onPressed: () {
print('pressed');
},
icon: const Icon(Icons.web),
label: const Text('Woolha.com'),
)
Output:
FilledButton
Parameters
Below is the list of parameters of the FilledButton
or FilledButton.tonal
constructor.
Key? key
: The widget's key, used to control how a widget is replaced with another.required VoidCallback? onPressed
: Function to be called when the button is pressed.VoidCallback? onLongPress
: Function to be called when the button is long-pressed.ValueChanged<bool>? onHover
: Function to be called when a pointer enters or exits the button area.ValueChanged<bool>? onFocusChange
: Function to be called when the focus changes.ButtonStyle? style
: Style to be applied to the button.FocusNode? focusNode
: Focus node for the widget.bool autofocus
: Whether the widget will be selected as the initial focus when there is no other focused node in the scope.MaterialStatesController? statesController
: Represents theMaterialState
of the widget.Clip clipBehavior
: How to clip the widget. Defaults toClip.none
.Widget? child
: The button content.
Below is the list of parameters of the FilledButton.icon
or FilledButton.tonalIcon
constructor.
Key? key
: The widget's key, used to control how a widget is replaced with another.required VoidCallback? onPressed
: Function to be called when the button is pressed.VoidCallback? onLongPress
: Function to be called when the button is long-pressed.ValueChanged<bool>? onHover
: Function to be called when a pointer enters or exits the button area.ValueChanged<bool>? onFocusChange
: Function to be called when the focus changes.ButtonStyle? style
: Style to be applied to the button.FocusNode? focusNode
: Focus node for the widget.bool autofocus
: Whether the widget will be selected as the initial focus when there is no other focused node in the scope.MaterialStatesController? statesController
: Represents theMaterialState
of the widget.Clip clipBehavior
: How to clip the widget. Defaults toClip.none
.required Widget icon
: Widget to be set as the icon.required Widget label
: Widget to be set as the label.
Summary
In Flutter, you can use the FilledButton
widget for creating filled buttons. The constructors have some arguments you can use for customizations. There is also a variant called filled tonal button which is suitable for buttons whose priority is higher than an OutlinedButton
but less than a FilledButton
.
You can also read about:
ElevatedButton
, a button whose elevation increases when being pressed by the user.OutlinedButton
, a button with an outlined border.TextButton
, a standard button without outlined border and elevation change.