🎯

FlutterGen

2020/09/17に公開

Test Article

Copy from https://github.com/FlutterGen/flutter_gen

The Flutter code generator for your assets, fonts, colors, … — Get rid of all String-based APIs.

Inspired by SwiftGen.

Motivation.

Using asset path string directly is not safe.

# pubspec.yaml
flutter:
  assets:
    - assets/images/profile.jpg

Bad
What would happen if you made a typo?

Widget build(BuildContext context) {
  return Image.asset('assets/images/profile.jpeg');
}

// The following assertion was thrown resolving an image codec:
// Unable to load asset: assets/images/profile.jpeg

⭕️ Good
We want to use it safely.

Widget build(BuildContext context) {
  return Assets.images.profile.image();
}

Installation

Run fluttergen after the configuration pubspec.yaml.

Use this package as an executable

Using a Homebrew Formula

  1. Install FlutterGen
$ brew install FlutterGen/tap/fluttergen
  1. Use FlutterGen
$ fluttergen -h

$ fluttergen -c example/pubspec.yaml

Using a Dart command-line

  1. Install FlutterGen
$ pub global activate flutter_gen

$ export PATH="$PATH":"$HOME/.pub-cache/bin"
  1. Use FlutterGen
$ fluttergen -h

$ fluttergen -c example/pubspec.yaml

Use this package as a part of build_runner

  1. Add build_runner and FlutterGen to your package's pubspec.yaml file:
dev_dependencies:
  build_runner:
  flutter_gen_runner:
  1. Install FlutterGen
$ flutter pub get
  1. Use FlutterGen
$ flutter packages pub run build_runner build

Configuration file

FlutterGen generates dart files based on the key flutter and flutter_gen of pubspec.yaml.

# pubspec.yaml
# ...

flutter_gen:
  output: lib/gen/ # Optional (default: lib/gen/)
  lineLength: 80 # Optional (default: 80)
  
  integrations:
    flutter_svg: true

  colors:
    inputs:
      - assets/color/colors.xml

flutter:
  uses-material-design: true
  assets:
    - assets/images/

  fonts:
    - family: Raleway
      fonts:
        - asset: assets/fonts/Raleway-Regular.ttf
        - asset: assets/fonts/Raleway-Italic.ttf
          style: italic

Available Parsers

Assets

Just follow the doc Adding assets and images#Specifying assets to specify assets, then FlutterGen will generate related dart files.
No other specific configuration is required.
Ignore duplicated.

# pubspec.yaml
flutter:
  assets:
    - assets/images/
    - assets/images/chip3/chip.jpg
    - assets/images/chip4/chip.jpg
    - assets/images/icons/paint.svg
    - assets/json/fruits.json
    - pictures/ocean_view.jpg

These configurations will generate assets.gen.dart under the lib/gen/ directory by default.

Usage Example

FlutterGen generates Image class if the asset is Flutter supported image format.

Example results of assets/images/chip.jpg:

  • Assets.images.chip is an implementation of AssetImage class.
  • Assets.images.chip.image(...) returns Image class.
  • Assets.images.chip.path just returns the path string.
Widget build(BuildContext context) {
  return Image(image: Assets.images.chip);
}

Widget build(BuildContext context) {
  return Assets.images.chip.image(
    width: 120,
    height: 120,
    fit: BoxFit.scaleDown,
  );

Widget build(BuildContext context) {
  // Assets.images.chip.path = 'assets/images/chip3/chip3.jpg'
  return Image.asset(Assets.images.chip.path);
}

If you are using SVG images with flutter_svg you can use the integration feature.

# pubspec.yaml
flutter_gen:

  integrations:
    flutter_svg: true

flutter:
  assets:
    - assets/images/icons/paint.svg
Widget build(BuildContext context) {
  return Assets.images.icons.paint.svg(
    width: 120,
    height: 120
  );
}

In other cases, the asset is generated as String class.

// If don't use the Integrations.
final svg = SvgPicture.asset(Assets.images.icons.paint);

final json = await rootBundle.loadString(Assets.json.fruits);

FlutterGen also support generating other style of Assets class:

# pubspec.yaml
flutter_gen:

  assets:
    # Assets.imagesChip 
    # style: camel-case
    
    # Assets.images_chip
    # style: snake-case

    # Assets.images.chip (default style)
    # style: dot-delimiter

flutter:
  assets:
    - assets/images/chip.png

The root directory will be omitted if it is either assets or asset.

assets/images/chip3/chip.jpg  => Assets.images.chip3.chip
assets/images/chip4/chip.jpg  => Assets.images.chip4.chip
assets/images/icons/paint.svg => Assets.images.icons.paint
assets/json/fruits.json       => Assets.json.fruits
pictures/ocean_view.jpg       => Assets.pictures.oceanView

<details><summary>Example of code generated by FlutterGen</summary>
<p>

/// GENERATED CODE - DO NOT MODIFY BY HAND
/// *****************************************************
///  FlutterGen
/// *****************************************************

import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter/services.dart';

class $PicturesGen {
  const $PicturesGen();

  AssetGenImage get chip5 => const AssetGenImage('pictures/chip5.jpg');
}

class $AssetsImagesGen {
  const $AssetsImagesGen();

  AssetGenImage get chip1 => const AssetGenImage('assets/images/chip1.jpg');
  AssetGenImage get chip2 => const AssetGenImage('assets/images/chip2.jpg');
  $AssetsImagesChip3Gen get chip3 => const $AssetsImagesChip3Gen();
  $AssetsImagesChip4Gen get chip4 => const $AssetsImagesChip4Gen();
  $AssetsImagesIconsGen get icons => const $AssetsImagesIconsGen();
  AssetGenImage get logo => const AssetGenImage('assets/images/logo.png');
  AssetGenImage get profile => const AssetGenImage('assets/images/profile.jpg');
}

class $AssetsJsonGen {
  const $AssetsJsonGen();

  String get fruits => 'assets/json/fruits.json';
}

class $AssetsImagesChip3Gen {
  const $AssetsImagesChip3Gen();

  AssetGenImage get chip3 =>
      const AssetGenImage('assets/images/chip3/chip3.jpg');
}

class $AssetsImagesChip4Gen {
  const $AssetsImagesChip4Gen();

  AssetGenImage get chip4 =>
      const AssetGenImage('assets/images/chip4/chip4.jpg');
}

class $AssetsImagesIconsGen {
  const $AssetsImagesIconsGen();

  SvgGenImage get fuchsia =>
      const SvgGenImage('assets/images/icons/fuchsia.svg');
  SvgGenImage get kmm => const SvgGenImage('assets/images/icons/kmm.svg');
  SvgGenImage get paint => const SvgGenImage('assets/images/icons/paint.svg');
}

class Assets {
  Assets._();

  static const $AssetsImagesGen images = $AssetsImagesGen();
  static const $AssetsJsonGen json = $AssetsJsonGen();
  static const $PicturesGen pictures = $PicturesGen();
}

class AssetGenImage extends AssetImage {
  const AssetGenImage(String assetName)
      : _assetName = assetName,
        super(assetName);
  final String _assetName;

  Image image({
    ImageFrameBuilder frameBuilder,
    ImageLoadingBuilder loadingBuilder,
    ImageErrorWidgetBuilder errorBuilder,
    String semanticLabel,
    bool excludeFromSemantics = false,
    double width,
    double height,
    Color color,
    BlendMode colorBlendMode,
    BoxFit fit,
    AlignmentGeometry alignment = Alignment.center,
    ImageRepeat repeat = ImageRepeat.noRepeat,
    Rect centerSlice,
    bool matchTextDirection = false,
    bool gaplessPlayback = false,
    bool isAntiAlias = false,
    FilterQuality filterQuality = FilterQuality.low,
  }) {
    return Image(
      image: this,
      frameBuilder: frameBuilder,
      loadingBuilder: loadingBuilder,
      errorBuilder: errorBuilder,
      semanticLabel: semanticLabel,
      excludeFromSemantics: excludeFromSemantics,
      width: width,
      height: height,
      color: color,
      colorBlendMode: colorBlendMode,
      fit: fit,
      alignment: alignment,
      repeat: repeat,
      centerSlice: centerSlice,
      matchTextDirection: matchTextDirection,
      gaplessPlayback: gaplessPlayback,
      isAntiAlias: isAntiAlias,
      filterQuality: filterQuality,
    );
  }

  String get path => _assetName;
}

class SvgGenImage {
  const SvgGenImage(this._assetName);

  final String _assetName;

  SvgPicture svg({
    bool matchTextDirection = false,
    AssetBundle bundle,
    String package,
    double width,
    double height,
    BoxFit fit = BoxFit.contain,
    AlignmentGeometry alignment = Alignment.center,
    bool allowDrawingOutsideViewBox = false,
    WidgetBuilder placeholderBuilder,
    Color color,
    BlendMode colorBlendMode = BlendMode.srcIn,
    String semanticsLabel,
    bool excludeFromSemantics = false,
    Clip clipBehavior = Clip.hardEdge,
  }) {
    return SvgPicture.asset(
      _assetName,
      matchTextDirection: matchTextDirection,
      bundle: bundle,
      package: package,
      width: width,
      height: height,
      fit: fit,
      alignment: alignment,
      allowDrawingOutsideViewBox: allowDrawingOutsideViewBox,
      placeholderBuilder: placeholderBuilder,
      color: color,
      colorBlendMode: colorBlendMode,
      semanticsLabel: semanticsLabel,
      excludeFromSemantics: excludeFromSemantics,
      clipBehavior: clipBehavior,
    );
  }

  String get path => _assetName;
}

</p>
</details>

Fonts

Just follow the doc Use a custom font to specify fonts, then FlutterGen will generate related dart files.
No other specific configuration is required.
Ignore duplicated.

# pubspec.yaml
flutter:
  fonts:
    - family: Raleway
      fonts:
        - asset: assets/fonts/Raleway-Regular.ttf
        - asset: assets/fonts/Raleway-Italic.ttf
          style: italic
    - family: RobotoMono
      fonts:
        - asset: assets/fonts/RobotoMono-Regular.ttf
        - asset: assets/fonts/RobotoMono-Bold.ttf
          weight: 700

These configurations will generate fonts.gen.dart under the lib/gen/ directory by default.

Usage Example

Text(
  'Hi there, I\'m FlutterGen',
  style: TextStyle(
    fontFamily: FontFamily.robotoMono,
    fontFamilyFallback: const [FontFamily.raleway],
  ),

<details><summary>Example of code generated by FlutterGen</summary>
<p>

/// GENERATED CODE - DO NOT MODIFY BY HAND
/// *****************************************************
///  FlutterGen
/// *****************************************************

class FontFamily {
  FontFamily._();

  static const String raleway = 'Raleway';
  static const String robotoMono = 'RobotoMono';
}

</p>
</details>

Colors

FlutterGen supports generating colors from XML format files.
Ignore duplicated.

# pubspec.yaml
flutter_gen:
  colors:
    inputs:
      - assets/color/colors.xml
      - assets/color/colors2.xml

FlutterGen generates MaterialColor class
if the element has the attribute type="material", otherwise a normal Color class will be generated.

<color name="milk_tea">#F5CB84</color>
<color name="cinnamon" type="material">#955E1C</color>

These configurations will generate colors.gen.dart under the lib/gen/ directory by default.

Usage Example

Text(
  'Hi there, I\'m FlutterGen',
  style: TextStyle(
    color: ColorName.denim,
  ),

<details><summary>Example of code generated by FlutterGen</summary>
<p>

/// GENERATED CODE - DO NOT MODIFY BY HAND
/// *****************************************************
///  FlutterGen
/// *****************************************************

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

class ColorName {
  ColorName._();

  static const Color black = Color(0xFF000000);
  static const Color black30 = Color(0x4D000000);
  static const Color black40 = Color(0x66000000);
  static const Color black50 = Color(0x80000000);
  static const Color black60 = Color(0x99000000);
  static const MaterialColor crimsonRed = MaterialColor(
    0xFFCF2A2A,
    <int, Color>{
      50: Color(0xFFF9E5E5),
      100: Color(0xFFF1BFBF),
      200: Color(0xFFE79595),
      300: Color(0xFFDD6A6A),
      400: Color(0xFFD64A4A),
      500: Color(0xFFCF2A2A),
      600: Color(0xFFCA2525),
      700: Color(0xFFC31F1F),
      800: Color(0xFFBD1919),
      900: Color(0xFFB20F0F),
    },
  );
  static const Color gray410 = Color(0xFF979797);
  static const Color gray70 = Color(0xFFEEEEEE);
  static const Color white = Color(0xFFFFFFFF);
  static const MaterialColor yellowOcher = MaterialColor(
    0xFFDF9527,
    <int, Color>{
      50: Color(0xFFFBF2E5),
      100: Color(0xFFF5DFBE),
      200: Color(0xFFEFCA93),
      300: Color(0xFFE9B568),
      400: Color(0xFFE4A547),
      500: Color(0xFFDF9527),
      600: Color(0xFFDB8D23),
      700: Color(0xFFD7821D),
      800: Color(0xFFD27817),
      900: Color(0xFFCA670E),
    },
  );
}

</p>
</details>

Issues

Please file FlutterGen specific issues, bugs, or feature requests in our issue tracker.

Plugin issues that are not specific to FlutterGen can be filed in the Flutter issue tracker.

Contributing

We are looking for co-developers.

If you wish to contribute a change to any of the existing plugins in this repo,
please review our contribution guide
and open a pull request.

Milestone

  • Documentation (English proofreading)
  • Assets generation
  • Fonts generation
  • Colors generation
    • Support xml
  • Support change output path
  • Support hierarchical generation
    'assets/image/home/label.png' => Assets.image.home.label
    'assets/image/detail/label.png' => Assets.image.detail.label
  • Platforms channels generation

Discussion