LYCN Theme Engine

A reusable Flutter theme engine for managing configurable light, dark, and system themes with custom color tokens, dynamic statuses, runtime theme editing, persistence, JSON import/export, and bundled theme configuration.

Features

  • Light, dark, and system theme modes
  • Built-in default theme configuration
  • Custom color tokens
  • Separate light and dark colors for tokens
  • Dynamic status definitions and status colors
  • Runtime theme customization
  • Persistent runtime configuration
  • JSON import and export
  • Theme reset and seed reset
  • Bundled theme configuration through assets/theme_seed.json
  • Material 3 ThemeData generation
  • Built-in Theme Manager UI
  • Reusable themed widgets
  • Theme mode controls
  • Customizable status labels
  • Custom storage implementations
  • Android, iOS, Windows, macOS, and Linux support

Installation

Add lycn_theme_engine to your pubspec.yaml:

dependencies:
  lycn_theme_engine: ^1.0.0

Then install the package:

flutter pub get

Quick Start

Create a ThemeManager, initialize it, and provide it to LycnThemeApp:

import 'package:flutter/material.dart';
import 'package:lycn_theme_engine/lycn_theme_engine.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final themeManager = ThemeManager();

  await themeManager.initialize();

  runApp(
    LycnThemeApp(
      manager: themeManager,
      home: const HomePage(),
    ),
  );
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('LYCN Theme Engine'),
      ),
      body: Center(
        child: Text(
          'Theme Engine Example',
          style: TextStyle(
            color: Theme.of(context).colorScheme.primary,
          ),
        ),
      ),
    );
  }
}

ThemeManager generates the light and dark ThemeData instances and controls the active Flutter theme mode.

Theme Modes

LYCN Theme Engine supports three theme modes:

  • ThemeModeType.light
  • ThemeModeType.dark
  • ThemeModeType.system

Set the theme mode at runtime:

await themeManager.setMode(ThemeModeType.dark);

For system-controlled theme mode:

await themeManager.setMode(ThemeModeType.system);

The Flutter ThemeMode can also be accessed through:

themeManager.flutterThemeMode

Built-in Theme Tokens

The package includes a set of built-in color tokens for common application UI elements.

Examples include:

primary
secondary
tertiary

bg.scaffold
bg.surface

btn.confirm
btn.reject
btn.other

text.title
text.subtitle
text.description
text.other

border.enabled
border.disabled
border.other

status.default

Access commonly used colors directly from ThemeManager:

final primary = themeManager.primary;
final secondary = themeManager.secondary;
final scaffoldBackground = themeManager.bgScaffold;
final surfaceBackground = themeManager.bgSurface;

final titleColor = themeManager.textTitle;
final subtitleColor = themeManager.textSubtitle;

final confirmColor = themeManager.buttonConfirm;
final rejectColor = themeManager.buttonReject;

You can also resolve any token by its key:

final color = themeManager.color('primary');

Custom Color Tokens

Applications can define their own color tokens in addition to the built-in tokens.

For example:

await themeManager.addOrUpdateToken(
  TokenDef(
    key: 'brand.accent',
    label: 'Brand Accent',
  ),
  lightColor: Colors.blue,
  darkColor: Colors.lightBlue,
);

Resolve the custom token:

final accentColor = themeManager.color('brand.accent');

Custom tokens support separate light and dark colors.

Check Whether a Token Exists

if (themeManager.hasToken('brand.accent')) {
  final color = themeManager.color('brand.accent');
}

Get Token Definitions

final tokens = themeManager.tokenDefs;

for (final token in tokens) {
  print(token.key);
}

Remove a Custom Token

await themeManager.removeToken('brand.accent');

Built-in locked tokens cannot be removed.

Dynamic Statuses

LYCN Theme Engine supports dynamic application statuses.

A status contains:

  • Actual value
  • Display label
  • Light-mode color
  • Dark-mode color

Example:

await themeManager.addOrUpdateStatus(
  StatusDef(
    actualValue: 'SUCCESS',
    displayLabel: 'Success',
    lightColor: Colors.green,
    darkColor: Colors.lightGreen,
  ),
);

Resolve a Status Color

final color = themeManager.statusColor('SUCCESS');

Resolve a Status Label

final label = themeManager.statusLabel('SUCCESS');

Check Whether a Status Exists

if (themeManager.hasStatus('SUCCESS')) {
  final color = themeManager.statusColor('SUCCESS');
}

Remove a Status

await themeManager.removeStatus('SUCCESS');

Status colors are also exposed through automatically generated status.<ACTUAL_VALUE> tokens.

For example:

status.SUCCESS
status.PENDING
status.FAILED

Bundled Theme Configuration

Applications can provide a theme configuration that is bundled into the consuming application's build.

Create:

assets/theme_seed.json

Then add the asset to the application's pubspec.yaml:

flutter:
  assets:
    - assets/theme_seed.json

A seed can contain the application's initial theme configuration, including:

  • Theme mode
  • Color token definitions
  • Status definitions
  • Light palette
  • Dark palette

Initialize the manager using the seed:

final themeManager = ThemeManager();

await themeManager.initialize(
  seed: const ThemeSeed(),
);

The default seed path is:

assets/theme_seed.json

A custom asset path can also be supplied:

await themeManager.initialize(
  seed: const ThemeSeed(
    assetPath: 'assets/my_theme.json',
  ),
);

Configuration Precedence

Theme configuration is resolved in the following order:

Built-in defaults
       ↓
Bundled theme seed
       ↓
Saved runtime configuration
       ↓
Final active configuration

This means an application can ship a predefined theme while allowing users to customize it at runtime.

Saved runtime configuration takes precedence over the bundled seed.

Theme Reset

Reset the theme to the built-in defaults:

await themeManager.reset();

Reset to the bundled seed configuration:

await themeManager.resetToSeed();

Clear saved runtime configuration and return to the bundled configuration:

await themeManager.clearSavedConfiguration();

Persistence

Runtime theme configuration is persisted using SharedPreferences by default.

The default storage implementation is:

SharedPreferencesThemeStorage

The package also provides:

ThemeStorage
MemoryThemeStorage

Using Custom Storage

Applications can implement ThemeStorage when a different persistence mechanism is required:

class MyThemeStorage implements ThemeStorage {
  @override
  Future<void> save(ThemeConfig config) async {
    // Save configuration.
  }

  @override
  Future<ThemeConfig?> load() async {
    // Load configuration.
    return null;
  }

  @override
  Future<void> clear() async {
    // Clear configuration.
  }
}

Then provide the storage implementation to ThemeManager:

final themeManager = ThemeManager(
  storage: MyThemeStorage(),
);

JSON Import and Export

Theme configurations can be exported to JSON.

Export

final json = themeManager.exportJson();

For compact JSON:

final json = themeManager.exportJson(
  pretty: false,
);

Import

final success = await themeManager.importJson(json);

if (success) {
  print('Theme imported successfully');
}

Invalid JSON does not replace the current configuration.

The package also exposes ThemeJson for direct encoding and decoding of ThemeConfig objects.

Theme Manager UI

The package includes a built-in Theme Manager screen for runtime theme configuration.

Open it using:

Navigator.of(context).push(
  MaterialPageRoute(
    builder: (_) => ThemeManagerScreen(
      manager: themeManager,
    ),
  ),
);

The Theme Manager UI provides:

  • Light and dark palette editing
  • Theme mode selection
  • Built-in token display
  • Custom token creation
  • Token color editing
  • Token deletion for unlocked tokens
  • Dynamic status creation
  • Status editing
  • Status color editing
  • Status deletion
  • Theme reset
  • Reset to bundled seed
  • Clear saved configuration
  • JSON import
  • JSON export
  • Live theme preview

The UI adapts to different screen sizes.

Reusable Theme Widgets

LYCN Theme Engine provides convenience widgets that resolve colors and statuses through the active ThemeManager.

Text

TText.title('Page Title');

TText.subtitle('Subtitle');

TText.description('Description');

TText.other('Additional text');

Buttons

TButton.confirm(
  label: 'Save',
  onPressed: () {},
);

TButton.reject(
  label: 'Cancel',
  onPressed: () {},
);

TButton.other(
  label: 'Other Action',
  onPressed: () {},
);

Custom token buttons are also supported:

TButton.token(
  tokenKey: 'brand.accent',
  label: 'Custom Action',
  onPressed: () {},
);

Status Chips

TChip.status('SUCCESS');

The status chip automatically resolves:

  • Status color
  • Status display label

from the active theme configuration.

Cards

TCard(
  child: Text('Content'),
);

Dividers

TDivider();

Theme Mode Controls

The package includes reusable theme mode controls.

Toggle

ThemeModeToggle(
  manager: themeManager,
);

The toggle cycles through:

Light → System → Dark → Light

Light/Dark Switch

ThemeModeSwitch(
  manager: themeManager,
);

Segmented Control

ThemeModeSegmented(
  manager: themeManager,
);

The segmented control provides:

Light | System | Dark

Using ThemeResolver

For applications that need direct token and status resolution, the package provides ThemeResolver.

final resolver = ThemeResolver(themeManager.config);

final color = resolver.color('primary');

final statusColor = resolver.statusColor('SUCCESS');

final statusLabel = resolver.statusLabel('SUCCESS');

The resolver can also resolve colors for a specific theme mode:

final lightColor = resolver.color(
  'primary',
  mode: ThemeModeType.light,
);

final darkColor = resolver.color(
  'primary',
  mode: ThemeModeType.dark,
);

Theme Configuration Model

A complete theme configuration consists of:

ThemeConfig
├── mode
├── token definitions
├── status definitions
├── light palette
└── dark palette

Example:

final config = ThemeConfig(
  mode: ThemeModeType.system,
  tokenDefs: const [
    TokenDef(
      key: 'brand.accent',
      label: 'Brand Accent',
    ),
  ],
  statuses: const [
    StatusDef(
      actualValue: 'SUCCESS',
      displayLabel: 'Success',
      lightColor: Color(0xFF16A34A),
      darkColor: Color(0xFF4ADE80),
    ),
  ],
  light: const {
    'brand.accent': Color(0xFF2563EB),
  },
  dark: const {
    'brand.accent': Color(0xFF60A5FA),
  },
);

Application Integration

A typical application structure can look like:

my_app/
├── assets/
│   └── theme_seed.json
├── lib/
│   ├── main.dart
│   └── ...
└── pubspec.yaml

pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  lycn_theme_engine: ^1.0.0

flutter:
  assets:
    - assets/theme_seed.json

main.dart:

import 'package:flutter/material.dart';
import 'package:lycn_theme_engine/lycn_theme_engine.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final manager = ThemeManager();

  await manager.initialize(
    seed: const ThemeSeed(),
  );

  runApp(
    LycnThemeApp(
      manager: manager,
      home: const HomePage(),
    ),
  );
}

Supported Platforms

LYCN Theme Engine is designed for:

  • Android
  • iOS
  • Windows
  • macOS
  • Linux

Web is not currently a supported target.

Example Application

The package repository contains an example Flutter application demonstrating the theme engine.

The example demonstrates:

  • Theme initialization
  • Bundled theme seed configuration
  • Light/dark/system modes
  • Custom tokens
  • Dynamic statuses
  • Runtime theme editing
  • Theme Manager UI
  • Reusable theme widgets

Testing

The package includes unit and widget tests covering the core functionality.

Run the package tests:

flutter test

Run static analysis:

flutter analyze

For deterministic test execution in environments where test concurrency can cause runner issues:

flutter test --concurrency=1

Package Structure

The main public API is available through:

import 'package:lycn_theme_engine/lycn_theme_engine.dart';

The package contains the following major components:

ThemeManager
ThemeResolver
ThemeConfig
TokenDef
StatusDef
ThemeSeed
ThemeStorage
ThemeJson
ThemeDataBuilder

LycnThemeApp
ThemeInitializer

ThemeManagerScreen

TText
TButton
TChip
TCard
TDivider

ThemeModeToggle
ThemeModeSwitch
ThemeModeSegmented

Version

Current version:

1.0.0

Repository

Source code, examples, tests, and issue tracking are available on GitHub:

https://github.com/LycnAlpha/lycn_theme_engine

License

LYCN Theme Engine is released under the MIT License.

See the LICENSE file for the complete license text.