How to customize theme dark and light in flutter?

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.

By incorporating light and dark themes with custom colors in your Flutter web application development, you can create visually appealing interfaces that adapt seamlessly to different lighting conditions. This guide will explore how to implement light and dark themes in Flutter and demonstrate how to customize the color palette to match your app’s branding or design requirements. Let’s dive into the world of Flutter theming and discover how to bring a vibrant and dynamic look to your app quickly.

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.

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.

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.

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.

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 mode.

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.

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.

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.

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.

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:

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:

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.

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More