DEV Community

Cover image for Flutter Themes: Dynamic Color & ThemeExtension
Niraj Prakash
Niraj Prakash

Posted on

Flutter Themes: Dynamic Color & ThemeExtension

Greetings, fellow developers! Step into the captivating realm of Flutter 3.10, where theming takes the center stage in creating immersive and user-friendly apps. In this blog, we'll embark on an exciting journey to implement dynamic theme customization. Embrace the powerful capabilities of ThemeExtension as we gain unparalleled control over colors.

Let's dive in and integrate dynamic theming with custom colors extension into Flutter App! Example Github Repo you can find here


Default Theme

By default, Flutter provides a light and dark theme. These themes come with predefined color schemes for elements like the app bar, background, text, buttons, and more. Before we explore dynamic theming, let's take a brief look at the default color palette for both light and dark modes.

Step 1: Create theme for the app.


// Theme 
class AppTheme {

static ThemeData themeData(
      {Color seedColor = Colors.green, bool isDark = false}) {
    ColorScheme colorScheme = isDark
        ? ColorScheme.fromSeed(seedColor: seedColor).copyWith(brightness: Brightness.dark)
        : ColorScheme.fromSeed(seedColor: seedColor).copyWith( brightness: Brightness.light);

    return ThemeData(colorScheme: colorScheme);
  }




}

Enter fullscreen mode Exit fullscreen mode

Step 2: In your main.dart file, use your light and dark themes.



 // ..


class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "'Flutter Theme Example',"
      theme: AppTheme.themeData(isDark: false),
      darkTheme: AppTheme.themeData(isDark: true),
      home: MyHomePage(),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

Making it Dynamic

In this section, we'll explore how to dynamically change the theme color in your Flutter app. With this users can personalize their app's appearance. To achieve this, we'll leverage Flutter's ChangeNotifier to update the theme throughout the app.

Step 1. We'll begin by creating a AppThemeProvider class that extends the ChangeNotifier class which notify listeners whenever the theme changes.


class AppThemeProvider extends ChangeNotifier {
  late Color _seedColor;
  Color get seedColor => _seedColor;

  set seedColor(value) {
    _seedColor = value;
    notifyListeners();
  }

  AppThemeProvider({required Color defaultColor}) {
    _seedColor = Color.lerp(defaultColor, Colors.black, 0.1) ?? defaultColor;
  }

  void updateTheme(Color currentColor) {
    seedColor = currentColor;
  }

}

Enter fullscreen mode Exit fullscreen mode

Step 2. Now change you main.dart file add ChangeNotifierProvider.


void main() {
  runApp(
    ChangeNotifierProvider<AppThemeProvider>(
      create: (context) => AppThemeProvider(),
      child: MyApp(),
    ),
  );
}

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {

    final appThemeProvider = Provider.of<AppThemeProvider>(context);
    return MaterialApp(
      title: 'Flutter Theme Example',
      theme: AppTheme.themeData(seedColor: appThemeProvider.seedColor),
      darkTheme: AppTheme.themeData(seedColor: appThemeProvider.seedColor, isDark: true),
      home: MyHomePage(),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

Step 3: In your any page.dart file, use the Consumer widget to update the theme.


class MyHomePage extends StatelessWidget {

  // ..

  // call this for color change
  void _toggleTheme(BuildContext context, Color seedColor) {
    context.read<AppThemeProvider>().updateTheme(currentColor);
  }
}

Enter fullscreen mode Exit fullscreen mode

For picking the color you can use flutter_colorpicker.


Custom Colors using ThemeExtension

Suppose you want more control over your app's color palette. For example different grey colors for text and background.
Flutter provide ThemeExtensions, a powerful way to add custom colors to your app's theme. With ThemeExtensions, you can define colors like "surface," "onSurface," "primaryHigh," and many more. These custom colors give you granular control over the appearance of various UI elements, resulting in a truly personalized and cohesive app design. Let's do this:

Step 1: Create a new Dart file called app_theme_colors.dart.



@immutable
class AppThemeColors extends ThemeExtension<AppThemeColors> {
  final Color onSurfaceHigh;

  const AppThemeColors({
    required this.onSurfaceHigh,
  });

  @override
  ThemeExtension<AppThemeColors> copyWith({
    Color? onSurfaceHigh,

  }) {
    return AppThemeColors(
      onSurfaceHigh: onSurfaceHigh ?? this.onSurfaceHigh,

    );
  }

  @override
  ThemeExtension<AppThemeColors> lerp(
      ThemeExtension<AppThemeColors>? other, double t) {
    if (other is! AppThemeColors) {
      return this;
    }
    return AppThemeColors(
      onSurfaceHigh: Color.lerp(onSurfaceHigh, other.onSurfaceHigh, t) ??
          other.onSurfaceHigh,
      // isDark: true,
    );
  }

  @override
  String toString() {
    return 'AppThemeColors(seed: $onSurfaceHigh)';
  }

  factory AppThemeColors.seedColor({
    Color seedColor = Colors.red,
    bool isDark = false,
  }) {
    print("isDark: $isDark $seedColor");

    var surfaceBasedColor = isDark
        ? Color.alphaBlend(Colors.white.withOpacity(0.1), Colors.black)
        : Colors.white;

    var onSurfaceBasedColor = isDark ? Colors.white : Colors.black;

    var onSurfaceHigh = Color.alphaBlend(
        onSurfaceBasedColor.withOpacity(0.7), surfaceBasedColor);

    return AppThemeColors(onSurfaceHigh: onSurfaceHigh);
  }
}

Enter fullscreen mode Exit fullscreen mode

Step 2: Update your main.dart to use the theme extension.


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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {

    final appThemeProvider = Provider.of<AppThemeProvider>(context);
    return MaterialApp(
      title: 'Flutter Theme Example',
      theme: AppTheme.themeData(seedColor: appThemeProvider.seedColor).copyWith(
          extensions: <ThemeExtension<dynamic>>[
            AppThemeColors.create(
                 isDark: false)
          ]),
      darkTheme: AppTheme.themeData(seedColor: appThemeProvider.seedColor, isDark: true).copyWith(
          extensions: <ThemeExtension<dynamic>>[
            AppThemeColors.create(
                 isDark: true)
          ]),
      home: MyHomePage(),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

Step 3: Update your Page to use the theme extension



class MyPage extends StatelessWidget {
  late AppThemeColors themeColors;

  @override
  Widget build(BuildContext context) {
    themeColors = Theme.of(context).extension<AppThemeColors>() ??
        AppThemeColors.create(); // if null
    // ..

    return Scaffold(
      body: Container(
        child: Text(
          "By continuing, you agree to our",
          style: TextStyle(color: themeColors.onSurfaceHigh),
        ),
      ),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

By setting up the app_theme_colors.dart, you can now easily customize the light and dark themes in one place, making your code cleaner and more maintainable.


Conclusion:

Congratulations! You've done the setup of dynamic theme and ThemeExtensions.

Don't forget to try out our example app
Image description

Unleash the full potential of Flutter themes and ThemeExtensions in your future projects.

We have implemented this in one of our app Hundi: Record Book.

Hundi: Record Book - Apps on Google Play

Periodic Hundi group management. Log transactions, and ensure transparency.

favicon play.google.com

Top comments (0)