flutter_2yaml 0.6.0 copy "flutter_2yaml: ^0.6.0" to clipboard
flutter_2yaml: ^0.6.0 copied to clipboard

Convert Flutter widget files into compact YAML optimized for LLM token consumption, and back again. Pipe syntax, CSS-like shorthands, arrow callbacks.

flutter_2yaml #

pub package pub points License: MIT Dart 3 Platform

Convert Flutter .dart widget files into compact YAML representations optimized for LLM/AI token consumption. Supports bidirectional conversion — Dart to YAML and YAML back to Dart.

Why? #

When feeding Flutter code to AI models, full .dart files waste tokens on boilerplate — imports, @override, super.key, BuildContext context, const keywords, semicolons, brackets. flutter_2yaml strips all that and produces structured YAML using CSS-like shorthands, pipe syntax, and arrow notation.

Measured across the fixtures in this repo, that saves 40-65% of tokens — around 50% on average at the default standard level, more on simple screens and at --level minimal, less on property-dense ones.

Before (Dart — 45 lines) #

class SplashScreen extends StatefulWidget {
  const SplashScreen({super.key});
  @override
  State<SplashScreen> createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen> {
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    Future.delayed(const Duration(seconds: 3), () {
      Navigator.pushReplacementNamed(context, '/home');
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Image.asset('assets/logo.png', width: 200, height: 200),
            const SizedBox(height: 20),
            if (_isLoading)
              const CircularProgressIndicator(color: Colors.blue),
          ],
        ),
      ),
    );
  }
}

After (YAML — 14 lines, standard level) #

page: SplashScreen
type: StatefulWidget
state: [_isLoading: bool = true]
lifecycle:
  initState: [Future.delayed(3s)]
build:
  Scaffold:
    bg: white
    body:
      Center:
        child:
          Column(center):
            children:
              - Image.asset: assets/logo.png | 200x200
              - SizedBox: { h: 20 }
              - if _isLoading: CircularProgressIndicator: { color: blue }

Compact Format Features #

Pipe Syntax | #

Text: "Hello World" | 20 | bold | white    # text | fontSize | fontWeight | color
Image.asset: logo.png | 200x200            # source | dimensions
Icon: search | onTap → goSearch()          # icon | callback

Icon takes no callbacks of its own, so an icon with a callback is generated as the widget that does: onPressed becomes an IconButton, any other callback a GestureDetector wrapping the Icon.

CSS-Like Shorthands #

Shorthand Flutter Equivalent
bg backgroundColor / color (in BoxDecoration)
br borderRadius: BorderRadius.circular(N)
p padding: EdgeInsets.all(N)
px, py EdgeInsets.symmetric(horizontal/vertical: N)
h, w height, width
full double.infinity
t, l, r, b top, left, right, bottom (Positioned)
gap, runGap spacing, runSpacing (Wrap)
shadow boxShadow: [BoxShadow(...)]
border Border.all(...){c: color, w: width}

Arrow Callback Notation #

onTap → goSearch()          # onTap: () => controller.goSearch()
onPressed → handleSubmit    # onPressed: handleSubmit

Parenthetical Alignment #

Row(spaceBetween)     # Row(mainAxisAlignment: MainAxisAlignment.spaceBetween)
Column(start)         # Column(crossAxisAlignment: CrossAxisAlignment.start)
Column(center)        # Column(mainAxisAlignment: MainAxisAlignment.center)

Scaffold Named Children #

Scaffold:
  bg: white
  appBar:
    AppBar: { title: Text: "Home" | 20 | bold, bg: blue }
  drawer:
    Drawer: { ... }
  body:
    Center: { ... }
  floatingActionButton:
    FloatingActionButton: { onPressed → add(), child: Icon: add }
  bottomNavigationBar:
    BottomNavigationBar: { ... }

Auto-Detection #

  • Page vs Widget: page: for Scaffold-containing widgets, widget: for components
  • State Management: Auto-detects GetX, Riverpod, Bloc, Provider, MobX from imports
  • Color Shorthand: Colors.blueblue, Color(0xFF123456)#123456
  • Icon Shorthand: Icons.menumenu, Icons.searchsearch
  • Dimension Shorthand: width: 80, height: 8080x80, double.infinityfull
  • Theme Shorthand: Theme.of(context).textTheme.headlinetheme.headline
  • MediaQuery Shorthand: MediaQuery.of(context).size.widthscreen.w

Component References (figma2flutter) #

children:
  - <QuickAcess:Property 1=Default>           # Component with variant
  - <B-NavBar:Status=Home, Mode=Light>        # Multi-property variant
  - <B-NavBar>                                # Component without variant

Component tags (<>) are generated by the figma2flutter plugin. Full resolution requires the MCP server. Offline reverse produces Text('ComponentName (Variant)') placeholders with a warning.

Collection Elements #

children:
  - ...items                                  # Spread operator
  - for(item in products): ProductCard: {}    # For-in loop
  - if _isLoading: CircularProgressIndicator  # Conditional
  - if _isLoading: Spinner: {}                # Conditional with alternative
    else: Text: "Done"

Named Child Slots #

Any single-widget slot is a key of its own; list-valued slots keep their own name so the reverse converter emits the argument the widget actually declares.

Scaffold:
  appBar:
    AppBar:
      title:
        Text: "Home"
      actions:                                # not `children:` — AppBar has none
        - Icon: search
  body:
    Center: { }
BottomNavigationBar:
  items: []                                   # empty lists are preserved
ListView.builder:
  itemCount: items.length
  itemBuilder:                                # rebuilt as (context, index) => …
    ProductCard: { }

Switch Arms #

A switch in a build method becomes a switch(...) node with one arm per case:

build:
  switch(status):
    - case Status.loading: CircularProgressIndicator: {}
    - case Status.error: Text: "failed"
    - default: HomeView: {}

Raw Expression Escape Hatch #

Anything the compact format has no shape for is preserved verbatim rather than dropped, and round-trips unchanged:

build:
  expr: _buildBody(context)

Multiple Widget Classes Per File #

A .dart file declaring several widget classes produces one YAML document per class, separated by ---. Reversing such a file writes all classes back into a single .dart with the imports emitted once.

widget: Header
type: StatelessWidget
build:
  Text: title
---
widget: Footer
type: StatelessWidget
build:
  Text: "footer"

Generic Widgets and Top-Level Declarations #

Generic widget classes keep their type parameters, and non-widget declarations the widget refers to (enums, typedefs) travel with it at --level full:

widget: Boxed<T extends Comparable<T>>
type: StatelessWidget
declarations:
  - enum Status {loading, error, done}

Installation #

dart pub global activate flutter_2yaml

Or add to your project's dev_dependencies:

dev_dependencies:
  flutter_2yaml: ^0.6.0

Requires Dart 3.11 or newer (the analyzer package this tool is built on requires it).

Usage #

CLI — Forward (Dart → YAML) #

# Convert a single file
flutter_2yaml lib/screens/splash_screen.dart

# Convert all .dart files in a directory
flutter_2yaml lib/screens/ --recursive

# Choose verbosity level
flutter_2yaml lib/screens/ --level minimal    # Widget tree only
flutter_2yaml lib/screens/ --level standard   # + state & lifecycle (default)
flutter_2yaml lib/screens/ --level full       # + imports, constructor, methods

# Custom output directory
flutter_2yaml lib/screens/ --output yaml_output/

# Watch mode — auto-regenerate on file changes
flutter_2yaml lib/screens/ --watch

CLI — Reverse (YAML → Dart) #

# Convert a single YAML file back to Dart
flutter_2yaml reverse splash_screen.yaml

# Convert a directory of YAML files
flutter_2yaml reverse yaml_output/ --recursive

# Custom output directory
flutter_2yaml reverse yaml_output/ --output lib/screens/

Programmatic API #

import 'package:flutter_2yaml/flutter_2yaml.dart';

// Forward: Dart → YAML
final analyzer = DartFileAnalyzer();
final model = analyzer.analyze(dartSource, 'my_widget.dart');
final yamlGenerator = YamlGenerator();
final yaml = yamlGenerator.generate(model!, VerbosityLevel.standard);

// Reverse: YAML → Dart
final yamlParser = YamlParser();
final reverseModel = yamlParser.parse(yamlSource, 'my_widget.yaml');
final dartGenerator = DartGenerator();
final dartCode = dartGenerator.generate(reverseModel);

// Files declaring several widget classes: one model per class
final models = analyzer.analyzeAll(dartSource, 'my_widgets.dart');
final multiYaml = yamlGenerator.generateAll(models, VerbosityLevel.standard);

final reverseModels = yamlParser.parseAll(multiYaml, 'my_widgets.yaml');
final multiDart = dartGenerator.generateAll(reverseModels);

// File-level conversion (handles multi-class files automatically)
final converter = Converter();
converter.convertFile('lib/screens/home.dart');

final reverseConverter = ReverseConverter();
reverseConverter.reverseFile('home.yaml');

Verbosity Levels #

Level Includes Use Case
minimal Widget tree + shorthands Quick UI structure overview
standard + state, lifecycle Full widget understanding (default)
full + imports, constructor, all methods, enums/typedefs Complete file representation — use this for a round trip

Supported Widget Types #

  • StatelessWidget, StatefulWidget
  • ConsumerWidget, ConsumerStatefulWidget (Riverpod)
  • GetView, GetWidget (GetX)
  • Non-widget classes are automatically skipped

Supported Widget Features #

  • Scaffold with all named slots (appBar, drawer, FAB, bottomNav, etc.)
  • Container with BoxDecoration (bg, br, shadow, gradient, border, shape)
  • Stack + Positioned with coordinate shorthands
  • ListView.builder / GridView.builder (itemBuilder extraction)
  • Wrap with spacing shorthands
  • Text with full TextStyle pipe syntax
  • Conditional children (if / if-else)
  • Spread and for-in elements in children lists
  • All callback types with arrow notation
  • switch expressions and statements in build()
  • Multiple widget classes per file (one --- document each)
  • Generic widget classes, with type parameters preserved
  • Colors as Colors.x, #RRGGBB, or #AARRGGBB (alpha preserved)
  • theme.* and screen.* context lookups

Known Limitations #

The compact format is a summary, so some detail is deliberately not carried:

  • Method bodies are summarised, not preserved. initState becomes a list of action descriptions; on the way back, actions that dropped information (an assignment, a call whose arguments were elided, control flow) are emitted as comments rather than as code that would not compile.
  • Callback bodies with more than one statement are reduced to an empty lambda of the right arity (validator → (value) {}).
  • Only enums and typedefs are carried among non-widget top-level declarations, and only at --level full; other top-level code is skipped.
  • Field mutability is not recorded — a final state field comes back non-final.
  • minimal and standard omit imports and constructors by design, so reversing at those levels produces a widget without its constructor parameters. Use --level full for a round trip that preserves the class surface.

Contributing #

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

License #

MIT License — see LICENSE for details.

1
likes
160
points
111
downloads

Documentation

API reference

Publisher

verified publishermsishamim.com

Weekly Downloads

Convert Flutter widget files into compact YAML optimized for LLM token consumption, and back again. Pipe syntax, CSS-like shorthands, arrow callbacks.

Repository (GitHub)
View/report issues
Contributing

License

MIT (license)

Dependencies

analyzer, args, path, watcher

More

Packages that depend on flutter_2yaml