Table of Contents

What is Flutter Theming?

A theme is a generic styling element that represents the overall style, look, and feel of your application. When you wish to modify your Flutter app theme, say for eg. from Flutter Dark mode to Flutter Light mode or vice-versa, it is known as Flutter theming.

There are many articles available on Flutter theming, showing how to implement Flutter theme Dark Light in projects using the default App Theme and their default Colors attribute. But in the live project, we are required to add our custom Color for our app branding.

In this blog, we will be sharing how to create a flutter custom theme step by step

create a flutter custom theme

Step-by-Step Guide for Flutter Theming with Custom Color

To successfully implement Flutter theming, you will have to take care of the following prerequisites:

  • Create flutter project
  • Add riverpod package in yaml (Note: You can use any state management packages base on your convenient)

Once you are ready to go ahead, execute the following steps consecutively for enabling custom Flutter theming.

Want to Implement a custom option to change the theme dynamically in your app?
Hire Flutter Developers from Bacancy to add or customize theme Color for your app branding

Step 1: Create Light/Dark Theme

For creating a theme for light and dark mode, we use ThemeData class and customize colors and other properties based on needs. We have created one method for getting ThemeDate based on selected light/dark themes.

We give different values for scaffoldBackgroundColor, bodyColor, thumbColor, listTileTheme, and appBarTheme based on the selected theme.

Copy Text
ThemeData getAppTheme(BuildContext context, bool isDarkTheme) {
  return ThemeData(
    scaffoldBackgroundColor: isDarkTheme ? Colors.black : Colors.white,
    textTheme: Theme.of(context)
        .textTheme
        .copyWith(
          titleSmall:
              Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: 11),
        )
        .apply(
          bodyColor: isDarkTheme ? Colors.white : Colors.black,
          displayColor: Colors.grey,
        ),
    switchTheme: SwitchThemeData(
      thumbColor: MaterialStateProperty.all(
          isDarkTheme ? Colors.orange : Colors.purple),
    ),
    listTileTheme: ListTileThemeData(
        iconColor: isDarkTheme ? Colors.orange : Colors.purple),
    appBarTheme: AppBarTheme(
        backgroundColor: isDarkTheme ? Colors.black : Colors.white,
        iconTheme:
            IconThemeData(color: isDarkTheme ? Colors.white : Colors.black54)),
  );
}

Step 2: Create Provider for Theme State Using River-pod

We will use river-pod to manage the app theme state. We only want to store bool value to manage light or dark themes so we will use StateProvider.

Copy Text
final appThemeProvider = StateProvider<bool>((ref) => false);

Step 3: Use Theme in App

We use river-pod in the project so we have to wrap MyApp with ProviderScope to access all providers though-out app. MyApp extends ConsumerWidget so we can get the WidgetRef object in the build method and access any river-pod using the ref variable. getAppTheme(context, ref.watch(appThemeProvider)) method listens to any change in the app theme and updates the app accordingly.

Copy Text
class MyApp extends ConsumerWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return MaterialApp(
      title: 'Flutter Light/Dark Theme',
      debugShowCheckedModeBanner: false,
      theme: getAppTheme(context, ref.watch(appThemeProvider)),
      home: const MyHomePage(),
    );
  }
}

ref.read(appThemeProvider.notifier).state = value. We are updating the theme state in appThemeProvider when the switch state is changed from light/dark mood.

Copy Text
Switch(
  activeColor: Colors.orange,
  onChanged: (value) {
    ref.read(appThemeProvider.notifier).state = value;
  },
  value: isDarkMode )

Step 4: Add Custom Color

It works fine as far as it will show the same color in all icons and texts. If we want to use different colors on icons, we have to create an extension for the Theme. Create a class and extend with ThemeExtension and add necessary fields that you want to customize.

Copy Text
class AppColors extends ThemeExtension<AppColors> {
  final Color? color1;
  final Color? color2;
  final Color? color3;

  const AppColors({
    required this.color1,
    required this.color2,
    required this.color3,
  });

  @override
  AppColors copyWith({
    Color? color1,
    Color? color2,
    Color? color3,
  }) {
    return AppColors(
      color1: color1 ?? this.color1,
      color2: color2 ?? this.color2,
      color3: color3 ?? this.color3,
    );
  }

  @override
  AppColors lerp(ThemeExtension<AppColors>? other, double t) {
    if (other is! AppColors) {
      return this;
    }
    return AppColors(
      color1: Color.lerp(color1, other.color1, t),
      color2: Color.lerp(color2, other.color2, t),
      color3: Color.lerp(color3, other.color3, t),
    );
  }
}

Now add this extension attribute in ThemeData in our create method getAppTheme and define the theme according to colors.

Copy Text
extensions: <ThemeExtension<AppColors>>[
  AppColors(
    color1: isDarkTheme ? Colors.blue : Colors.blueGrey,
    color2: isDarkTheme ? Colors.pink : Colors.pinkAccent,
    color3: isDarkTheme ? Colors.yellow : Colors.limeAccent,
  ),

Create another extension function which we use to access custom color easily.

Copy Text
AppColors colors(context) => Theme.of(context).extension<AppColors>()!;

We can access these colors in the widget simply using colors(context).color1. If we do not specify the Icon color, it will fetch the color from listTileTheme.

Copy Text
ListTile(
    leading: Icon(Icons.chat_outlined, color: colors(context).color3),
    title: Text( "Help Center", style: Theme.of(context).textTheme.titleSmall),
 ),
ListTile(
    leading: const Icon(Icons.notifications),
    title: Text("Notification", style: Theme.of(context).textTheme.titleSmall),
),

Step 5: Full Source Code

Here is the code for theme class:

Copy Text
import 'package:flutter/material.dart';

AppColors colors(context) => Theme.of(context).extension<AppColors>()!;

ThemeData getAppTheme(BuildContext context, bool isDarkTheme) {
  return ThemeData(
    extensions: <ThemeExtension<AppColors>>[
      AppColors(
        color1: isDarkTheme ? Colors.blue : Colors.green,
        color2: isDarkTheme ? Colors.pink : Colors.blue,
        color3: isDarkTheme ? Colors.yellow : Colors.red,
      ),
    ],
    scaffoldBackgroundColor: isDarkTheme ? Colors.black : Colors.white,
    textTheme: Theme.of(context)
        .textTheme
        .copyWith(
          titleSmall:
              Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: 12),
        )
        .apply(
          bodyColor: isDarkTheme ? Colors.white : Colors.black,
          displayColor: Colors.grey,
        ),
    switchTheme: SwitchThemeData(
      thumbColor: MaterialStateProperty.all(
          isDarkTheme ? Colors.orange : Colors.purple),
    ),
    listTileTheme: ListTileThemeData(
        iconColor: isDarkTheme ? Colors.orange : Colors.purple),
    appBarTheme: AppBarTheme(
        backgroundColor: isDarkTheme ? Colors.black : Colors.white,
        iconTheme:
            IconThemeData(color: isDarkTheme ? Colors.white : Colors.black54)),
  );
}

@immutable
class AppColors extends ThemeExtension<AppColors> {
  final Color? color1;
  final Color? color2;
  final Color? color3;

  const AppColors({
    required this.color1,
    required this.color2,
    required this.color3,
  });

  @override
  AppColors copyWith({
    Color? color1,
    Color? color2,
    Color? color3,
  }) {
    return AppColors(
      color1: color1 ?? this.color1,
      color2: color2 ?? this.color2,
      color3: color3 ?? this.color3,
    );
  }

  @override
  AppColors lerp(ThemeExtension<AppColors>? other, double t) {
    if (other is! AppColors) {
      return this;
    }
    return AppColors(
      color1: Color.lerp(color1, other.color1, t),
      color2: Color.lerp(color2, other.color2, t),
      color3: Color.lerp(color3, other.color3, t),
    );
  }
}

Here is code of our main screen:

Copy Text
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:light_dark_mode/provider%20/app_theme_provider.dart';
import 'package:light_dark_mode/utils/app_theme.dart';

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return MaterialApp(
      title: 'Flutter Light/Dark Theme',
      debugShowCheckedModeBanner: false,
      theme: getAppTheme(context, ref.watch(appThemeProvider)),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends ConsumerWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    var isDarkMode = ref.watch(appThemeProvider);
    return Scaffold(
      appBar: AppBar(
        elevation: 0,
        leading: const Icon(Icons.arrow_back_ios_sharp),
        actions: const [
          Padding(
            padding: EdgeInsets.symmetric(horizontal: 15.0),
            child: Icon(Icons.add_circle_outline),
          )
        ],
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 8.0),
        child: ListView(
          children: [
            CircleAvatar(
              radius: 60,
              backgroundColor: Colors.grey,
              child: Padding(
                padding: const EdgeInsets.all(1), // Border radius
                child: ClipRRect(
                    borderRadius: BorderRadius.circular(60),
                    child: Image.asset(
                      "assets/ic_profile.jpeg",
                      fit: BoxFit.fill,
                      width: 120,
                      height: 120,
                    )),
              ),
            ),
            Container(
              margin: const EdgeInsets.only(top: 10, bottom: 60),
              alignment: Alignment.center,
              child: Text(
                "Testing User",
                style: Theme.of(context).textTheme.titleLarge,
              ),
            ),
            ListTile(
              leading: Icon(isDarkMode ? Icons.brightness_3 : Icons.sunny),
              title: Text(
                isDarkMode ? "Dark mode" : "Light mode",
                style: Theme.of(context).textTheme.titleSmall,
              ),
              trailing: Consumer(builder: (context, ref, child) {
                return Transform.scale(
                  scale: 0.7,
                  child: Switch(
                    activeColor: Colors.orange,
                    onChanged: (value) {
                      ref.read(appThemeProvider.notifier).state = value;
                    },
                    value: isDarkMode,
                  ),
                );
              }),
            ),
            ListTile(
              leading: Icon(Icons.grid_on_sharp, color: colors(context).color1,),
              title: Text(
                "Story",
                style: Theme.of(context).textTheme.titleSmall,
              ),
            ),
            ListTile(
              leading: Icon(Icons.settings, color: colors(context).color2),
              title: Text("Settings and Privacy",
                  style: Theme.of(context).textTheme.titleSmall),
            ),
            ListTile(
              leading: Icon(Icons.chat_outlined, color: colors(context).color3),
              title: Text(
                "Help Center",
                style: Theme.of(context).textTheme.titleSmall,
              ),
            ),
            ListTile(
              leading: const Icon(Icons.notifications),
              title: Text(
                "Notification",
                style: Theme.of(context).textTheme.titleSmall,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

You can find full code here: GitHub Repository.

Conclusion

I hope this blog will help to customize your Flutter app theme. In this blog we customized Colors in ThemeExtension; however, we can use any field to customize the value. Flutter theming is just one aspect, there are several other solutions that Flutter app development company like Bacancy can provide you. Contact us Now.

Need Help to Customize Theming in Your Flutter App?

Light to Dark or Dark to Light?

Book a Free 30 Min Call to Know How

Build Your Agile Team

Hire Skilled Developer From Us

[email protected]

Your Success Is Guaranteed !

We accelerate the release of digital product and guaranteed their success

We Use Slack, Jira & GitHub for Accurate Deployment and Effective Communication.

How Can We Help You?