Flutter Themes: Dynamic Color & ThemeExtension
Tags: flutter, dart, themes, android
Published: 2023-07-30 08:50:06

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](https://api.flutter.dev/flutter/material/ThemeExtension-class.html) 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](https://github.com/JunkieLabs/hundi-app-parts) 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.

```dart

// 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);
  }




}

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

```dart


 // ..


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(),
    );
  }
}

```

---


### 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](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html) 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.

```dart

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;
  }

}

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

```dart

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(),
    );
  }
}

```

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

```dart

class MyHomePage extends StatelessWidget {

  // ..

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

```

For picking the color you can use [flutter_colorpicker](https://pub.dev/packages/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.

```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);
  }
}

```

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

```dart

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(),
    );
  }
}

```
Step 3: Update your Page to use the theme extension

```dart


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),
        ),
      ),
    );
  }
}

```


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](https://github.com/JunkieLabs/hundi-app-parts) 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2swrzjqf5hvghgm38h07.png)


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. 
{% embed https://play.google.com/store/apps/details?id=com.hundi.social %}