Dart & Flutter Code Formatter

Paste a Flutter widget, a model class or a whole library file and get the layout dart format produces. It runs as WebAssembly in the page, with no SDK download needed.

Input

Settings

History

Load from URL

dart_style is the Dart formatter

Dart has one official formatter. dart format on the command line, Format Document in VS Code and Reformat Code in Android Studio all call the same library, dart_style, maintained by the Dart team. This page runs that library compiled to WebAssembly, so the output is what your IDE would produce, which is handy on a machine without the Flutter SDK, in a code review, or when tidying a snippet from an issue tracker. The code is formatted locally and never uploaded.

Like gofmt, dart_style deliberately has almost no configuration. Indentation is always 2 spaces, so the toolbar’s Indent setting has no effect here, and lines are wrapped at 80 columns, the Dart default. Since Dart 3.7 a project can raise that limit with formatter: page_width: in analysis_options.yaml; if yours does, expect lines between 80 and that width to be split here but not in your IDE.

The tall style and trailing commas

Recent versions of dart_style use the “tall” style introduced with Dart 3.7, and that is what you get here. The big change from the older formatter is how it treats trailing commas: the formatter now owns them.

  • When an argument list, collection or parameter list has to be split across lines, a trailing comma is added after the last element, and the closing bracket goes on its own line.
  • When a construct fits on one line, an existing trailing comma is removed and the construct is joined, so [\n 1,\n 2,\n] becomes [1, 2].

If you learned Flutter when a trailing comma was the trick to force a widget tree onto multiple lines, that trick no longer works: the formatter decides purely by line length. Deeply nested widget trees still end up vertical, because they rarely fit in 80 columns.

To keep a hand-formatted region, such as a matrix or a lookup table, wrap it in // dart format off and // dart format on comments.

What it leaves alone

dart_style only changes whitespace and commas, including blank lines: class declarations are separated from their neighbours by an empty line, as in the counter example below. It does not swap double quotes for single ones (that is the prefer_single_quotes lint), sort imports, add const or remove unused code; those are dart fix and analyzer jobs. Modern syntax parses fine: records, patterns and destructuring, switch expressions, sealed and final classes, class modifiers and extension types.

Input with import 'package:, a void main() or a Widget build( method is recognised automatically. Ctrl/Cmd+Enter reformats the widget tree and Ctrl/Cmd+Shift+C grabs it.

When parsing fails

dart_style refuses to format code it cannot parse, and the message is always the same: “Could not format because the source could not be parsed:”. The useful part is the position, which the editor highlights. Missing semicolons, unclosed braces and unterminated strings are the usual causes. The message does not say which of these it is, so start at the highlighted column and work backwards.

Examples

Flutter counter widget

The widget tree is split one child per line, with trailing commas added after each argument list that had to wrap.

Input
import 'package:flutter/material.dart';
class Counter extends StatefulWidget{const Counter({super.key,this.step=1});final int step;@override State<Counter> createState()=>_CounterState();}
class _CounterState extends State<Counter>{int count=0;
@override Widget build(BuildContext context){return Column(children:[Text('Count: $count'),ElevatedButton(onPressed:()=>setState(()=>count+=widget.step),child:const Text('Add'))]);}}
Output
import 'package:flutter/material.dart';

class Counter extends StatefulWidget {
  const Counter({super.key, this.step = 1});
  final int step;
  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int count = 0;
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: $count'),
        ElevatedButton(
          onPressed: () => setState(() => count += widget.step),
          child: const Text('Add'),
        ),
      ],
    );
  }
}
Open this example in the tool

Dart 3 records, patterns and sealed classes

The switch expression gets one case per line, and the record type and list pattern are spaced consistently.

Input
sealed class Shape{}
class Circle extends Shape{final double r;Circle(this.r);}
class Square extends Shape{final double side;Square(this.side);}
double area(Shape s)=>switch(s){Circle(:final r)=>3.14159*r*r,Square(:final side)=>side*side};
(String,int) parseLine(String line){final [name,qty]=line.split(',');return (name,int.parse(qty));}
extension type OrderId(String value){}
Output
sealed class Shape {}

class Circle extends Shape {
  final double r;
  Circle(this.r);
}

class Square extends Shape {
  final double side;
  Square(this.side);
}

double area(Shape s) => switch (s) {
  Circle(:final r) => 3.14159 * r * r,
  Square(:final side) => side * side,
};
(String, int) parseLine(String line) {
  final [name, qty] = line.split(',');
  return (name, int.parse(qty));
}

extension type OrderId(String value) {}
Open this example in the tool

Async HTTP call with a needless trailing comma

The trailing comma after page is dropped because the signature fits on one line, while the long get call is wrapped and given one.

Input
Future<List<Order>> fetchOrders(http.Client client,{int page=1,}) async{final res=await client.get(Uri.parse('https://api.example.com/orders?page=$page'));if(res.statusCode!=200){throw HttpException('status ${res.statusCode}');}final data=jsonDecode(res.body) as List<dynamic>;return data.map((e)=>Order.fromJson(e as Map<String,dynamic>)).toList();}
Output
Future<List<Order>> fetchOrders(http.Client client, {int page = 1}) async {
  final res = await client.get(
    Uri.parse('https://api.example.com/orders?page=$page'),
  );
  if (res.statusCode != 200) {
    throw HttpException('status ${res.statusCode}');
  }
  final data = jsonDecode(res.body) as List<dynamic>;
  return data.map((e) => Order.fromJson(e as Map<String, dynamic>)).toList();
}
Open this example in the tool

Common errors and how to fix them

ErrorCauseFix
Could not format because the source could not be parsed:A statement is missing its semicolon, as in print(‘x’) }. Dart requires a semicolon after every statement, including the last one in a block.Add the ; at the highlighted position.
Could not format because the source could not be parsed: (at the end of the input)When the highlighted position is the very end of the code, a class, function or widget tree is missing a closing } or ).Count brackets in the last widget or method you edited and add the missing closer.
Could not format because the source could not be parsed: (inside a string)A string literal is not closed, so the rest of the line is read as text. Single and double quoted strings cannot span lines.Close the quote, or use a triple-quoted string for multi-line text.
My trailing commas disappearedNot an error. The tall style removes trailing commas from constructs that fit on one line.Let the formatter decide, or wrap the region in // dart format off and // dart format on if the layout matters.

Frequently asked questions

Is this the same as dart format?

Yes. It is the dart_style library that dart format uses, so the output matches a current SDK with the default 80-column page width.

Can I change the indentation to 4 spaces?

No. dart_style always uses 2-space indentation and has no setting for it, so the toolbar Indent choice is ignored for Dart.

Why does the formatter add and remove trailing commas?

Since the tall style arrived in Dart 3.7, trailing commas follow the layout: added when a list splits, removed when it fits on one line. Older Flutter advice about adding commas to force line breaks no longer applies.

Does it work for Flutter widget code?

Yes. Flutter code is ordinary Dart, and long widget trees are split into the familiar nested layout with one argument per line.

Does it change quotes or sort imports?

No. Quotes, import order and other style choices covered by lints are left as written.

Related tools