Dark Mode

Setting Up Light/Dark Mode and Fonts in a Flutter App

Setting Up Light/Dark Mode

The theme mode will be divided into three options:

  • Light Mode

  • Dark Mode

  • System Mode (which follows the system settings)

However, the actual UI will only display two primary modes: Light and Dark.

extension MyThemeModeContext on BuildContext {
  bool get isDarkMode => switch (myThemeMode) {
    ThemeMode.system => MediaQuery.of(this).platformBrightness == Brightness.dark,
    ThemeMode.light => false,
    ThemeMode.dark => true,
  };
}

Creating a Global Variable to Manage Themes

We will define a global variable to manage the application's theme:

ThemeMode myThemeMode = ThemeMode.system;

Handling Theme Persistence

A common issue with this approach is that the theme setting will not persist after the app is completely closed. There are several solutions to this problem:

  1. Using shared_preferences – A package that allows saving and retrieving values when initializing the app. Learn more here.

  2. Storing the Theme in a BLoC State – This ensures that the theme is managed within the state management logic.

Example of storing the theme in a Bloc:

myThemeMode = context.select((SettingBloc bloc) => bloc.state.settingWeb.theme);

For more details on how to persist values using BLoC, check out my article on the topic here.

Bloc

Applying Light and Dark Themes in the UI

Defining Theme-Specific Colors

Each theme (light and dark) has its own set of colors. We can determine which color to use based on the theme mode.

extension WebColorContext on BuildContext {
  Color get white => isDarkMode ? WebColor.blackForDark : WebColor.white;
}

How to Use

context.white

Handling context Accessibility

One potential issue with this approach is that context is required to access theme-specific colors, which may not always be convenient.

I have a simple way to access context from anywhere in the project. Check out my article for more details:

Context Anywhere

Switching Between Light and Dark Themes

To change the theme mode, use the following code:

myThemeMode = ThemeMode.light;
await WidgetsFlutterBinding.ensureInitialized().performReassemble();

The second line forces the entire application to redraw the screen, ensuring the theme change is applied immediately.

Light and Dark Themes with Custom Fonts

Fonts play a crucial role in UI design. Currently, I use two methods to add custom fonts to my Flutter applications.

Method 1: Adding Fonts from Asset Files

Define a TextStyle using a custom font stored in the assets folder:

static const _neoTextStyle = TextStyle(
  fontFamily: WebFontFamily.nanumSquareNeo,
  fontFamilyFallback: [
    WebFontFamily.pretendard,
  ],
  package: 'dsg_ui_core', // Loading the font from a package
  height: 1.5,
  leadingDistribution: TextLeadingDistribution.even,
);

You can include fonts in your pubspec.yaml and reference them in the TextStyle. If using fonts from a package, ensure you specify the package argument correctly.

Method 2: Using Google Fonts

If your desired font is available on Google Fonts, you can use it directly with the google_fonts package:

static final _webTextStyle = GoogleFonts.inter().copyWith(
  fontWeight: FontWeight.w600,
  height: 1.5,
  leadingDistribution: TextLeadingDistribution.even,
);

Handling Font Scaling for Different Screen Sizes

When the screen size changes, fonts should scale proportionally to maintain design consistency. One way to achieve this is by defining a ratio based on the screen width:

double get webRatio {
  final width = MediaQuery.of(this).size.width;
  final ratio = width / 1440;
  return ratio < 1 ? (ratio < 0.75 ? 0.75 : ratio) : 1;
}

Applying Scaled Font Styles from Figma Design

Once we have the ratio, we can create text styles dynamically:

TextStyle get h46B => _webTextStyle.copyWith(
  fontSize: 46 * webRatio,
  fontWeight: FontWeight.w700,
  color: neutral80,
);

This approach ensures your fonts scale properly across different devices, keeping your UI responsive and visually consistent.

Method 2: Using the google_fonts Library

You can use the google_fonts package if your desired font is available on Google Fonts.

static final _webTextStyle = GoogleFonts.inter().copyWith(
  fontWeight: FontWeight.w600,
  height: 1.5,
  leadingDistribution: TextLeadingDistribution.even,
);

Handling Font Scaling for Different Screen Sizes

A common issue when changing screen sizes is that fonts need to scale proportionally to maintain a harmonious design.

In this case, I use a ratio based on the screen width. This is calculated by taking the current screen width and dividing it by the original design width (e.g., 1440px in Figma).

double get webRatio {
  final width = MediaQuery.of(this).size.width;
  final ratio = width / 1440;
  return ratio < 1 ? (ratio < 0.75 ? 0.75 : ratio) : 1;
}

Applying Scaled Font Styles from Figma Design

Once we have the scaling ratio, we can create text styles dynamically based on the design specifications:

TextStyle get h46B => _webTextStyle.copyWith(
  fontSize: 46 * webRatio,
  fontWeight: FontWeight.w700,
  color: neutral80,
);

Full Code Overview

extension WebTextStyle on BuildContext{
/*static const _neoTextStyle = TextStyle(
fontFamily: WebFontFamily.nanumSquareNeo,
fontFamilyFallback: [
WebFontFamily.pretendard,
],
package: 'dsg_ui_core', // Thêm thư viện front chữ từ package: https://api.flutter.dev/flutter/painting/TextStyle-class.html#:~:text=To%20use%20a%20font%20family%20defined%20in%20a%20package%2C%20the%20package%20argument%20must%20be%20provided.%20For%20instance%2C%20suppose%20the%20font%20declaration%20above%20is%20in%20the%20pubspec.yaml%20of%20a%20package%20named%20my_package%20which%20the%20app%20depends%20on.%20Then%20creating%20the%20TextStyle%20is%20done%20as%20follows%3A
height: 1.5,
leadingDistribution:  TextLeadingDistribution.even,

); */
  static final _webTextStyle = GoogleFonts.inter().copyWith(
    fontWeight: FontWeight.w600,
    height: 1.5,
    leadingDistribution: TextLeadingDistribution.even,

  );

  /// Edit ratio of fontSize for responsive screen
  double get webRatio {
    final width = MediaQuery.of(this).size.width;
    final ratio = width / 1440;
    return ratio < 1 ? ratio < 0.75 ? 0.75 : ratio : 1;
  }

  /// Heading

  TextStyle get h46B =>
      _webTextStyle.copyWith(
        fontSize: 46 * webRatio,
        fontWeight: FontWeight.w700,
        color: white,
      );
}

Usage Example

You can apply the text styles as follows:

Text("Hello", style: context.h46B)

Customizing Styles with Colors

You can fully customize styles according to your design system. Here’s an example of modifying the text color dynamically:

Text("Hello", style: context.b10B.copyWith(
  color: context.white,
))

With this setup, when the light/dark mode changes, the text color will automatically adapt based on the selected theme.

This concludes my guide on setting up light/dark mode in Flutter. You can explore more about creating a UI package at

Core UI

Buy Me a Coffee | Support Me on Ko-fi

Last updated