Exploring Flutter ThemeData: A Guide to Customizing Your App’s Appearance

Azhar Ali
9 min readJan 23, 2024

--

Flutter is a popular open-source framework used for developing high-quality, cross-platform mobile applications for both Android and iOS. Flutter provides many pre-built components to make the development process more accessible, such as theme widgets. In this article, we will discuss the Flutter theme and its different components, including AppBarTheme, InputDecoration, Button, OutlineButton, ElevatedButton, and TextButton.

On occasion, we may require distinct UI designs for the app and develop bespoke widgets instead of utilizing the default components provided by Flutter. For instance, you may be working on a unique Appbar style that is rounded or a TextFormField with a border and distinct style, and buttons with different heights and colors.

We should not ignore the Flutter Theme for such problems, it provides the ability to override or define custom styles in the theme and which will result in styling widgets automatically.

Table of content

AppBarTheme

TextFormField

Button

What is a Flutter Theme?

In Flutter, a theme is a set of pre-defined colors, fonts, and other design properties that can be applied to an entire app or specific widgets within the app. The main benefit of using themes is that they provide a consistent and visually appealing appearance throughout the app.

A theme can be defined by creating a ThemeData object and setting its properties according to your app’s design requirements. A typical theme configuration may include primary and secondary colors, text, and button styles.

AppBarTheme

The AppBar is an essential part of a Flutter app, and it is used to display the app’s name, navigation buttons, and other relevant information. The AppBarTheme is used to configure the appearance of the app bar.

The AppBarTheme widget is defined within the ThemeData object and is used to set properties like the background color, text color, icon theme, and elevation.

Default Appbar (Light)

Default Appbar

Default Appbar (Dark)

Appbar styling using the theme:

Custom Color: By using AppBarTheme, it is possible to add custom colors to the appbar theme data. This custom color will be consistent throughout the app and can be easily implemented by utilizing the simple appbar widget within your Scaffold.

Title Alignment: Sometimes, we may want to center the app title consistently throughout the app. Although we can accomplish this by adding centerTitle: true in every app bar widget, we can also achieve the same result by incorporating this property in the App theme.

Elevation and shadow color: It is possible to add a custom elevation and shadow color to the app’s appbar. In cases where we do not want to display the appbar elevation, we can achieve this by utilizing two properties of the AppBarTheme.

Title Style: The AppBarTheme has a property called titleTextStyle that allows you to customize the title style. You can incorporate all sorts of properties that are utilized in TextStyle to achieve this

Statusbar: The AppBarTheme provides the ability to customize the status bar color using systemOverlayStyle. You can alter the status bar background by adjusting, while statusBarIconBrightness enables you to modify the color of notification icons, clock, and network icons.

ScrolledUnderElevation: We can show elevation just in case of scrolling by adding scrolledUnderElevation.

ActionsIconTheme: actionsIconTheme can be used for custom theming of the action buttons.

ThemeData(
appBarTheme: const AppBarTheme(
shadowColor: Colors.transparent,
elevation: 0.0,
centerTitle: true,
color: Colors.grey,
titleTextStyle: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.normal,
),
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
statusBarBrightness: Brightness.dark,
),
)
)
const AppBarTheme(
backgroundColor: Colors.green,
shadowColor: Colors.transparent,
elevation: 0.0,
centerTitle: false,
scrolledUnderElevation: 10.0,
toolbarHeight: 72.0,
titleTextStyle: TextStyle(color: Colors.white, fontSize: 20.0, fontWeight: FontWeight.normal),
// round corners
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)),
),
// icons button theme
actionsIconTheme: IconThemeData(color: Colors.white, size: 16.0),
)

TextFormField

Default UI design for TextFormField

Occasionally, we would implement a customized user interface and style for TextFormFields our application rather than using the default design shown above.

As demonstrated in Google’s Create Account UI design for TextFormFields, we adhere to the same UI design, and to avoid defining the style for each TextFormField, we find Flutter Theme data to be extremely useful.

Google uses the same user interface (UI) design for the whole Login and Create Account process.
In certain situations, it may be desirable to construct a unique widget with the appropriate styling, but we can also use the Flutter theme with a simple TextFormField widget to mirror the UI’s look.

Google Like theme for TextFormField

ThemeData

ThemeData get googleFormTheme => ThemeDatad(
inputDecorationTheme: InputDecorationTheme(
floatingLabelBehavior: FloatingLabelBehavior.auto,
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey.withOpacity(0.4), width: 1.0),
borderRadius: const BorderRadius.all(Radius.circular(4.0)),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey.withOpacity(0.4), width: 1.0),
borderRadius: const BorderRadius.all(Radius.circular(4.0)),
),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Color(0xff1872E9), width: 2.0),
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
errorBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.red, width: 2.0),
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
focusedErrorBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.red, width: 2.0),
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
disabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Color(0xff1872E9), width: 1.0),
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
),
);

How we’ll use it in the Widget tree

// Theme is a widget if you want to apply specific theme to specific area
// then it would be helpful
Theme(
data: AppTheme.googleFormTheme,
child: Column(
children: [
TextFormField(
decoration: const InputDecoration(
labelText: 'First name',
),
),
const SizedBox(height: 18.0),
TextFormField(
decoration: const InputDecoration(
labelText: 'Surname (optional)',
),
),
],
),
),

TextFormField with the background.

 static ThemeData get lightGreyFormTheme => ThemeData(
inputDecorationTheme: InputDecorationTheme(
fillColor: Colors.grey.withOpacity(0.1),
filled: true,
hintStyle: TextStyle(color: Colors.grey.withOpacity(0.4)),
border: const OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
errorBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
focusedErrorBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
disabledBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
),
);

There are a lot of properties in InputDecorationTheme that can be used to modify the default properties of the widget.

TextStyle: You can define test style for all text(s) that are used in a field like hintStyle, errorStyle, labelStyle, floatingLabelStyle these all are the properties of test style that can help you to customize the style of the text.

Color: InputDecorationTheme provides you the ability to define colors for, background (FilledColors, filled), prefixIconColor, suffixIconColor, focusColor.

Border: one of the major properties of the InputDecorationTheme is the border. InputDecorationTheme provides a way to add a border for all status of the TextFormField which help you build beautiful UI design. You can add borders for focusedBorder, focusedErrorBorder, disabledBorder, enabledBorder, errorBorder you can separate colors for them as well.

Button

We’ve mainly two types of buttons in Flutter and we used to think about its customization and want to change its height, width, color, and text style according to our app UI design.

Default button style:

By default, Flutter buttons wrap around their text content, which can cause them to stretch and appear relatively small in height. This default design may not always fit well with certain design layouts.

ElevatedButton and OutlinedButton

In Flutter, buttons are typically blue by default and may not match the desired background color or have the appropriate dimensions for a particular design. One workaround is to wrap the button with a Container or SizedBox widget to adjust its height and width, or to set the button properties manually. However, this can be simplified by using Flutter’s theme data to define the button’s color, height, width, and other attributes to match the desired design.

Elevation: elevation: This property specifies the shadow elevation of the button, i.e., the distance between the button and the surface it appears to be raised from. The value of this property must be double.

ShadowColor: shadowColor: This property sets the color of the button's shadow. The value of this property must be an Color object.

MinimumSize: The minimumSize property sets the minimum width and height of the button using Size. This property is useful when we want to increase the height or width of the button beyond its default size, which may be too small for some designs. By setting the default height of the button with this property, we can ensure that all buttons in the app will have the same height.

BackgroundColor: backgroundColor: This property sets the background color of the button when it is enabled. The value of this property must be an Color object.

DisabledBackgroundColor: disabledBackgroundColor: This property sets the background color of the button when it is disabled. The value of this property must beColor.

DisabledForegroundColor: disabledForegroundColor: This property sets the text color of the button when it is disabled. The value of this property must be Color .

Padding: padding: This property sets the padding around the button's text content. The value of this property must be an EdgeInsets object.

TextStyle: textStyle: This property sets the text style of the button's text content. The value of this property must beTextStyle.

Shape: shape: This property sets the shape of the button, which is defined as a rounded rectangle with a specified radius. The value of this property must be RoundedRectangleBorder with a specified BorderRadius.

Side: you can define color and stroke with for Outline Button.

Theme data for buttons

static ThemeData get buttonStyleTheme => ThemeData(
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
elevation: 8.0,
shadowColor: Colors.green,
backgroundColor: Colors.green,
disabledBackgroundColor: Colors.green.withOpacity(0.4),
disabledForegroundColor: Colors.grey,
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 16.0),
textStyle: const TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.green, width: 1.5),
disabledForegroundColor: Colors.grey,
foregroundColor: Colors.green,
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 16.0),
textStyle: const TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
),
),
);

Widget tree:

Theme(
data: AppTheme.buttonStyleTheme,
child: Column(
children: [
// const SizedBox(height: 120),
ElevatedButton(
onPressed: () {},
child: const Text('Elevated button'),
),
const SizedBox(height: 8.0),
const ElevatedButton(
onPressed: null,
child: Text('Diabled Elevated button'),
),
const SizedBox(height: 20),
Center(
child: OutlinedButton(
onPressed: () {},
child: const Text('Outlined Button'),
),
),
const SizedBox(height: 8.0),
const Center(
child: OutlinedButton(
onPressed: null,
child: Text('Diabled Outlined Button'),
),
),
],
),
),

If we want all buttons in our app UI to have the same full width and a specific height, we can use button theme data to achieve this. This will ensure that every button in the app will have the same width and the specified height.

In some cases, we want to add full-size buttons in accordance with the Figma design and want to specify a minimum button size as well to prevent any text overflows and in accordance with the app design.

Theme data for the above design:

static ThemeData get customHeightWithButton => ThemeData(
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
elevation: 8.0,
shadowColor: Colors.green,
// define min size of the button in theme
minimumSize: const Size(double.infinity, 56.0),
backgroundColor: Colors.green,
disabledBackgroundColor: Colors.green.withOpacity(0.4),
disabledForegroundColor: Colors.grey,
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 16.0),
textStyle: const TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.green, width: 1.5),
minimumSize: const Size(double.infinity, 56.0),
disabledForegroundColor: Colors.grey,
foregroundColor: Colors.green,
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 16.0),
textStyle: const TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
),
),
);

How we will use it in the widget tree.

Theme(
data: AppTheme.customHeightWithButton,
child: Column(
children: [
ElevatedButton(
onPressed: () {},
child: const Text('Elevated button'),
),
const SizedBox(height: 8.0),
const ElevatedButton(
onPressed: null,
child: Text('Disabled Elevated button'),
),
const SizedBox(height: 20),
Center(
child: OutlinedButton(
onPressed: () {},
child: const Text('Outlined Button'),
),
),
const SizedBox(height: 8.0),
const Center(
child: OutlinedButton(
onPressed: null,
child: Text('Disabled Outlined Button'),
),
),
],
),
);

Thank you for taking the time to read this article. If you have any feedback or corrections, please let me know in the comments below. I am always looking for ways to improve my writing and provide better content for my readers.

If you found this article helpful, please show your support by giving it a round of claps. Your feedback and support motivate me to continue creating valuable content.

--

--