Move every in-body declaring constructor to a primary.
diff --git a/lib/src/back_end/code.dart b/lib/src/back_end/code.dart
index 82069db..126ea83 100644
--- a/lib/src/back_end/code.dart
+++ b/lib/src/back_end/code.dart
@@ -132,50 +132,42 @@
 }
 
 /// A [Code] object for a newline followed by any leading indentation.
-final class _NewlineCode extends Code {
-  this({
-    /// Whether a blank line (two newlines) should be written.
-    required final bool _blank,
+final class _NewlineCode({
+  /// Whether a blank line (two newlines) should be written.
+  required final bool _blank,
 
-    /// The number of spaces of indentation after this newline.
-    required final int _indent,
-  });
-}
+  /// The number of spaces of indentation after this newline.
+  required final int _indent,
+}) extends Code;
 
 /// A [Code] object for literal source text.
-final class _TextCode extends Code {
-  this(final String _text);
-}
+final class _TextCode(final String _text) extends Code;
 
 /// Marks the location of the beginning or end of a selection as occurring
 /// [_offset] characters past the point where this marker object appears in the
 /// list of [Code] objects.
-final class _MarkerCode extends Code {
-  this(
-    /// What kind of selection endpoint is being marked.
-    final _Marker _marker,
+final class _MarkerCode(
+  /// What kind of selection endpoint is being marked.
+  final _Marker _marker,
 
-    /// The number of characters into the next [Code] object where the marker
-    /// should appear in the resulting output.
-    final int _offset,
-  );
-}
+  /// The number of characters into the next [Code] object where the marker
+  /// should appear in the resulting output.
+  final int _offset,
+) extends Code;
 
-final class _EnableFormattingCode extends Code {
-  this(
-    /// Whether this comment disables formatting (`format off`) or re-enables it
-    /// (`format on`).
-    final bool _enabled,
+final class _EnableFormattingCode(
+  /// Whether this comment disables formatting (`format off`) or re-enables it
+  /// (`format on`).
+  final bool _enabled,
 
-    /// The number of code points from the beginning of the unformatted source
-    /// where the unformatted code should begin or end.
-    ///
-    /// If this piece is for `// dart format off`, then the offset is just past
-    /// the `off`. If this piece is for `// dart format on`, it points to just
-    /// before `//`.
-    final int _sourceOffset,
-  );
-}
+  /// The number of code points from the beginning of the unformatted source
+  /// where the unformatted code should begin or end.
+  ///
+  /// If this piece is for `// dart format off`, then the offset is just past
+  /// the `off`. If this piece is for `// dart format on`, it points to just
+  /// before `//`.
+  final int _sourceOffset,
+) extends Code;
 
 /// Which selection marker is pointed to by a [_MarkerCode].
 enum _Marker { start, end }
diff --git a/lib/src/back_end/solver.dart b/lib/src/back_end/solver.dart
index 4c11fcf..11a0442 100644
--- a/lib/src/back_end/solver.dart
+++ b/lib/src/back_end/solver.dart
@@ -42,9 +42,16 @@
 ///     use it across different Solutions. This enables us to both divide and
 ///     conquer the Piece tree and solve portions separately, while also
 ///     reusing work across different solutions.
-final class Solver {
+final class Solver(
+  final SolutionCache _cache, {
+  required final int _pageWidth,
+
+  /// The number of spaces of indentation on the first line.
+  int final int _leadingIndent = 0,
+  int? subsequentIndent,
+}) {
   /// The number of spaces of indentation on all lines after the first.
-  final int _subsequentIndent;
+  final int _subsequentIndent = subsequentIndent ?? _leadingIndent;
 
   final PriorityQueue<Solution> _queue = PriorityQueue();
 
@@ -53,14 +60,7 @@
   /// The first line is indented by [leadingIndent] spaces and all lines after
   /// that are indented by [subsequentIndent]. If [subsequentIndent] is omitted,
   /// defaults to [leadingIndent].
-  this(
-    final SolutionCache _cache, {
-    required final int _pageWidth,
-
-    /// The number of spaces of indentation on the first line.
-    int final int _leadingIndent = 0,
-    int? subsequentIndent,
-  }) : _subsequentIndent = subsequentIndent ?? leadingIndent;
+  this;
 
   /// Finds the best set of line splits for [root] piece and returns the
   /// resulting formatted code.
diff --git a/lib/src/cli/format_command.dart b/lib/src/cli/format_command.dart
index d8651a9..e9f5e66 100644
--- a/lib/src/cli/format_command.dart
+++ b/lib/src/cli/format_command.dart
@@ -19,17 +19,16 @@
   String get name => 'format';
 
   @override
+  final String category;
+
+  @override
   String get description => 'Idiomatically format Dart source code.';
 
   @override
   String get invocation =>
       '${runner!.executableName} $name [options...] <files or directories...>';
 
-  this({
-    bool verbose = false,
-    @override
-    final String category = '',
-  }) {
+  new({bool verbose = false, this.category = ''}) {
     argParser.addFlag(
       'verbose',
       abbr: 'v',
diff --git a/lib/src/front_end/chain_builder.dart b/lib/src/front_end/chain_builder.dart
index 885db77..fc4405c 100644
--- a/lib/src/front_end/chain_builder.dart
+++ b/lib/src/front_end/chain_builder.dart
@@ -42,7 +42,16 @@
 ///
 /// This lets us create a single [ChainPiece] for the entire series of dotted
 /// operations, so that we can control splitting them or not as a unit.
-final class ChainBuilder {
+final class ChainBuilder(
+  final PieceFactory _visitor,
+
+  /// The outermost expression being converted to a chain.
+  ///
+  /// If it's a [CascadeExpression], then the chain is the cascade sections.
+  /// Otherwise, it's some kind of method call or property access and the chain
+  /// is the nested series of selector subexpressions.
+  final Expression _root,
+) {
   /// The left-most target of the chain.
   late Piece _target;
 
@@ -61,16 +70,7 @@
   /// The dotted property accesses and method calls following the target.
   final List<ChainCall> _calls = [];
 
-  this(
-    final PieceFactory _visitor,
-
-    /// The outermost expression being converted to a chain.
-    ///
-    /// If it's a [CascadeExpression], then the chain is the cascade sections.
-    /// Otherwise, it's some kind of method call or property access and the chain
-    /// is the nested series of selector subexpressions.
-    final Expression _root,
-  ) {
+  this {
     if (_root case CascadeExpression cascade) {
       _visitTarget(cascade.target, cascadeTarget: true);
 
diff --git a/lib/src/front_end/delimited_list_builder.dart b/lib/src/front_end/delimited_list_builder.dart
index 9ebeaca..246e805 100644
--- a/lib/src/front_end/delimited_list_builder.dart
+++ b/lib/src/front_end/delimited_list_builder.dart
@@ -18,7 +18,10 @@
 /// delimiter token. Then call [add()] for each [AstNode] that is inside the
 /// delimiters. The [rightBracket()] with the closing delimiter and finally
 /// [build()] to get the resulting [ListPiece].
-final class DelimitedListBuilder {
+final class DelimitedListBuilder(
+  final PieceFactory _visitor, [
+  final ListStyle _style = const ListStyle(),
+]) {
   /// The opening bracket before the elements, if any.
   Piece? _leftBracket;
 
@@ -43,10 +46,7 @@
 
   /// Creates a new [DelimitedListBuilder] for an argument list, collection
   /// literal, etc.
-  this(
-    final PieceFactory _visitor, [
-    final ListStyle _style = const ListStyle(),
-  ]);
+  this;
 
   /// Creates the final [ListPiece] out of the added brackets, delimiters,
   /// elements, and style.
diff --git a/lib/src/piece/assign.dart b/lib/src/piece/assign.dart
index a49c3fb..a1b9736 100644
--- a/lib/src/piece/assign.dart
+++ b/lib/src/piece/assign.dart
@@ -61,7 +61,30 @@
 ///     var [unsplitBlock] =
 ///         longOperand +
 ///             anotherOperand;
-final class AssignPiece extends Piece {
+final class AssignPiece(
+  /// The left-hand side of the operation and the operator itself.
+  final Piece _left,
+
+  /// The right-hand side of the operation.
+  final Piece _right, {
+
+  /// Whether the piece should have a cost for splitting at the operator.
+  ///
+  /// Usually true because it's generally better to block split inside the
+  /// operands when possible. But false for `=>` when the expression has a form
+  /// where we'd rather keep the expression itself unsplit as in:
+  ///
+  ///     // Don't avoid split:
+  ///     makeStuff() => [
+  ///       element,
+  ///       element,
+  ///     ];
+  ///
+  ///     // Avoid split:
+  ///     doThing() =>
+  ///       thingToDo(argument, argument);
+  final bool _avoidSplit = true,
+}) extends Piece {
   /// Allow the right-hand side to block split.
   static const State _blockOrHeadlineSplitRight = State(1, cost: 0);
 
@@ -69,31 +92,6 @@
   /// split.
   static const State _blockSplitLeft = State(2);
 
-  this(
-    /// The left-hand side of the operation and the operator itself.
-    final Piece _left,
-
-    /// The right-hand side of the operation.
-    final Piece _right, {
-
-    /// Whether the piece should have a cost for splitting at the operator.
-    ///
-    /// Usually true because it's generally better to block split inside the
-    /// operands when possible. But false for `=>` when the expression has a form
-    /// where we'd rather keep the expression itself unsplit as in:
-    ///
-    ///     // Don't avoid split:
-    ///     makeStuff() => [
-    ///       element,
-    ///       element,
-    ///     ];
-    ///
-    ///     // Avoid split:
-    ///     doThing() =>
-    ///       thingToDo(argument, argument);
-    final bool _avoidSplit = true
-  });
-
   @override
   List<State> get additionalStates => [
     _blockOrHeadlineSplitRight,
diff --git a/lib/src/piece/assign_3_dot_7.dart b/lib/src/piece/assign_3_dot_7.dart
index b1797c9..2f5adb9 100644
--- a/lib/src/piece/assign_3_dot_7.dart
+++ b/lib/src/piece/assign_3_dot_7.dart
@@ -68,7 +68,57 @@
 ///     var [unsplitBlock] =
 ///         longOperand +
 ///             anotherOperand;
-final class AssignPiece3Dot7 extends Piece {
+final class AssignPiece3Dot7(
+  /// The `=` or other operator.
+  final Piece _operator,
+
+  // TODO(rnystrom): If it wasn't for the need to constrain [_left] to split
+  // in [applyConstraints()], we could write the operator into the same piece
+  // as [_left]. In the common case where the AssignPiece is for a named
+  // argument, the name and `:` would then end up in a single atomic
+  // [CodePiece].
+
+  /// The right-hand side of the operation.
+  final Piece _right, {
+
+  /// The left-hand side of the operation.
+  final Piece? _left,
+
+  /// If `true`, then the left side supports being block-formatted, like:
+  ///
+  ///     var [
+  ///       element1,
+  ///       element2,
+  ///     ] = value;
+  final bool _canBlockSplitLeft = false,
+
+  /// If `true` then the right side supports being block-formatted, like:
+  ///
+  ///     var list = [
+  ///       element1,
+  ///       element2,
+  ///     ];
+  final bool _canBlockSplitRight = false,
+
+  /// If `true` then prefer to split at the operator instead of block splitting
+  /// the right side.
+  ///
+  /// This is `true` for `=>` functions whose body is a function call. This
+  /// keeps the called function next to its arguments instead having the
+  /// function name stick to the `=>` while the arguments split. In other words,
+  /// prefer:
+  ///
+  ///     someMethod() =>
+  ///         someFunction(argument, another);
+  ///
+  /// Over:
+  ///
+  ///     someMethod() => someFunction(
+  ///       argument,
+  ///       another,
+  ///     );
+  final bool _avoidBlockSplitRight = false,
+}) extends Piece {
   /// Force the block left-hand side to split and allow the right-hand side to
   /// split.
   static const State _blockSplitLeft = State(1);
@@ -79,58 +129,6 @@
   /// Split at the operator.
   static const State _atOperator = State(3);
 
-  this(
-    /// The `=` or other operator.
-    final Piece _operator,
-
-    // TODO(rnystrom): If it wasn't for the need to constrain [_left] to split
-    // in [applyConstraints()], we could write the operator into the same piece
-    // as [_left]. In the common case where the AssignPiece is for a named
-    // argument, the name and `:` would then end up in a single atomic
-    // [CodePiece].
-
-    /// The right-hand side of the operation.
-    final Piece _right, {
-
-    /// The left-hand side of the operation.
-    final Piece? _left,
-
-    /// If `true`, then the left side supports being block-formatted, like:
-    ///
-    ///     var [
-    ///       element1,
-    ///       element2,
-    ///     ] = value;
-    final bool _canBlockSplitLeft = false,
-
-    /// If `true` then the right side supports being block-formatted, like:
-    ///
-    ///     var list = [
-    ///       element1,
-    ///       element2,
-    ///     ];
-    final bool _canBlockSplitRight = false,
-
-    /// If `true` then prefer to split at the operator instead of block splitting
-    /// the right side.
-    ///
-    /// This is `true` for `=>` functions whose body is a function call. This
-    /// keeps the called function next to its arguments instead having the
-    /// function name stick to the `=>` while the arguments split. In other words,
-    /// prefer:
-    ///
-    ///     someMethod() =>
-    ///         someFunction(argument, another);
-    ///
-    /// Over:
-    ///
-    ///     someMethod() => someFunction(
-    ///       argument,
-    ///       another,
-    ///     );
-    final bool _avoidBlockSplitRight = false,
-  });
-
   @override
   List<State> get additionalStates => [
     // If at least one operand can block split, allow splitting in operands
diff --git a/lib/src/piece/case.dart b/lib/src/piece/case.dart
index c626d27..e1097f2 100644
--- a/lib/src/piece/case.dart
+++ b/lib/src/piece/case.dart
@@ -6,7 +6,37 @@
 import 'piece.dart';
 
 /// Piece for a case pattern, guard, and body in a switch expression.
-final class CaseExpressionPiece extends Piece {
+final class CaseExpressionPiece(
+  /// The pattern the value is matched against.
+  final Piece _pattern,
+
+  /// If there is a `when` clause, that clause.
+  final Piece? _guard,
+
+  /// The `=>` token separating the pattern and body.
+  final Piece _arrow,
+
+  /// The case body expression.
+  final Piece _body, {
+
+  /// Whether the pattern can be block formatted.
+  required final bool _canBlockSplitPattern,
+
+  /// Whether the outermost pattern is a logical-or pattern.
+  ///
+  /// We format these specially to make them look like parallel cases:
+  ///
+  ///     switch (obj) {
+  ///       firstPattern ||
+  ///       secondPattern ||
+  ///       thirdPattern =>
+  ///         body;
+  ///     }
+  required final bool _patternIsLogicalOr,
+
+  /// Whether the body expression can be block formatted.
+  final bool _canBlockSplitBody = true,
+}) extends Piece {
   /// Split inside the body, which must be block shaped, like:
   ///
   ///     pattern => function(
@@ -20,38 +50,6 @@
   /// Split before the `when` guard clause and after the `=>`.
   static const State _beforeWhenAndBody = State(3);
 
-  this(
-    /// The pattern the value is matched against.
-    final Piece _pattern,
-
-    /// If there is a `when` clause, that clause.
-    final Piece? _guard,
-
-    /// The `=>` token separating the pattern and body.
-    final Piece _arrow,
-
-    /// The case body expression.
-    final Piece _body, {
-
-    /// Whether the pattern can be block formatted.
-    required final bool _canBlockSplitPattern,
-
-    /// Whether the outermost pattern is a logical-or pattern.
-    ///
-    /// We format these specially to make them look like parallel cases:
-    ///
-    ///     switch (obj) {
-    ///       firstPattern ||
-    ///       secondPattern ||
-    ///       thirdPattern =>
-    ///         body;
-    ///     }
-    required final bool _patternIsLogicalOr,
-
-    /// Whether the body expression can be block formatted.
-    final bool _canBlockSplitBody = true,
-  });
-
   @override
   List<State> get additionalStates => [
     if (_canBlockSplitBody) _blockSplitBody,
diff --git a/lib/src/piece/clause.dart b/lib/src/piece/clause.dart
index a390cb1..a26efc9 100644
--- a/lib/src/piece/clause.dart
+++ b/lib/src/piece/clause.dart
@@ -60,7 +60,13 @@
 ///
 /// This ensures that when any wrapping occurs, the keywords are always at the
 /// beginning of the line.
-final class ClausePiece extends Piece {
+final class ClausePiece(
+  /// The leading construct the clauses are applied to: a class declaration,
+  /// import directive, etc.
+  final Piece _header,
+  final List<Piece> _clauses, {
+  bool allowLeadingClause = false,
+}) extends Piece {
   /// State where we split between the clauses but not before the first one.
   static const State _betweenClauses = State(1);
 
@@ -75,16 +81,7 @@
   ///         implements OtherThing {
   ///       ...
   ///     }
-  final bool _allowLeadingClause;
-
-  this(
-    /// The leading construct the clauses are applied to: a class declaration,
-    /// import directive, etc.
-    final Piece _header,
-    final List<Piece> _clauses, {
-    bool allowLeadingClause = false,
-  })
-    : _allowLeadingClause = allowLeadingClause && _clauses.length > 1;
+  final bool _allowLeadingClause = allowLeadingClause && _clauses.length > 1;
 
   @override
   List<State> get additionalStates => [
diff --git a/lib/src/piece/constructor.dart b/lib/src/piece/constructor.dart
index 932fa6a..d681199 100644
--- a/lib/src/piece/constructor.dart
+++ b/lib/src/piece/constructor.dart
@@ -47,41 +47,39 @@
 ///     ]) : firstInitializer = 1,
 ///          second = 2;
 ///     //  ^ Five spaces of indentation.
-final class ConstructorPiece extends Piece {
+final class ConstructorPiece(
+  /// The leading keywords, class name, and constructor name.
+  final Piece _header,
+
+  /// The constructor parameter list.
+  final Piece _parameters,
+
+  /// The constructor body.
+  final Piece _body, {
+
+  /// Whether there are parameters or comments inside the parameter list.
+  ///
+  /// If so, then we allow splitting the parameter list while leaving the `:`
+  /// on the same line as the `)`.
+  required final bool _canSplitParameters;
+
+  /// Whether the parameter list contains a `]` or `}` closing delimiter before
+  /// the `)`.
+  required final bool _hasOptionalParameter,
+
+  /// If this is a redirecting constructor, the redirection clause.
+  final Piece? _redirect,
+
+  /// If there are initializers, the `:` before them.
+  final Piece? _initializerSeparator,
+
+  /// The constructor initializers, if there are any.
+  final Piece? _initializers,
+}) extends Piece {
   static const _splitBeforeInitializers = State(1, cost: 1);
 
   static const _splitBetweenInitializers = State(2, cost: 2);
 
-  this(
-    /// The leading keywords, class name, and constructor name.
-    final Piece _header,
-
-    /// The constructor parameter list.
-    final Piece _parameters,
-
-    /// The constructor body.
-    final Piece _body, {
-
-    /// Whether there are parameters or comments inside the parameter list.
-    ///
-    /// If so, then we allow splitting the parameter list while leaving the `:`
-    /// on the same line as the `)`.
-    required final bool _canSplitParameters;
-
-    /// Whether the parameter list contains a `]` or `}` closing delimiter before
-    /// the `)`.
-    required final bool _hasOptionalParameter,
-
-    /// If this is a redirecting constructor, the redirection clause.
-    final Piece? _redirect,
-
-    /// If there are initializers, the `:` before them.
-    final Piece? _initializerSeparator,
-
-    /// The constructor initializers, if there are any.
-    final Piece? _initializers,
-  });
-
   @override
   List<State> get additionalStates => [
     if (_initializers != null) _splitBeforeInitializers,
diff --git a/lib/src/piece/for.dart b/lib/src/piece/for.dart
index 5eac459..cc8cc2e 100644
--- a/lib/src/piece/for.dart
+++ b/lib/src/piece/for.dart
@@ -5,30 +5,28 @@
 import 'piece.dart';
 
 /// A piece for the `for (...)` part of a for statement or element.
-final class ForPiece extends Piece {
-  this(
-    /// The `for` keyword.
-    final Piece _forKeyword,
+final class ForPiece(
+  /// The `for` keyword.
+  final Piece _forKeyword,
 
-    /// The part inside `( ... )`, including the parentheses themselves, at the
-    /// header of a for statement.
-    final Piece _parts, {
+  /// The part inside `( ... )`, including the parentheses themselves, at the
+  /// header of a for statement.
+  final Piece _parts, {
 
-    /// Whether the contents of the parentheses in the `for (...)` should be
-    /// expression indented or not.
-    ///
-    /// This is usually not necessary because the contents will either be a
-    /// [ListPiece] which adds its own block indentation, or an [AssignPiece]
-    /// which indents as necessary. But in the rare case the for-parts is a
-    /// variable or pattern variable declaration with metadata that splits, we
-    /// need to ensure that the metadata is indented, as in:
-    ///
-    ///     for (@LongAnnotation
-    ///         @AnotherAnnotation
-    ///         var element in list) { ... }
-    required final bool _indent,
-  });
-
+  /// Whether the contents of the parentheses in the `for (...)` should be
+  /// expression indented or not.
+  ///
+  /// This is usually not necessary because the contents will either be a
+  /// [ListPiece] which adds its own block indentation, or an [AssignPiece]
+  /// which indents as necessary. But in the rare case the for-parts is a
+  /// variable or pattern variable declaration with metadata that splits, we
+  /// need to ensure that the metadata is indented, as in:
+  ///
+  ///     for (@LongAnnotation
+  ///         @AnotherAnnotation
+  ///         var element in list) { ... }
+  required final bool _indent,
+}) extends Piece {
   @override
   void format(CodeWriter writer, State state) {
     writer.format(_forKeyword);
diff --git a/lib/src/piece/if_case.dart b/lib/src/piece/if_case.dart
index 9256087..3e3b72e 100644
--- a/lib/src/piece/if_case.dart
+++ b/lib/src/piece/if_case.dart
@@ -35,7 +35,19 @@
 ///     if (obj
 ///         case pattern
 ///         when cond) ...
-final class IfCasePiece extends Piece {
+final class IfCasePiece(
+  /// The value expression being matched.
+  final Piece _value,
+
+  /// The pattern the value is matched against along with the leading `case`.
+  final Piece _pattern,
+
+  /// If there is a `when` clause, that clause.
+  final Piece? _guard, {
+
+  /// Whether the pattern can be block formatted.
+  required final bool _canBlockSplitPattern,
+}) extends Piece {
   /// Split before the `when` guard clause.
   static const State _beforeWhen = State(1);
 
@@ -45,20 +57,6 @@
   /// Split before the `case` pattern clause and the `when` guard clause.
   static const State _beforeCaseAndWhen = State(3);
 
-  this(
-    /// The value expression being matched.
-    final Piece _value,
-
-    /// The pattern the value is matched against along with the leading `case`.
-    final Piece _pattern,
-
-    /// If there is a `when` clause, that clause.
-    final Piece? _guard, {
-
-    /// Whether the pattern can be block formatted.
-    required final bool _canBlockSplitPattern,
-  });
-
   @override
   List<State> get additionalStates => [
     if (_guard != null) _beforeWhen,
diff --git a/lib/src/piece/list.dart b/lib/src/piece/list.dart
index ed4adc6..6dd35bd 100644
--- a/lib/src/piece/list.dart
+++ b/lib/src/piece/list.dart
@@ -38,7 +38,34 @@
 ///
 /// ListPieces are usually constructed using [createList()] or
 /// [DelimitedListBuilder].
-final class ListPiece extends Piece {
+final class ListPiece(
+  /// The opening bracket before the elements, if any.
+  final Piece? _before,
+
+  /// The list of elements.
+  final List<ListElementPiece> _elements,
+
+  /// The elements that should have a blank line preserved between them and the
+  /// next piece.
+  final Set<ListElementPiece> _blanksAfter,
+
+  /// The closing bracket after the elements, if any.
+  final Piece? _after,
+
+  /// The details of how this particular list should be formatted.
+  final ListStyle _style, {
+
+  /// The index of the last element in [_elements] that has content and isn't
+  /// a comment, or `-1` if all elements are comments.
+  required final int _lastNonCommentElement,
+
+  /// Whether this list should have [Shape.block] when it splits.
+  ///
+  /// This is true for most lists, but false for some lists where we don't want
+  /// them to be treated as block-formatted in the surrounding context, mainly
+  /// type argument lists.
+  required final bool _isBlockShaped,
+}) extends Piece {
   /// Whether any element in this argument list can be block formatted.
   bool get hasBlockElement =>
       _elements.any((element) => element.allowNewlinesWhenUnsplit);
@@ -47,34 +74,7 @@
   ///
   /// [_elements] must not be empty. (If there are no elements, just concatenate
   /// the brackets directly.)
-  this(
-    /// The opening bracket before the elements, if any.
-    final Piece? _before,
-
-    /// The list of elements.
-    final List<ListElementPiece> _elements,
-
-    /// The elements that should have a blank line preserved between them and the
-    /// next piece.
-    final Set<ListElementPiece> _blanksAfter,
-
-    /// The closing bracket after the elements, if any.
-    final Piece? _after,
-
-    /// The details of how this particular list should be formatted.
-    final ListStyle _style, {
-
-    /// The index of the last element in [_elements] that has content and isn't
-    /// a comment, or `-1` if all elements are comments.
-    required final int _lastNonCommentElement,
-
-    /// Whether this list should have [Shape.block] when it splits.
-    ///
-    /// This is true for most lists, but false for some lists where we don't want
-    /// them to be treated as block-formatted in the surrounding context, mainly
-    /// type argument lists.
-    required final bool _isBlockShaped,
-  }) : assert(_elements.isNotEmpty) {
+  this : assert(_elements.isNotEmpty) {
     // For most elements, we know whether or not it will have a comma based
     // only on the comma style and its position in the list, so pin those here.
     for (var i = 0; i < _elements.length; i++) {
diff --git a/lib/src/piece/variable.dart b/lib/src/piece/variable.dart
index 877768a..eba3aa9 100644
--- a/lib/src/piece/variable.dart
+++ b/lib/src/piece/variable.dart
@@ -28,7 +28,20 @@
 ///     VeryLongTypeName
 ///     longVariableName = initializer,
 ///     anotherVariable = anotherInitializer;
-final class VariablePiece extends Piece {
+final class VariablePiece(
+  /// The leading keywords (`var`, `final`, `late`) and optional type
+  /// annotation.
+  final Piece _header;
+
+  /// Each individual variable being declared.
+  final List<Piece> _variables, {
+
+  /// Whether the variable declaration has a type annotation.
+  required final bool _hasType,
+
+  /// Whether we are using the 3.7 style.
+  required final bool _is3Dot7,
+}) extends Piece {
   /// Split between each variable in a multiple variable declaration.
   static const State _betweenVariables = State(1);
 
@@ -39,20 +52,7 @@
   ///
   /// The [hasType] parameter should be `true` if the variable declaration has
   /// a type annotation.
-  this(
-    /// The leading keywords (`var`, `final`, `late`) and optional type
-    /// annotation.
-    final Piece _header;
-
-    /// Each individual variable being declared.
-    final List<Piece> _variables, {
-
-    /// Whether the variable declaration has a type annotation.
-    required final bool _hasType,
-
-    /// Whether we are using the 3.7 style.
-    required final bool _is3Dot7,
-  });
+  this;
 
   @override
   List<State> get additionalStates => [