style_builder 1.1.0 copy "style_builder: ^1.1.0" to clipboard
style_builder: ^1.1.0 copied to clipboard

A generator for building style extensions for Flutter.

style_builder #

Welcome to style_builder, the code generator for Flutter style (ThemeExtension) classes. The code generator helps to:

  • minimize the required boilerplate code (without ugly part statements)
  • define cosmetic default values, that could be derived from the BuildContext (e.g. current main theme)
  • resolve cosmetic values as intended by the flutter team

Theme extensions #

For more information on ThemeExtensions see: https://youtu.be/8-szcYzFVao

Install #

Make sure to add these packages to the project dependencies:

  • [build_runner] tool to run code generators (dev dependency)
  • [style_builder] this package (dev dependency)
  • [style_builder_annotation] annotations for [style_builder]
flutter pub add --dev build_runner
flutter pub add --dev style_builder
flutter pub add style_builder_annotation

Define your style class #

style_builder is a generator for classes that are annotated with @GenerateStyleClass().

The annotated class provides:

  • The cosmetic properties of the style class.
  • The name of the style class. It typically ends with "Default". e.g. "MyWidgetDefault" will generate a "MyWidgetStyle" class.

Note an annotated class:

  • Must be a const class.
  • Provides default values for the all cosmetic properties. The properties and their default values are defined by either:
    • none static final fields
    • none static getter methods
    • none static methods without parameters
    • none static methods with a BuildContext parameter

An example:

//file: my_company.dart;

import 'dart:ui';

import 'package:style_builder_annotation/style_builder_annotation.dart';

/// This library demonstrates the use of the style_builder package to generate
/// a generic style class. The generated class provides a way to
/// define and resolve cosmetic properties. In this case it is a company style.

/// This class configures the generation of the [MyCompanyStyle] class.
/// It contains various cosmetic property types to demonstrate it working.
/// See the generated code for the [MyCompanyStyle] class in my_company.g.dart
///
/// Notes:
/// * The class is annotated with [GenerateStyleClass] to indicate that it
///   should be processed by the style_builder package.
/// * The class is a const class, which means it can be used as a compile-time constant.
/// * That cosmetic properties are defined by providing default values with either:
///   * none static final fields
///   * none static getter methods
///   * none static methods without parameters
///   * none static methods with a BuildContext parameter
@GenerateStyleClass()
class MyCompanyDefault {
  const MyCompanyDefault();
  final Color primary = const Color(0xFF7986CB); // Indigo
  final Color secondary = const Color(0xFF009688); // Teal
  final Color tertiary = const Color(0xFFFFC107); // Amber
}

Run the code generator #

To run the code generator, run the following commands:

flutter pub run build_runner build --delete-conflicting-outputs

The generated code #

This generates the following code:

//file: my_company.g.dart;

import 'package:flutter/material.dart' as i1;
import 'dart:ui' as i2;
import 'package:example/my_company.dart' as i3;

/// This class is generated by the style_builder package. Do not edit manually.
/// Source class : MyCompanyDefault
/// To update run: dart run build_runner build --delete-conflicting-output
/// For more info: https://pub.dev/packages/style_builder
class MyCompanyStyle extends i1.ThemeExtension<MyCompanyStyle> {
  final i2.Color? primary;
  final i2.Color? secondary;
  final i2.Color? tertiary;
  static const i3.MyCompanyDefault defaults = i3.MyCompanyDefault();
  const MyCompanyStyle({this.primary, this.secondary, this.tertiary});

  /// Resolves the MyCompanyStyle within the current context / theme
  /// and returns a record with non nullable values.
  /// The values are resolved in the following order:
  /// 1. The value from the provided style (e.g. a constructor parameter of a widget)
  /// 2. The value from the theme extension
  /// 3. The default value from the annotated class
  static ({i2.Color primary, i2.Color secondary, i2.Color tertiary}) resolve(
    i1.BuildContext context, [
    MyCompanyStyle? style,
  ]) {
    var theme = i1.Theme.of(context).extension<MyCompanyStyle>();
    return (
      primary: style?.primary ?? theme?.primary ?? defaults.primary,
      secondary: style?.secondary ?? theme?.secondary ?? defaults.secondary,
      tertiary: style?.tertiary ?? theme?.tertiary ?? defaults.tertiary,
    );
  }

  /// Creates a copy of this MyCompanyStyle with the current values
  /// replaced by given none-null parameter values.
  @override
  MyCompanyStyle copyWith({
    i2.Color? primary,
    i2.Color? secondary,
    i2.Color? tertiary,
  }) => MyCompanyStyle(
    primary: primary ?? this.primary,
    secondary: secondary ?? this.secondary,
    tertiary: tertiary ?? this.tertiary,
  );

  /// Linearly interpolate with another [ThemeExtension] object.
  /// The following types are supported:
  /// * bool
  /// * Enum
  /// * int
  /// * double
  /// * all classes with a correct static lerp method, e.g. Color.lerp(a,b,t)
  @override
  i1.ThemeExtension<MyCompanyStyle> lerp(MyCompanyStyle? other, double t) =>
      other == null
          ? this
          : MyCompanyStyle(
            primary: i2.Color.lerp(primary, other.primary, t),
            secondary: i2.Color.lerp(secondary, other.secondary, t),
            tertiary: i2.Color.lerp(tertiary, other.tertiary, t),
          );

  @override
  String toString() {
    final values = <String>[
      if (primary != null) 'primary: $primary',
      if (secondary != null) 'secondary: $secondary',
      if (tertiary != null) 'tertiary: $tertiary',
    ];
    return "MyCompanyStyle(${values.join(', ')})";
  }

  @override
  int get hashCode => Object.hash(primary, secondary, tertiary);

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is MyCompanyStyle &&
          primary == other.primary &&
          secondary == other.secondary &&
          tertiary == other.tertiary;
}

Using a generated style class #

Use the static resolve method of the generated style class in your widget tree. It returns a record with none-nullable cosmetic values. In example:

  MyCompanyStyle.resolve(context).tertiary

You can also add a style class ass optional parameter. In example, you created MyWidget with a generated generated MyWidgetStyle class. You would use it as:

class MyWidget extends StatelessWidget {
  /// You can pass a custom style to the widget constructor,
  /// which will override default values
  final MyWidgetStyle? style;

  const MyWidget({super.key, this.style});

  @override
  Widget build(BuildContext context) {
    // Resolve the style using the generated MyWidgetStyle class
    var resolvedStyle = MyWidgetStyle.resolve(context, style);
    /// return your implementation here, using resolvedStyle
  }
}

Examples #

An example project can be found here: https://github.com/domain-centric/style_builder/tree/main/example

2
likes
160
points
187
downloads

Publisher

unverified uploader

Weekly Downloads

A generator for building style extensions for Flutter.

Repository (GitHub)

Topics

#theme #style #theme-extension #codegen

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

analyzer, build, collection, dart_code, style_builder_annotation

More

Packages that depend on style_builder