Enable prefer_final_locals lint rule (dart-lang/markdown#415)

diff --git a/pkgs/markdown/analysis_options.yaml b/pkgs/markdown/analysis_options.yaml
index 2f0f043..17b1083 100644
--- a/pkgs/markdown/analysis_options.yaml
+++ b/pkgs/markdown/analysis_options.yaml
@@ -31,3 +31,4 @@
     - package_api_docs
     - test_types_in_equals
     - throw_in_finally
+    - prefer_final_locals
diff --git a/pkgs/markdown/benchmark/benchmark.dart b/pkgs/markdown/benchmark/benchmark.dart
index f3e934c..4c4d61f 100644
--- a/pkgs/markdown/benchmark/benchmark.dart
+++ b/pkgs/markdown/benchmark/benchmark.dart
@@ -19,7 +19,7 @@
   // Run the benchmark several times. This ensures the VM is warmed up and lets
   // us see how much variance there is.
   for (var i = 0; i <= numTrials; i++) {
-    var start = DateTime.now();
+    final start = DateTime.now();
 
     // For a single benchmark, convert the source multiple times.
     late String result;
@@ -27,7 +27,7 @@
       result = markdownToHtml(source);
     }
 
-    var elapsed =
+    final elapsed =
         DateTime.now().difference(start).inMilliseconds / runsPerTrial;
 
     // Keep track of the best run so far.
@@ -51,7 +51,7 @@
 }
 
 String _loadFile(String name) {
-  var path = p.join(p.dirname(p.fromUri(Platform.script)), name);
+  final path = p.join(p.dirname(p.fromUri(Platform.script)), name);
   return File(path).readAsStringSync();
 }
 
diff --git a/pkgs/markdown/bin/markdown.dart b/pkgs/markdown/bin/markdown.dart
index fd4eee2..f3b4ad3 100644
--- a/pkgs/markdown/bin/markdown.dart
+++ b/pkgs/markdown/bin/markdown.dart
@@ -16,7 +16,7 @@
 };
 
 Future<void> main(List<String> args) async {
-  var parser = ArgParser()
+  final parser = ArgParser()
     ..addFlag('help', negatable: false, help: 'Print help text and exit')
     ..addFlag('version', negatable: false, help: 'Print version and exit')
     ..addOption('extension-set',
@@ -29,7 +29,7 @@
           'GitHubFlavored': 'Parse like GitHub Flavored Markdown',
           'GitHubWeb': 'Parse like GitHub\'s Markdown-enabled web input fields',
         });
-  var results = parser.parse(args);
+  final results = parser.parse(args);
 
   if (results['help'] as bool) {
     printUsage(parser);
@@ -41,7 +41,7 @@
     return;
   }
 
-  var extensionSet = extensionSets[results['extension-set']];
+  final extensionSet = extensionSets[results['extension-set']];
 
   if (results.rest.length > 1) {
     printUsage(parser);
@@ -51,13 +51,13 @@
 
   if (results.rest.length == 1) {
     // Read argument as a file path.
-    var input = File(results.rest.first).readAsStringSync();
+    final input = File(results.rest.first).readAsStringSync();
     print(markdownToHtml(input, extensionSet: extensionSet));
     return;
   }
 
   // Read from stdin.
-  var buffer = StringBuffer();
+  final buffer = StringBuffer();
   String? line;
   while ((line = stdin.readLineSync()) != null) {
     buffer.writeln(line);
diff --git a/pkgs/markdown/example/app.dart b/pkgs/markdown/example/app.dart
index ace4fa3..f9556c2 100644
--- a/pkgs/markdown/example/app.dart
+++ b/pkgs/markdown/example/app.dart
@@ -36,7 +36,7 @@
   versionSpan.text = 'v${md.version}';
   markdownInput.onKeyUp.listen(_renderMarkdown);
 
-  var savedMarkdown = window.localStorage['markdown'];
+  final savedMarkdown = window.localStorage['markdown'];
 
   if (savedMarkdown != null &&
       savedMarkdown.isNotEmpty &&
@@ -60,12 +60,12 @@
 }
 
 void _renderMarkdown([Event? event]) {
-  var markdown = markdownInput.value!;
+  final markdown = markdownInput.value!;
 
   htmlDiv.setInnerHtml(md.markdownToHtml(markdown, extensionSet: extensionSet),
       treeSanitizer: nullSanitizer);
 
-  for (var block in htmlDiv.querySelectorAll('pre code')) {
+  for (final block in htmlDiv.querySelectorAll('pre code')) {
     try {
       highlightElement(block);
     } catch (e) {
@@ -100,7 +100,7 @@
 }
 
 void _switchFlavor(Event e) {
-  var target = e.currentTarget as HtmlElement;
+  final target = e.currentTarget as HtmlElement;
   if (!target.attributes.containsKey('checked')) {
     if (basicRadio != target) {
       basicRadio.attributes.remove('checked');
diff --git a/pkgs/markdown/lib/src/block_parser.dart b/pkgs/markdown/lib/src/block_parser.dart
index 4b11b3d..06b7880 100644
--- a/pkgs/markdown/lib/src/block_parser.dart
+++ b/pkgs/markdown/lib/src/block_parser.dart
@@ -156,11 +156,11 @@
   }
 
   List<Node> parseLines() {
-    var blocks = <Node>[];
+    final blocks = <Node>[];
     while (!isDone) {
-      for (var syntax in blockSyntaxes) {
+      for (final syntax in blockSyntaxes) {
         if (syntax.canParse(this)) {
-          var block = syntax.parse(this);
+          final block = syntax.parse(this);
           if (block != null) blocks.add(block);
           break;
         }
@@ -187,10 +187,10 @@
 
   List<String?> parseChildLines(BlockParser parser) {
     // Grab all of the lines that form the block element.
-    var childLines = <String?>[];
+    final childLines = <String?>[];
 
     while (!parser.isDone) {
-      var match = pattern.firstMatch(parser.current);
+      final match = pattern.firstMatch(parser.current);
       if (match == null) break;
       childLines.add(match[1]);
       parser.advance();
@@ -243,7 +243,7 @@
     if (!_interperableAsParagraph(parser.current)) return false;
     var i = 1;
     while (true) {
-      var nextLine = parser.peek(i);
+      final nextLine = parser.peek(i);
       if (nextLine == null) {
         // We never reached an underline.
         return false;
@@ -261,10 +261,10 @@
 
   @override
   Node parse(BlockParser parser) {
-    var lines = <String>[];
+    final lines = <String>[];
     String? tag;
     while (!parser.isDone) {
-      var match = _setextPattern.firstMatch(parser.current);
+      final match = _setextPattern.firstMatch(parser.current);
       if (match == null) {
         // More text.
         lines.add(parser.current);
@@ -278,7 +278,7 @@
       }
     }
 
-    var contents = UnparsedContent(lines.join('\n').trimRight());
+    final contents = UnparsedContent(lines.join('\n').trimRight());
 
     return Element(tag!, [contents]);
   }
@@ -301,7 +301,7 @@
 
   @override
   Node parse(BlockParser parser) {
-    var element = super.parse(parser) as Element;
+    final element = super.parse(parser) as Element;
     element.generatedId = BlockSyntax.generateAnchorHash(element);
     return element;
   }
@@ -316,10 +316,10 @@
 
   @override
   Node parse(BlockParser parser) {
-    var match = pattern.firstMatch(parser.current)!;
+    final match = pattern.firstMatch(parser.current)!;
     parser.advance();
-    var level = match[1]!.length;
-    var contents = UnparsedContent(match[2]!.trim());
+    final level = match[1]!.length;
+    final contents = UnparsedContent(match[2]!.trim());
     return Element('h$level', [contents]);
   }
 }
@@ -330,7 +330,7 @@
 
   @override
   Node parse(BlockParser parser) {
-    var element = super.parse(parser) as Element;
+    final element = super.parse(parser) as Element;
     element.generatedId = BlockSyntax.generateAnchorHash(element);
     return element;
   }
@@ -382,11 +382,11 @@
   @override
   List<String> parseChildLines(BlockParser parser) {
     // Grab all of the lines that form the blockquote, stripping off the ">".
-    var childLines = <String>[];
+    final childLines = <String>[];
 
     bool encounteredCodeBlock = false;
     while (!parser.isDone) {
-      var match = pattern.firstMatch(parser.current);
+      final match = pattern.firstMatch(parser.current);
       if (match != null) {
         final line = match[1]!;
         childLines.add(line);
@@ -416,10 +416,10 @@
 
   @override
   Node parse(BlockParser parser) {
-    var childLines = parseChildLines(parser);
+    final childLines = parseChildLines(parser);
 
     // Recursively parse the contents of the blockquote.
-    var children = BlockParser(childLines, parser.document).parseLines();
+    final children = BlockParser(childLines, parser.document).parseLines();
 
     return Element('blockquote', children);
   }
@@ -437,17 +437,17 @@
 
   @override
   List<String?> parseChildLines(BlockParser parser) {
-    var childLines = <String?>[];
+    final childLines = <String?>[];
 
     while (!parser.isDone) {
-      var match = pattern.firstMatch(parser.current);
+      final match = pattern.firstMatch(parser.current);
       if (match != null) {
         childLines.add(match[1]);
         parser.advance();
       } else {
         // If there's a codeblock, then a newline, then a codeblock, keep the
         // code blocks together.
-        var nextMatch =
+        final nextMatch =
             parser.next != null ? pattern.firstMatch(parser.next!) : null;
         if (parser.current.trim() == '' && nextMatch != null) {
           childLines.add('');
@@ -464,7 +464,7 @@
 
   @override
   Node parse(BlockParser parser) {
-    var childLines = parseChildLines(parser);
+    final childLines = parseChildLines(parser);
 
     // The Markdown tests expect a trailing newline.
     childLines.add('');
@@ -505,11 +505,11 @@
   List<String> parseChildLines(BlockParser parser, [String? endBlock]) {
     endBlock ??= '';
 
-    var childLines = <String>[];
+    final childLines = <String>[];
     parser.advance();
 
     while (!parser.isDone) {
-      var match = pattern.firstMatch(parser.current);
+      final match = pattern.firstMatch(parser.current);
       if (match == null || !match[1]!.startsWith(endBlock)) {
         childLines.add(parser.current);
         parser.advance();
@@ -525,11 +525,11 @@
   @override
   Node parse(BlockParser parser) {
     // Get the syntax identifier, if there is one.
-    var match = pattern.firstMatch(parser.current)!;
-    var endBlock = match.group(1);
+    final match = pattern.firstMatch(parser.current)!;
+    final endBlock = match.group(1);
     var infoString = match.group(2)!;
 
-    var childLines = parseChildLines(parser, endBlock);
+    final childLines = parseChildLines(parser, endBlock);
 
     // The Markdown tests expect a trailing newline.
     childLines.add('');
@@ -538,7 +538,7 @@
     if (parser.document.encodeHtml) {
       text = escapeHtml(text);
     }
-    var code = Element.text('code', text);
+    final code = Element.text('code', text);
 
     // the info-string should be trimmed
     // http://spec.commonmark.org/0.22/#example-100
@@ -546,7 +546,7 @@
     if (infoString.isNotEmpty) {
       // only use the first word in the syntax
       // http://spec.commonmark.org/0.22/#example-100
-      var firstSpace = infoString.indexOf(' ');
+      final firstSpace = infoString.indexOf(' ');
       if (firstSpace >= 0) {
         infoString = infoString.substring(0, firstSpace);
       }
@@ -556,7 +556,7 @@
       code.attributes['class'] = 'language-$infoString';
     }
 
-    var element = Element('pre', [code]);
+    final element = Element('pre', [code]);
 
     return element;
   }
@@ -618,7 +618,7 @@
 
   @override
   Node parse(BlockParser parser) {
-    var childLines = <String>[];
+    final childLines = <String>[];
 
     // Eat until we hit a blank line.
     while (!parser.isDone && !parser.matches(_emptyPattern)) {
@@ -664,7 +664,7 @@
 
   @override
   Node parse(BlockParser parser) {
-    var childLines = <String>[];
+    final childLines = <String>[];
     // Eat until we hit [endPattern].
     while (!parser.isDone) {
       childLines.add(parser.current);
@@ -693,7 +693,7 @@
     // Ideally, [BlockSyntax.canEndBlock] should be changed to be a method
     // which accepts a [BlockParser], but this would be a breaking change,
     // so we're going with this temporarily.
-    var match = pattern.firstMatch(parser.current)!;
+    final match = pattern.firstMatch(parser.current)!;
     // The seventh group, in both [_olPattern] and [_ulPattern] is the text
     // after the delimiter.
     return match[7]?.isNotEmpty ?? false;
@@ -717,7 +717,7 @@
 
   @override
   Node parse(BlockParser parser) {
-    var items = <ListItem>[];
+    final items = <ListItem>[];
     var childLines = <String>[];
 
     void endItem() {
@@ -740,8 +740,9 @@
     int? startNumber;
 
     while (!parser.isDone) {
-      var leadingSpace = _whitespaceRe.matchAsPrefix(parser.current)!.group(0)!;
-      var leadingExpandedTabLength = _expandedTabLength(leadingSpace);
+      final leadingSpace =
+          _whitespaceRe.matchAsPrefix(parser.current)!.group(0)!;
+      final leadingExpandedTabLength = _expandedTabLength(leadingSpace);
       if (tryMatch(_emptyPattern)) {
         if (_emptyPattern.hasMatch(parser.next ?? '')) {
           // Two blank lines ends a list.
@@ -751,7 +752,7 @@
         childLines.add('');
       } else if (indent != null && indent.length <= leadingExpandedTabLength) {
         // Strip off indent and add to current item.
-        var line = parser.current
+        final line = parser.current
             .replaceFirst(leadingSpace, ' ' * leadingExpandedTabLength)
             .replaceFirst(indent, '');
         childLines.add(line);
@@ -759,22 +760,22 @@
         // Horizontal rule takes precedence to a new list item.
         break;
       } else if (tryMatch(_ulPattern) || tryMatch(_olPattern)) {
-        var precedingWhitespace = match![1]!;
-        var digits = match![2] ?? '';
+        final precedingWhitespace = match![1]!;
+        final digits = match![2] ?? '';
         if (startNumber == null && digits.isNotEmpty) {
           startNumber = int.parse(digits);
         }
-        var marker = match![3]!;
-        var firstWhitespace = match![5] ?? '';
-        var restWhitespace = match![6] ?? '';
-        var content = match![7] ?? '';
-        var isBlank = content.isEmpty;
+        final marker = match![3]!;
+        final firstWhitespace = match![5] ?? '';
+        final restWhitespace = match![6] ?? '';
+        final content = match![7] ?? '';
+        final isBlank = content.isEmpty;
         if (listMarker != null && listMarker != marker) {
           // Changing the bullet or ordered list delimiter starts a new list.
           break;
         }
         listMarker = marker;
-        var markerAsSpaces = ' ' * (digits.length + marker.length);
+        final markerAsSpaces = ' ' * (digits.length + marker.length);
         if (isBlank) {
           // See http://spec.commonmark.org/0.28/#list-items under "3. Item
           // starting with a blank line."
@@ -816,15 +817,15 @@
     }
 
     endItem();
-    var itemNodes = <Element>[];
+    final itemNodes = <Element>[];
 
     items.forEach(_removeLeadingEmptyLine);
-    var anyEmptyLines = _removeTrailingEmptyLines(items);
+    final anyEmptyLines = _removeTrailingEmptyLines(items);
     var anyEmptyLinesBetweenBlocks = false;
 
-    for (var item in items) {
-      var itemParser = BlockParser(item.lines, parser.document);
-      var children = itemParser.parseLines();
+    for (final item in items) {
+      final itemParser = BlockParser(item.lines, parser.document);
+      final children = itemParser.parseLines();
       itemNodes.add(Element('li', children));
       anyEmptyLinesBetweenBlocks =
           anyEmptyLinesBetweenBlocks || itemParser.encounteredBlankLine;
@@ -832,16 +833,16 @@
 
     // Must strip paragraph tags if the list is "tight".
     // http://spec.commonmark.org/0.28/#lists
-    var listIsTight = !anyEmptyLines && !anyEmptyLinesBetweenBlocks;
+    final listIsTight = !anyEmptyLines && !anyEmptyLinesBetweenBlocks;
 
     if (listIsTight) {
       // We must post-process the list items, converting any top-level paragraph
       // elements to just text elements.
-      for (var item in itemNodes) {
-        var children = item.children;
+      for (final item in itemNodes) {
+        final children = item.children;
         if (children != null) {
           for (var i = 0; i < children.length; i++) {
-            var child = children[i];
+            final child = children[i];
             if (child is Element && child.tag == 'p') {
               children.removeAt(i);
               children.insertAll(i, child.children!);
@@ -883,7 +884,7 @@
 
   static int _expandedTabLength(String input) {
     var length = 0;
-    for (var char in input.codeUnits) {
+    for (final char in input.codeUnits) {
       length += char == 0x9 ? 4 - (length % 4) : 1;
     }
     return length;
@@ -936,21 +937,21 @@
   /// * many body rows of body cells (`<td>` cells)
   @override
   Node? parse(BlockParser parser) {
-    var alignments = _parseAlignments(parser.next!);
-    var columnCount = alignments.length;
-    var headRow = _parseRow(parser, alignments, 'th');
+    final alignments = _parseAlignments(parser.next!);
+    final columnCount = alignments.length;
+    final headRow = _parseRow(parser, alignments, 'th');
     if (headRow.children!.length != columnCount) {
       return null;
     }
-    var head = Element('thead', [headRow]);
+    final head = Element('thead', [headRow]);
 
     // Advance past the divider of hyphens.
     parser.advance();
 
-    var rows = <Element>[];
+    final rows = <Element>[];
     while (!parser.isDone && !BlockSyntax.isAtBlockEnd(parser)) {
-      var row = _parseRow(parser, alignments, 'td');
-      var children = row.children;
+      final row = _parseRow(parser, alignments, 'td');
+      final children = row.children;
       if (children != null) {
         while (children.length < columnCount) {
           // Insert synthetic empty cells.
@@ -968,18 +969,18 @@
     if (rows.isEmpty) {
       return Element('table', [head]);
     } else {
-      var body = Element('tbody', rows);
+      final body = Element('tbody', rows);
 
       return Element('table', [head, body]);
     }
   }
 
   List<String?> _parseAlignments(String line) {
-    var startIndex = _walkPastOpeningPipe(line);
+    final startIndex = _walkPastOpeningPipe(line);
 
     var endIndex = line.length - 1;
     while (endIndex > 0) {
-      var ch = line.codeUnitAt(endIndex);
+      final ch = line.codeUnitAt(endIndex);
       if (ch == $pipe) {
         endIndex--;
         break;
@@ -1007,10 +1008,10 @@
   /// [cellType] is used to declare either "td" or "th" cells.
   Element _parseRow(
       BlockParser parser, List<String?> alignments, String cellType) {
-    var line = parser.current;
-    var cells = <String>[];
+    final line = parser.current;
+    final cells = <String>[];
     var index = _walkPastOpeningPipe(line);
-    var cellBuffer = StringBuffer();
+    final cellBuffer = StringBuffer();
 
     while (true) {
       if (index >= line.length) {
@@ -1019,7 +1020,7 @@
         cellBuffer.clear();
         break;
       }
-      var ch = line.codeUnitAt(index);
+      final ch = line.codeUnitAt(index);
       if (ch == $backslash) {
         if (index == line.length - 1) {
           // A table row ending in a backslash is not well-specified, but it
@@ -1030,7 +1031,7 @@
           cellBuffer.clear();
           break;
         }
-        var escaped = line.codeUnitAt(index + 1);
+        final escaped = line.codeUnitAt(index + 1);
         if (escaped == $pipe) {
           // GitHub Flavored Markdown has a strange bit here; the pipe is to be
           // escaped before any other inline processing. One consequence, for
@@ -1061,8 +1062,8 @@
       }
     }
     parser.advance();
-    var row = [
-      for (var cell in cells) Element(cellType, [UnparsedContent(cell)])
+    final row = [
+      for (final cell in cells) Element(cellType, [UnparsedContent(cell)])
     ];
 
     for (var i = 0; i < row.length && i < alignments.length; i++) {
@@ -1078,7 +1079,7 @@
   /// Returns the index of the first non-whitespace character.
   int _walkPastWhitespace(String line, int index) {
     while (index < line.length) {
-      var ch = line.codeUnitAt(index);
+      final ch = line.codeUnitAt(index);
       if (ch != $space && ch != $tab) {
         break;
       }
@@ -1096,7 +1097,7 @@
   int _walkPastOpeningPipe(String line) {
     var index = 0;
     while (index < line.length) {
-      var ch = line.codeUnitAt(index);
+      final ch = line.codeUnitAt(index);
       if (ch == $pipe) {
         index++;
         index = _walkPastWhitespace(line, index);
@@ -1130,7 +1131,7 @@
 
   @override
   Node parse(BlockParser parser) {
-    var childLines = <String>[];
+    final childLines = <String>[];
 
     // Eat until we hit something that ends a paragraph.
     while (!BlockSyntax.isAtBlockEnd(parser)) {
@@ -1138,12 +1139,12 @@
       parser.advance();
     }
 
-    var paragraphLines = _extractReflinkDefinitions(parser, childLines);
+    final paragraphLines = _extractReflinkDefinitions(parser, childLines);
     if (paragraphLines == null) {
       // Paragraph consisted solely of reference link definitions.
       return Text('');
     } else {
-      var contents = UnparsedContent(paragraphLines.join('\n').trimRight());
+      final contents = UnparsedContent(paragraphLines.join('\n').trimRight());
       return Element('p', [contents]);
     }
   }
@@ -1235,7 +1236,7 @@
   //
   // Returns whether [contents] could be parsed as a reference link definition.
   bool _parseReflinkDefinition(BlockParser parser, String contents) {
-    var pattern = RegExp(
+    final pattern = RegExp(
         // Leading indentation.
         r'''^[ ]{0,3}'''
         // Reference id in brackets, and URL.
@@ -1243,7 +1244,7 @@
         // Title in double or single quotes, or parens.
         r'''("[^"]+"|'[^']+'|\([^)]+\)|)\s*$''',
         multiLine: true);
-    var match = pattern.firstMatch(contents);
+    final match = pattern.firstMatch(contents);
     if (match == null) {
       // Not a reference link definition.
       return false;
@@ -1254,7 +1255,7 @@
     }
 
     var label = match[1]!;
-    var destination = match[2] ?? match[3]!;
+    final destination = match[2] ?? match[3]!;
     var title = match[4];
 
     // The label must contain at least one non-whitespace character.
diff --git a/pkgs/markdown/lib/src/document.dart b/pkgs/markdown/lib/src/document.dart
index 374f955..5ee75ed 100644
--- a/pkgs/markdown/lib/src/document.dart
+++ b/pkgs/markdown/lib/src/document.dart
@@ -61,7 +61,7 @@
 
   /// Parses the given [lines] of Markdown to a series of AST nodes.
   List<Node> parseLines(List<String> lines) {
-    var nodes = BlockParser(lines, this).parseLines();
+    final nodes = BlockParser(lines, this).parseLines();
     _parseInlineContent(nodes);
     return nodes;
   }
@@ -71,9 +71,9 @@
 
   void _parseInlineContent(List<Node> nodes) {
     for (var i = 0; i < nodes.length; i++) {
-      var node = nodes[i];
+      final node = nodes[i];
       if (node is UnparsedContent) {
-        var inlineNodes = parseInline(node.textContent);
+        final inlineNodes = parseInline(node.textContent);
         nodes.removeAt(i);
         nodes.insertAll(i, inlineNodes);
         i += inlineNodes.length - 1;
diff --git a/pkgs/markdown/lib/src/html_renderer.dart b/pkgs/markdown/lib/src/html_renderer.dart
index 728d35f..baee0fc 100644
--- a/pkgs/markdown/lib/src/html_renderer.dart
+++ b/pkgs/markdown/lib/src/html_renderer.dart
@@ -23,7 +23,7 @@
   bool withDefaultBlockSyntaxes = true,
   bool withDefaultInlineSyntaxes = true,
 }) {
-  var document = Document(
+  final document = Document(
     blockSyntaxes: blockSyntaxes,
     inlineSyntaxes: inlineSyntaxes,
     extensionSet: extensionSet,
@@ -37,7 +37,7 @@
   if (inlineOnly) return renderToHtml(document.parseInline(markdown));
 
   // Replace windows line endings with unix line endings, and split.
-  var lines = markdown.replaceAll('\r\n', '\n').split('\n');
+  final lines = markdown.replaceAll('\r\n', '\n').split('\n');
 
   return renderToHtml(document.parseLines(lines)) + '\n';
 }
@@ -103,7 +103,7 @@
   void visitText(Text text) {
     var content = text.text;
     if (const ['br', 'p', 'li'].contains(_lastVisitedTag)) {
-      var lines = LineSplitter.split(content);
+      final lines = LineSplitter.split(content);
       content = content.contains('<pre>')
           ? lines.join('\n')
           : lines.map((line) => line.trimLeft()).join('\n');
@@ -125,11 +125,11 @@
 
     buffer.write('<${element.tag}');
 
-    for (var entry in element.attributes.entries) {
+    for (final entry in element.attributes.entries) {
       buffer.write(' ${entry.key}="${entry.value}"');
     }
 
-    var generatedId = element.generatedId;
+    final generatedId = element.generatedId;
 
     // attach header anchor ids generated from text
     if (generatedId != null) {
diff --git a/pkgs/markdown/lib/src/inline_parser.dart b/pkgs/markdown/lib/src/inline_parser.dart
index 2caf029..29b09b3 100644
--- a/pkgs/markdown/lib/src/inline_parser.dart
+++ b/pkgs/markdown/lib/src/inline_parser.dart
@@ -127,7 +127,7 @@
   /// This is the "look for link or image" routine from the CommonMark spec:
   /// https://spec.commonmark.org/0.29/#-look-for-link-or-image-.
   void _linkOrImage() {
-    var index = _delimiterStack
+    final index = _delimiterStack
         .lastIndexWhere((d) => d.char == $lbracket || d.char == $exclamation);
     if (index == -1) {
       // Never found a possible open bracket. This is just a literal "]".
@@ -136,7 +136,7 @@
       start = pos;
       return;
     }
-    var delimiter = _delimiterStack[index] as SimpleDelimiter;
+    final delimiter = _delimiterStack[index] as SimpleDelimiter;
     if (!delimiter.isActive) {
       _delimiterStack.removeAt(index);
       addNode(Text(']'));
@@ -144,21 +144,21 @@
       start = pos;
       return;
     }
-    var syntax = delimiter.syntax;
+    final syntax = delimiter.syntax;
     if (syntax is LinkSyntax && syntaxes.any(((e) => e is LinkSyntax))) {
-      var nodeIndex = _tree.lastIndexWhere((n) => n == delimiter.node);
-      var linkNode = syntax.close(this, delimiter, null, getChildren: () {
+      final nodeIndex = _tree.lastIndexWhere((n) => n == delimiter.node);
+      final linkNode = syntax.close(this, delimiter, null, getChildren: () {
         _processDelimiterRun(index);
         // All of the nodes which lie past [index] are children of this
         // link/image.
-        var children = _tree.sublist(nodeIndex + 1, _tree.length);
+        final children = _tree.sublist(nodeIndex + 1, _tree.length);
         _tree.removeRange(nodeIndex + 1, _tree.length);
         return children;
       });
       if (linkNode != null) {
         _delimiterStack.removeAt(index);
         if (delimiter.char == $lbracket) {
-          for (var d in _delimiterStack.sublist(0, index)) {
+          for (final d in _delimiterStack.sublist(0, index)) {
             if (d.char == $lbracket) d.isActive = false;
           }
         }
@@ -198,41 +198,41 @@
     // Each key in this map is an open delimiter character. Each value is a
     // 3-element list. Each value in the list is the lowest index for the given
     // delimiter length modulo 3 (0, 1, 2).
-    var openersBottom = <int, List<int>>{};
+    final openersBottom = <int, List<int>>{};
     while (currentIndex < _delimiterStack.length) {
-      var closer = _delimiterStack[currentIndex];
+      final closer = _delimiterStack[currentIndex];
       if (!closer.canClose || closer is! DelimiterRun) {
         currentIndex++;
         continue;
       }
       openersBottom.putIfAbsent(closer.char, () => List.filled(3, bottomIndex));
-      var openersBottomPerCloserLength = openersBottom[closer.char]!;
-      var openerBottom = openersBottomPerCloserLength[closer.length % 3];
-      var openerIndex = _delimiterStack.lastIndexWhere(
+      final openersBottomPerCloserLength = openersBottom[closer.char]!;
+      final openerBottom = openersBottomPerCloserLength[closer.length % 3];
+      final openerIndex = _delimiterStack.lastIndexWhere(
           (d) =>
               d.char == closer.char && d.canOpen && _canFormEmphasis(d, closer),
           currentIndex - 1);
       if (openerIndex > bottomIndex && openerIndex > openerBottom) {
         // Found an opener for [closer].
-        var opener = _delimiterStack[openerIndex];
+        final opener = _delimiterStack[openerIndex];
         if (opener is! DelimiterRun) {
           currentIndex++;
           continue;
         }
-        var matchedTagIndex = opener.tags.lastIndexWhere((e) =>
+        final matchedTagIndex = opener.tags.lastIndexWhere((e) =>
             opener.length >= e.indicatorLength &&
             closer.length >= e.indicatorLength);
         if (matchedTagIndex == -1) {
           currentIndex++;
           continue;
         }
-        var matchedTag = opener.tags[matchedTagIndex];
-        var indicatorLength = matchedTag.indicatorLength;
-        var openerTextNode = opener.node;
-        var openerTextNodeIndex = _tree.indexOf(openerTextNode);
-        var closerTextNode = closer.node;
+        final matchedTag = opener.tags[matchedTagIndex];
+        final indicatorLength = matchedTag.indicatorLength;
+        final openerTextNode = opener.node;
+        final openerTextNodeIndex = _tree.indexOf(openerTextNode);
+        final closerTextNode = closer.node;
         var closerTextNodeIndex = _tree.indexOf(closerTextNode);
-        var node = opener.syntax.close(this, opener, closer,
+        final node = opener.syntax.close(this, opener, closer,
             tag: matchedTag.tag,
             getChildren: () =>
                 _tree.sublist(openerTextNodeIndex + 1, closerTextNodeIndex));
@@ -256,7 +256,7 @@
           currentIndex--;
           closerTextNodeIndex--;
         } else {
-          var newOpenerTextNode =
+          final newOpenerTextNode =
               Text(openerTextNode.text.substring(indicatorLength));
           _tree[openerTextNodeIndex] = newOpenerTextNode;
           opener.node = newOpenerTextNode;
@@ -268,7 +268,7 @@
           // [currentIndex] has just moved to point at the next delimiter;
           // leave it.
         } else {
-          var newCloserTextNode =
+          final newCloserTextNode =
               Text(closerTextNode.text.substring(indicatorLength));
           _tree[closerTextNodeIndex] = newCloserTextNode;
           closer.node = newCloserTextNode;
@@ -293,13 +293,13 @@
   // correct output across newlines, where whitespace is sometimes compressed.
   void _combineAdjacentText(List<Node> nodes) {
     for (var i = 0; i < nodes.length - 1; i++) {
-      var node = nodes[i];
+      final node = nodes[i];
       if (node is Element && node.children != null) {
         _combineAdjacentText(node.children!);
         continue;
       }
       if (node is Text && nodes[i + 1] is Text) {
-        var buffer =
+        final buffer =
             StringBuffer('${node.textContent}${nodes[i + 1].textContent}');
         var j = i + 2;
         while (j < nodes.length && nodes[j] is Text) {
@@ -318,7 +318,7 @@
     if (pos == start) {
       return;
     }
-    var text = source.substring(start, pos);
+    final text = source.substring(start, pos);
     _tree.add(Text(text));
     start = pos;
   }
@@ -449,8 +449,8 @@
 
   @override
   bool onMatch(InlineParser parser, Match match) {
-    var chars = match.match;
-    var char = chars.codeUnitAt(1);
+    final chars = match.match;
+    final char = chars.codeUnitAt(1);
     // Insert the substitution. Why these three charactes are replaced with
     // their equivalent HTML entity referenced appears to be missing from the
     // CommonMark spec, but is very present in all of the examples.
@@ -499,9 +499,9 @@
 
   @override
   bool onMatch(InlineParser parser, Match match) {
-    var url = match[1]!;
-    var text = parser._encodeHtml ? escapeHtml(url) : url;
-    var anchor = Element.text('a', text);
+    final url = match[1]!;
+    final text = parser._encodeHtml ? escapeHtml(url) : url;
+    final anchor = Element.text('a', text);
     anchor.attributes['href'] = Uri.encodeFull('mailto:$url');
     parser.addNode(anchor);
 
@@ -515,9 +515,9 @@
 
   @override
   bool onMatch(InlineParser parser, Match match) {
-    var url = match[1]!;
-    var text = parser._encodeHtml ? escapeHtml(url) : url;
-    var anchor = Element.text('a', text);
+    final url = match[1]!;
+    final text = parser._encodeHtml ? escapeHtml(url) : url;
+    final anchor = Element.text('a', text);
     anchor.attributes['href'] = Uri.encodeFull(url);
     parser.addNode(anchor);
 
@@ -603,7 +603,7 @@
     // https://github.github.com/gfm/#example-599
     final trailingPunc = regExpTrailingPunc.firstMatch(url);
     if (trailingPunc != null) {
-      var trailingLength = trailingPunc.match.length;
+      final trailingLength = trailingPunc.match.length;
       url = url.substring(0, url.length - trailingLength);
       href = href.substring(0, href.length - trailingLength);
       matchLength -= trailingLength;
@@ -619,7 +619,7 @@
       final entityRef = regExpEndsWithColon.firstMatch(url);
       if (entityRef != null) {
         // Strip out HTML entity reference
-        var entityRefLength = entityRef.match.length;
+        final entityRefLength = entityRef.match.length;
         url = url.substring(0, url.length - entityRefLength);
         href = href.substring(0, href.length - entityRefLength);
         matchLength -= entityRefLength;
@@ -920,10 +920,10 @@
 
   @override
   bool onMatch(InlineParser parser, Match match) {
-    var runLength = match.group(0)!.length;
-    var matchStart = parser.pos;
-    var matchEnd = parser.pos + runLength;
-    var text = Text(parser.source.substring(matchStart, matchEnd));
+    final runLength = match.group(0)!.length;
+    final matchStart = parser.pos;
+    final matchEnd = parser.pos + runLength;
+    final text = Text(parser.source.substring(matchStart, matchEnd));
     if (!requiresDelimiterRun) {
       parser._pushDelimiter(SimpleDelimiter(
           node: text,
@@ -937,7 +937,7 @@
       return true;
     }
 
-    var delimiterRun = DelimiterRun.tryParse(parser, matchStart, matchEnd,
+    final delimiterRun = DelimiterRun.tryParse(parser, matchStart, matchEnd,
         syntax: this,
         node: text,
         allowIntraWord: allowIntraWord,
@@ -1021,7 +1021,7 @@
     String? tag,
     required List<Node> Function() getChildren,
   }) {
-    var text = parser.source.substring(opener.endPos, parser.pos);
+    final text = parser.source.substring(opener.endPos, parser.pos);
     // The current character is the `]` that closed the link text. Examine the
     // next character, to determine what type of link we might have (a '('
     // means a possible inline link; otherwise a possible reference link).
@@ -1033,13 +1033,13 @@
 
     // Peek at the next character; don't advance, so as to avoid later stepping
     // backward.
-    var char = parser.charAt(parser.pos + 1);
+    final char = parser.charAt(parser.pos + 1);
 
     if (char == $lparen) {
       // Maybe an inline link, like `[text](destination)`.
       parser.advanceBy(1);
-      var leftParenIndex = parser.pos;
-      var inlineLink = _parseInlineLink(parser);
+      final leftParenIndex = parser.pos;
+      final inlineLink = _parseInlineLink(parser);
       if (inlineLink != null) {
         return _tryCreateInlineLink(parser, inlineLink,
             getChildren: getChildren);
@@ -1065,7 +1065,7 @@
         parser.advanceBy(1);
         return _tryCreateReferenceLink(parser, text, getChildren: getChildren);
       }
-      var label = _parseReferenceLinkLabel(parser);
+      final label = _parseReferenceLinkLabel(parser);
       if (label != null) {
         return _tryCreateReferenceLink(parser, label, getChildren: getChildren);
       }
@@ -1090,7 +1090,7 @@
   Node? _resolveReferenceLink(
       String label, Map<String, LinkReference> linkReferences,
       {required List<Node> Function() getChildren}) {
-    var linkReference = linkReferences[normalizeLinkLabel(label)];
+    final linkReference = linkReferences[normalizeLinkLabel(label)];
     if (linkReference != null) {
       return _createNode(linkReference.destination, linkReference.title,
           getChildren: getChildren);
@@ -1103,7 +1103,7 @@
       // Normally, label text does not get parsed as inline Markdown. However,
       // for the benefit of the link resolver, we need to at least escape
       // brackets, so that, e.g. a link resolver can receive `[\[\]]` as `[]`.
-      var resolved = linkResolver(label
+      final resolved = linkResolver(label
           .replaceAll(r'\\', r'\')
           .replaceAll(r'\[', '[')
           .replaceAll(r'\]', ']'));
@@ -1117,8 +1117,8 @@
   /// Create the node represented by a Markdown link.
   Node _createNode(String destination, String? title,
       {required List<Node> Function() getChildren}) {
-    var children = getChildren();
-    var element = Element('a', children);
+    final children = getChildren();
+    final element = Element('a', children);
     element.attributes['href'] = escapeAttribute(destination);
     if (title != null && title.isNotEmpty) {
       element.attributes['title'] = escapeAttribute(title);
@@ -1154,12 +1154,12 @@
     parser.advanceBy(1);
     if (parser.isDone) return null;
 
-    var buffer = StringBuffer();
+    final buffer = StringBuffer();
     while (true) {
-      var char = parser.charAt(parser.pos);
+      final char = parser.charAt(parser.pos);
       if (char == $backslash) {
         parser.advanceBy(1);
-        var next = parser.charAt(parser.pos);
+        final next = parser.charAt(parser.pos);
         if (next != $backslash && next != $rbracket) {
           buffer.writeCharCode(char);
         }
@@ -1176,7 +1176,7 @@
       // TODO(srawlins): only check 999 characters, for performance reasons?
     }
 
-    var label = buffer.toString();
+    final label = buffer.toString();
 
     // A link label must contain at least one non-whitespace character.
     if (_entirelyWhitespacePattern.hasMatch(label)) return null;
@@ -1217,12 +1217,12 @@
   InlineLink? _parseInlineBracketedLink(InlineParser parser) {
     parser.advanceBy(1);
 
-    var buffer = StringBuffer();
+    final buffer = StringBuffer();
     while (true) {
-      var char = parser.charAt(parser.pos);
+      final char = parser.charAt(parser.pos);
       if (char == $backslash) {
         parser.advanceBy(1);
-        var next = parser.charAt(parser.pos);
+        final next = parser.charAt(parser.pos);
         // TODO: Follow the backslash spec better here.
         // http://spec.commonmark.org/0.29/#backslash-escapes
         if (next != $backslash && next != $gt) {
@@ -1242,12 +1242,12 @@
       parser.advanceBy(1);
       if (parser.isDone) return null;
     }
-    var destination = buffer.toString();
+    final destination = buffer.toString();
 
     parser.advanceBy(1);
-    var char = parser.charAt(parser.pos);
+    final char = parser.charAt(parser.pos);
     if (char == $space || char == $lf || char == $cr || char == $ff) {
-      var title = _parseTitle(parser);
+      final title = _parseTitle(parser);
       if (title == null &&
           (parser.isDone || parser.charAt(parser.pos) != $rparen)) {
         // This looked like an inline link, until we found this $space
@@ -1280,15 +1280,15 @@
     // We need to count the open parens. We start with 1 for the paren that
     // opened the destination.
     var parenCount = 1;
-    var buffer = StringBuffer();
+    final buffer = StringBuffer();
 
     while (true) {
-      var char = parser.charAt(parser.pos);
+      final char = parser.charAt(parser.pos);
       switch (char) {
         case $backslash:
           parser.advanceBy(1);
           if (parser.isDone) return null; // EOF. Not a link.
-          var next = parser.charAt(parser.pos);
+          final next = parser.charAt(parser.pos);
           // Parentheses may be escaped.
           //
           // http://spec.commonmark.org/0.28/#example-467
@@ -1302,8 +1302,8 @@
         case $lf:
         case $cr:
         case $ff:
-          var destination = buffer.toString();
-          var title = _parseTitle(parser);
+          final destination = buffer.toString();
+          final title = _parseTitle(parser);
           if (title == null &&
               (parser.isDone || parser.charAt(parser.pos) != $rparen)) {
             // This looked like an inline link, until we found this $space
@@ -1327,7 +1327,7 @@
         case $rparen:
           parenCount--;
           if (parenCount == 0) {
-            var destination = buffer.toString();
+            final destination = buffer.toString();
             return InlineLink(destination);
           }
           buffer.writeCharCode(char);
@@ -1344,7 +1344,7 @@
   // Walk the parser forward through any whitespace.
   void _moveThroughWhitespace(InlineParser parser) {
     while (!parser.isDone) {
-      var char = parser.charAt(parser.pos);
+      final char = parser.charAt(parser.pos);
       if (char != $space &&
           char != $tab &&
           char != $lf &&
@@ -1367,23 +1367,23 @@
     if (parser.isDone) return null;
 
     // The whitespace should be followed by a title delimiter.
-    var delimiter = parser.charAt(parser.pos);
+    final delimiter = parser.charAt(parser.pos);
     if (delimiter != $apostrophe &&
         delimiter != $quote &&
         delimiter != $lparen) {
       return null;
     }
 
-    var closeDelimiter = delimiter == $lparen ? $rparen : delimiter;
+    final closeDelimiter = delimiter == $lparen ? $rparen : delimiter;
     parser.advanceBy(1);
 
     // Now we look for an un-escaped closing delimiter.
-    var buffer = StringBuffer();
+    final buffer = StringBuffer();
     while (true) {
-      var char = parser.charAt(parser.pos);
+      final char = parser.charAt(parser.pos);
       if (char == $backslash) {
         parser.advanceBy(1);
-        var next = parser.charAt(parser.pos);
+        final next = parser.charAt(parser.pos);
         if (next != $backslash && next != closeDelimiter) {
           buffer.writeCharCode(char);
         }
@@ -1396,7 +1396,7 @@
       parser.advanceBy(1);
       if (parser.isDone) return null;
     }
-    var title = buffer.toString();
+    final title = buffer.toString();
 
     // Advance past the closing delimiter.
     parser.advanceBy(1);
@@ -1420,8 +1420,8 @@
   @override
   Element _createNode(String destination, String? title,
       {required List<Node> Function() getChildren}) {
-    var element = Element.empty('img');
-    var children = getChildren();
+    final element = Element.empty('img');
+    final children = getChildren();
     element.attributes['src'] = destination;
     element.attributes['alt'] = children.map((node) => node.textContent).join();
     if (title != null && title.isNotEmpty) {
@@ -1459,7 +1459,7 @@
       return false;
     }
 
-    var match = pattern.matchAsPrefix(parser.source, parser.pos);
+    final match = pattern.matchAsPrefix(parser.source, parser.pos);
     if (match == null) {
       return false;
     }
@@ -1490,8 +1490,8 @@
 
   @override
   bool onMatch(InlineParser parser, Match match) {
-    var alias = match[1]!;
-    var emoji = emojis[alias];
+    final alias = match[1]!;
+    final emoji = emojis[alias];
     if (emoji == null) {
       parser.advanceBy(1);
       return false;
diff --git a/pkgs/markdown/lib/src/util.dart b/pkgs/markdown/lib/src/util.dart
index 3a8a454..95cce15 100644
--- a/pkgs/markdown/lib/src/util.dart
+++ b/pkgs/markdown/lib/src/util.dart
@@ -17,7 +17,7 @@
 ///
 /// Based on http://spec.commonmark.org/0.28/#backslash-escapes.
 String escapeAttribute(String value) {
-  var result = StringBuffer();
+  final result = StringBuffer();
   int ch;
   for (var i = 0; i < value.codeUnits.length; i++) {
     ch = value.codeUnitAt(i);
diff --git a/pkgs/markdown/test/blns_test.dart b/pkgs/markdown/test/blns_test.dart
index 846ce8e..69790ef 100644
--- a/pkgs/markdown/test/blns_test.dart
+++ b/pkgs/markdown/test/blns_test.dart
@@ -20,18 +20,18 @@
   });
 
   var index = 0;
-  for (var str in blns) {
+  for (final str in blns) {
     test('blns string $index', () {
-      var result = markdownToHtml(str);
+      final result = markdownToHtml(str);
       expect(result, const TypeMatcher<String>());
     });
     index++;
   }
 
   index = 0;
-  for (var str in blns) {
+  for (final str in blns) {
     test('blns string $index w/ gitHubWeb', () {
-      var result = markdownToHtml(str, extensionSet: ExtensionSet.gitHubWeb);
+      final result = markdownToHtml(str, extensionSet: ExtensionSet.gitHubWeb);
       expect(result, const TypeMatcher<String>());
     });
     index++;
diff --git a/pkgs/markdown/test/document_test.dart b/pkgs/markdown/test/document_test.dart
index 4a4cd20..cac1bed 100644
--- a/pkgs/markdown/test/document_test.dart
+++ b/pkgs/markdown/test/document_test.dart
@@ -10,8 +10,8 @@
 void main() {
   group('Document', () {
     test('encodeHtml prevents less than and ampersand escaping', () {
-      var document = Document(encodeHtml: false);
-      var result = document.parseInline('< &');
+      final document = Document(encodeHtml: false);
+      final result = document.parseInline('< &');
       expect(result, hasLength(1));
       expect(
           result[0],
@@ -20,46 +20,47 @@
     });
 
     group('with encodeHtml enabled', () {
-      var document = Document(encodeHtml: true);
+      final document = Document(encodeHtml: true);
 
       test('encodes HTML in an inline code snippet', () {
-        var result = document.parseInline('``<p>Hello <em>Markdown</em></p>``');
-        var codeSnippet = result.single as Element;
+        final result =
+            document.parseInline('``<p>Hello <em>Markdown</em></p>``');
+        final codeSnippet = result.single as Element;
         expect(codeSnippet.textContent,
             equals('&lt;p&gt;Hello &lt;em&gt;Markdown&lt;/em&gt;&lt;/p&gt;'));
       });
 
       test('encodes HTML in a fenced code block', () {
-        var lines = '```\n<p>Hello <em>Markdown</em></p>\n```\n'.split('\n');
-        var result = document.parseLines(lines);
-        var codeBlock = result.single as Element;
+        final lines = '```\n<p>Hello <em>Markdown</em></p>\n```\n'.split('\n');
+        final result = document.parseLines(lines);
+        final codeBlock = result.single as Element;
         expect(codeBlock.textContent,
             equals('&lt;p&gt;Hello &lt;em&gt;Markdown&lt;/em&gt;&lt;/p&gt;\n'));
       });
 
       test('encodes HTML in an indented code block', () {
-        var lines = '    <p>Hello <em>Markdown</em></p>\n'.split('\n');
-        var result = document.parseLines(lines);
-        var codeBlock = result.single as Element;
+        final lines = '    <p>Hello <em>Markdown</em></p>\n'.split('\n');
+        final result = document.parseLines(lines);
+        final codeBlock = result.single as Element;
         expect(codeBlock.textContent,
             equals('&lt;p&gt;Hello &lt;em&gt;Markdown&lt;/em&gt;&lt;/p&gt;\n'));
       });
 
       test('encodeHtml spaces are preserved in text', () {
         // Example to get a <p> tag rendered before a text node.
-        var contents = 'Sample\n\n<pre>\n A\n B\n</pre>';
-        var document = Document(encodeHtml: true);
-        var lines = LineSplitter.split(contents).toList();
-        var nodes = BlockParser(lines, document).parseLines();
-        var result = HtmlRenderer().render(nodes);
+        final contents = 'Sample\n\n<pre>\n A\n B\n</pre>';
+        final document = Document(encodeHtml: true);
+        final lines = LineSplitter.split(contents).toList();
+        final nodes = BlockParser(lines, document).parseLines();
+        final result = HtmlRenderer().render(nodes);
         expect(result, '<p>\n</p><pre>\n A\n B\n</pre>');
       });
 
       test('encode double quotes, greater than, and less than when escaped',
           () {
-        var contents = r'\>\"\< Hello';
-        var document = Document(encodeHtml: true);
-        var nodes = document.parseInline(contents);
+        final contents = r'\>\"\< Hello';
+        final document = Document(encodeHtml: true);
+        final nodes = document.parseInline(contents);
         expect(nodes, hasLength(1));
         expect(
             nodes.single,
@@ -72,36 +73,36 @@
     });
 
     group('with encodeHtml disabled', () {
-      var document = Document(encodeHtml: false);
+      final document = Document(encodeHtml: false);
 
       test('leaves HTML alone, in a code snippet', () {
-        var result =
+        final result =
             document.parseInline('```<p>Hello <em>Markdown</em></p>```');
-        var codeSnippet = result.single as Element;
+        final codeSnippet = result.single as Element;
         expect(
             codeSnippet.textContent, equals('<p>Hello <em>Markdown</em></p>'));
       });
 
       test('leaves HTML alone, in a fenced code block', () {
-        var lines = '```\n<p>Hello <em>Markdown</em></p>\n```\n'.split('\n');
-        var result = document.parseLines(lines);
-        var codeBlock = result.single as Element;
+        final lines = '```\n<p>Hello <em>Markdown</em></p>\n```\n'.split('\n');
+        final result = document.parseLines(lines);
+        final codeBlock = result.single as Element;
         expect(
             codeBlock.textContent, equals('<p>Hello <em>Markdown</em></p>\n'));
       });
 
       test('leaves HTML alone, in an indented code block', () {
-        var lines = '    <p>Hello <em>Markdown</em></p>\n'.split('\n');
-        var result = document.parseLines(lines);
-        var codeBlock = result.single as Element;
+        final lines = '    <p>Hello <em>Markdown</em></p>\n'.split('\n');
+        final result = document.parseLines(lines);
+        final codeBlock = result.single as Element;
         expect(
             codeBlock.textContent, equals('<p>Hello <em>Markdown</em></p>\n'));
       });
 
       test('leave double quotes, greater than, and less than when escaped', () {
-        var contents = r'\>\"\< Hello';
-        var document = Document(encodeHtml: false);
-        var nodes = document.parseInline(contents);
+        final contents = r'\>\"\< Hello';
+        final document = Document(encodeHtml: false);
+        final nodes = document.parseInline(contents);
         expect(nodes, hasLength(1));
         expect(
             nodes.single,
diff --git a/pkgs/markdown/test/markdown_test.dart b/pkgs/markdown/test/markdown_test.dart
index 945b904..539c397 100644
--- a/pkgs/markdown/test/markdown_test.dart
+++ b/pkgs/markdown/test/markdown_test.dart
@@ -116,7 +116,7 @@
   });
 
   group('Custom inline syntax', () {
-    var nyanSyntax = <InlineSyntax>[TextSyntax('nyan', sub: '~=[,,_,,]:3')];
+    final nyanSyntax = <InlineSyntax>[TextSyntax('nyan', sub: '~=[,,_,,]:3')];
     validateCore(
         'simple inline syntax',
         '''
diff --git a/pkgs/markdown/test/util.dart b/pkgs/markdown/test/util.dart
index 86e78c8..354a02bd 100644
--- a/pkgs/markdown/test/util.dart
+++ b/pkgs/markdown/test/util.dart
@@ -12,8 +12,8 @@
 
 /// Runs tests defined in "*.unit" files inside directory [name].
 Future<void> testDirectory(String name, {ExtensionSet? extensionSet}) async {
-  await for (var dataCase in dataCasesUnder(testDirectory: name)) {
-    var description =
+  await for (final dataCase in dataCasesUnder(testDirectory: name)) {
+    final description =
         '${dataCase.directory}/${dataCase.file}.unit ${dataCase.description}';
     validateCore(
       description,
@@ -34,9 +34,9 @@
   Iterable<BlockSyntax> blockSyntaxes = const [],
   Iterable<InlineSyntax> inlineSyntaxes = const [],
 }) async {
-  var directory = p.join(await markdownPackageRoot, 'test');
-  for (var dataCase in dataCasesInFile(path: p.join(directory, file))) {
-    var description =
+  final directory = p.join(await markdownPackageRoot, 'test');
+  for (final dataCase in dataCasesInFile(path: p.join(directory, file))) {
+    final description =
         '${dataCase.directory}/${dataCase.file}.unit ${dataCase.description}';
     validateCore(description, dataCase.input, dataCase.expectedOutput,
         blockSyntaxes: blockSyntaxes, inlineSyntaxes: inlineSyntaxes);
@@ -55,7 +55,7 @@
   bool inlineOnly = false,
 }) {
   test(description, () {
-    var result = markdownToHtml(markdown,
+    final result = markdownToHtml(markdown,
         blockSyntaxes: blockSyntaxes,
         inlineSyntaxes: inlineSyntaxes,
         extensionSet: extensionSet,
diff --git a/pkgs/markdown/test/version_test.dart b/pkgs/markdown/test/version_test.dart
index b9c3878..4c200fe 100644
--- a/pkgs/markdown/test/version_test.dart
+++ b/pkgs/markdown/test/version_test.dart
@@ -12,20 +12,20 @@
 
 void main() {
   test('check versions', () async {
-    var packageRoot = await markdownPackageRoot;
-    var binary = p.normalize(p.join(packageRoot, 'bin', 'markdown.dart'));
-    var dartBin = Platform.executable;
-    var result = Process.runSync(dartBin, [binary, '--version']);
+    final packageRoot = await markdownPackageRoot;
+    final binary = p.normalize(p.join(packageRoot, 'bin', 'markdown.dart'));
+    final dartBin = Platform.executable;
+    final result = Process.runSync(dartBin, [binary, '--version']);
     expect(result.exitCode, 0,
         reason: 'Exit code expected: 0; actual: ${result.exitCode}\n\n'
             'stdout: ${result.stdout}\n\n'
             'stderr: ${result.stderr}');
 
-    var binVersion = (result.stdout as String).trim();
+    final binVersion = (result.stdout as String).trim();
 
-    var pubspecFile = p.normalize(p.join(packageRoot, 'pubspec.yaml'));
+    final pubspecFile = p.normalize(p.join(packageRoot, 'pubspec.yaml'));
 
-    var pubspecContent =
+    final pubspecContent =
         loadYaml(File(pubspecFile).readAsStringSync()) as YamlMap;
 
     expect(binVersion, pubspecContent['version'],
diff --git a/pkgs/markdown/tool/dartdoc_compare.dart b/pkgs/markdown/tool/dartdoc_compare.dart
index 7c24553..cd0c4fc 100644
--- a/pkgs/markdown/tool/dartdoc_compare.dart
+++ b/pkgs/markdown/tool/dartdoc_compare.dart
@@ -26,7 +26,7 @@
         defaultsTo: false, negatable: false, help: 'Is the package the SDK?')
     ..addFlag(_help, abbr: 'h', hide: true);
 
-  var options = parser.parse(arguments);
+  final options = parser.parse(arguments);
   if (options[_help] as bool) {
     print(parser.usage);
     exitCode = 0;
@@ -39,7 +39,7 @@
     exitCode = 1;
     return;
   }
-  var comparer = DartdocCompare(
+  final comparer = DartdocCompare(
       options[_dartdocDir] as String,
       options[_markdownBefore] as String,
       options[_markdownAfter] as String,
@@ -77,15 +77,15 @@
 
   bool compare(String? package) {
     // Generate docs with Markdown "Before".
-    var outBefore = _runDartdoc(markdownBefore, package);
+    final outBefore = _runDartdoc(markdownBefore, package);
 
     // Generate docs with Markdown "After".
-    var outAfter = _runDartdoc(markdownAfter, package);
+    final outAfter = _runDartdoc(markdownAfter, package);
 
     // Compare outputs
-    var diffOptions = ['-r', '-B', outBefore, outAfter];
-    var result = Process.runSync('diff', diffOptions, runInShell: true);
-    var nlines = '\n'.allMatches(result.stdout as String).length;
+    final diffOptions = ['-r', '-B', outBefore, outAfter];
+    final result = Process.runSync('diff', diffOptions, runInShell: true);
+    final nlines = '\n'.allMatches(result.stdout as String).length;
     print('Diff lines: $nlines');
     print('diff ${diffOptions.join(" ")}');
     return result.exitCode == 0;
@@ -96,7 +96,7 @@
     print('Running dartdoc for $markdownRef...');
     print('==========================================================');
     _doInPath(dartdocDir, () {
-      var returnCode = _updateDartdocPubspec(markdownRef);
+      final returnCode = _updateDartdocPubspec(markdownRef);
       if (returnCode != 0) {
         throw Exception("Could not update dartdoc's pubspec!");
       }
@@ -105,20 +105,20 @@
       if (!sdk) {
         _system('pub', ['upgrade']);
       }
-      var out = Directory.systemTemp
+      final out = Directory.systemTemp
           .createTempSync('dartdoc-compare-${markdownRef}__');
-      var cmd = 'dart';
-      var args = [dartdocBin, '--output=${out.path}'];
+      final cmd = 'dart';
+      final args = [dartdocBin, '--output=${out.path}'];
 
       if (sdk) {
         args.add('--sdk-docs');
       }
 
       print('Command: $cmd ${args.join(' ')}');
-      var startTime = DateTime.now();
+      final startTime = DateTime.now();
       _system(cmd, args);
-      var endTime = DateTime.now();
-      var duration = endTime.difference(startTime).inSeconds;
+      final endTime = DateTime.now();
+      final duration = endTime.difference(startTime).inSeconds;
       print('dartdoc generation for $markdownRef took $duration seconds.');
       print('');
 
@@ -151,7 +151,7 @@
 }
 
 int _system(String cmd, List<String> args) {
-  var result = Process.runSync(cmd, args);
+  final result = Process.runSync(cmd, args);
   print(result.stdout);
   print(result.stderr);
   return result.exitCode;
@@ -162,7 +162,7 @@
     return f();
   }
 
-  var former = Directory.current.path;
+  final former = Directory.current.path;
   Directory.current = path;
   try {
     return f();
diff --git a/pkgs/markdown/tool/expected_output.dart b/pkgs/markdown/tool/expected_output.dart
index 3d3b7e4..c72fdea 100644
--- a/pkgs/markdown/tool/expected_output.dart
+++ b/pkgs/markdown/tool/expected_output.dart
@@ -10,13 +10,13 @@
 /// Parse and yield data cases (each a [DataCase]) from [path].
 Iterable<DataCase> dataCasesInFile(
     {required String path, String? baseDir}) sync* {
-  var file = p.basename(path).replaceFirst(RegExp(r'\..+$'), '');
+  final file = p.basename(path).replaceFirst(RegExp(r'\..+$'), '');
   baseDir ??= p.relative(p.dirname(path), from: p.dirname(p.dirname(path)));
 
   // Explicitly create a File, in case the entry is a Link.
-  var lines = File(path).readAsLinesSync();
+  final lines = File(path).readAsLinesSync();
 
-  var frontMatter = StringBuffer();
+  final frontMatter = StringBuffer();
 
   var i = 0;
 
@@ -26,7 +26,7 @@
 
   while (i < lines.length) {
     var description = lines[i++].replaceFirst(RegExp(r'>>>\s*'), '').trim();
-    var skip = description.startsWith('skip:');
+    final skip = description.startsWith('skip:');
     if (description == '') {
       description = 'line ${i + 1}';
     } else {
@@ -43,7 +43,7 @@
       expectedOutput += lines[i] + '\n';
     }
 
-    var dataCase = DataCase(
+    final dataCase = DataCase(
         directory: baseDir,
         file: file,
         front_matter: frontMatter.toString(),
@@ -65,15 +65,15 @@
   String extension = 'unit',
   bool recursive = true,
 }) {
-  var entries =
+  final entries =
       Directory(directory).listSync(recursive: recursive, followLinks: false);
-  var results = <DataCase>[];
-  for (var entry in entries) {
+  final results = <DataCase>[];
+  for (final entry in entries) {
     if (!entry.path.endsWith(extension)) {
       continue;
     }
 
-    var relativeDir =
+    final relativeDir =
         p.relative(p.dirname(entry.path), from: p.dirname(directory));
 
     results.addAll(dataCasesInFile(path: entry.path, baseDir: relativeDir));
@@ -82,7 +82,7 @@
   // The API makes no guarantees on order. This is just here for stability in
   // tests.
   results.sort((a, b) {
-    var compare = a.directory.compareTo(b.directory);
+    final compare = a.directory.compareTo(b.directory);
     if (compare != 0) return compare;
 
     return a.file.compareTo(b.file);
@@ -107,7 +107,8 @@
 /// import 'package:test/test.dart';
 ///
 /// void main() {
-///   for (var dataCase in dataCasesUnder(library: #my_package.test.this_test)) {
+///   for (final dataCase
+///       in dataCasesUnder(library: #my_package.test.this_test)) {
 ///     // ...
 ///   }
 /// }
@@ -117,12 +118,12 @@
   String extension = 'unit',
   bool recursive = true,
 }) async* {
-  var markdownLibRoot = p.dirname((await Isolate.resolvePackageUri(
+  final markdownLibRoot = p.dirname((await Isolate.resolvePackageUri(
           Uri.parse('package:markdown/markdown.dart')))!
       .toFilePath());
-  var directory =
+  final directory =
       p.joinAll([p.dirname(markdownLibRoot), 'test', testDirectory]);
-  for (var dataCase in _dataCases(
+  for (final dataCase in _dataCases(
       directory: directory, extension: extension, recursive: recursive)) {
     yield dataCase;
   }
diff --git a/pkgs/markdown/tool/stats.dart b/pkgs/markdown/tool/stats.dart
index 1006f94..55ec4ca 100644
--- a/pkgs/markdown/tool/stats.dart
+++ b/pkgs/markdown/tool/stats.dart
@@ -54,11 +54,11 @@
     return;
   }
 
-  var specifiedSection = options['section'] as String?;
-  var raw = options['raw'] as bool;
-  var verbose = options['verbose'] as bool;
-  var verboseLooseMatch = options['verbose-loose'] as bool;
-  var updateFiles = options['update-files'] as bool;
+  final specifiedSection = options['section'] as String?;
+  final raw = options['raw'] as bool;
+  final verbose = options['verbose'] as bool;
+  final verboseLooseMatch = options['verbose-loose'] as bool;
+  final updateFiles = options['update-files'] as bool;
 
   if (updateFiles && (raw || verbose || (specifiedSection != null))) {
     stderr.writeln('The `update-files` flag must be used by itself');
@@ -75,7 +75,7 @@
   final testPrefixes =
       testPrefix == null ? _configs.map((c) => c.prefix) : <String>[testPrefix];
 
-  for (var testPrefix in testPrefixes) {
+  for (final testPrefix in testPrefixes) {
     await _processConfig(testPrefix, raw, updateFiles, verbose,
         specifiedSection, verboseLooseMatch);
   }
@@ -102,19 +102,19 @@
 ) async {
   final config = _configs.singleWhere((c) => c.prefix == testPrefix);
 
-  var sections = loadCommonMarkSections(testPrefix);
+  final sections = loadCommonMarkSections(testPrefix);
 
-  var scores = SplayTreeMap<String, SplayTreeMap<int, CompareLevel>>(
+  final scores = SplayTreeMap<String, SplayTreeMap<int, CompareLevel>>(
       compareAsciiLowerCaseNatural);
 
-  for (var entry in sections.entries) {
+  for (final entry in sections.entries) {
     if (specifiedSection != null && entry.key != specifiedSection) {
       continue;
     }
 
     final units = <DataCase>[];
 
-    for (var e in entry.value) {
+    for (final e in entry.value) {
       final result = compareResult(config, e,
           verboseFail: verbose, verboseLooseMatch: verboseLooseMatch);
 
@@ -127,7 +127,7 @@
                 : result.result!,
       ));
 
-      var nestedMap = scores.putIfAbsent(
+      final nestedMap = scores.putIfAbsent(
           entry.key, () => SplayTreeMap<int, CompareLevel>());
       nestedMap[e.example] = result.compareLevel;
     }
@@ -170,9 +170,9 @@
     }
   }
   if (obj is Map) {
-    var map = <String, Object?>{};
+    final map = <String, Object?>{};
     obj.forEach((k, v) {
-      var newKey = k.toString();
+      final newKey = k.toString();
       map[newKey] = v;
     });
     return map;
@@ -184,14 +184,14 @@
     Map<String, Map<int, CompareLevel>> scores, bool updateFiles) async {
   IOSink sink;
   if (updateFiles) {
-    var file = getStatsFile(testPrefix);
+    final file = getStatsFile(testPrefix);
     print('Updating ${file.path}');
     sink = file.openWrite();
   } else {
     sink = stdout;
   }
 
-  var encoder = const JsonEncoder.withIndent(' ', _convert);
+  final encoder = const JsonEncoder.withIndent(' ', _convert);
   try {
     sink.writeln(encoder.convert(scores));
   } on JsonUnsupportedObjectError catch (e) {
@@ -219,25 +219,25 @@
 
   IOSink sink;
   if (updateFiles) {
-    var path = p.join(toolDir, '${testPrefix}_stats.txt');
+    final path = p.join(toolDir, '${testPrefix}_stats.txt');
     print('Updating $path');
-    var file = File(path);
+    final file = File(path);
     sink = file.openWrite();
   } else {
     sink = stdout;
   }
 
   scores.forEach((section, Map<int, CompareLevel> map) {
-    var total = map.values.length;
+    final total = map.values.length;
     totalExamples += total;
 
-    var sectionStrictCount =
+    final sectionStrictCount =
         map.values.where((val) => val == CompareLevel.strict).length;
 
-    var sectionLooseCount =
+    final sectionLooseCount =
         map.values.where((val) => val == CompareLevel.loose).length;
 
-    var sectionValidCount = sectionStrictCount + sectionLooseCount;
+    final sectionValidCount = sectionStrictCount + sectionLooseCount;
 
     totalStrict += sectionStrictCount;
     totalValid += sectionValidCount;
diff --git a/pkgs/markdown/tool/stats_lib.dart b/pkgs/markdown/tool/stats_lib.dart
index 7439766..b0c7a26 100644
--- a/pkgs/markdown/tool/stats_lib.dart
+++ b/pkgs/markdown/tool/stats_lib.dart
@@ -27,18 +27,18 @@
 
 Map<String, List<CommonMarkTestCase>> loadCommonMarkSections(
     String testPrefix) {
-  var testFile = File(p.join(toolDir, '${testPrefix}_tests.json'));
-  var testsJson = testFile.readAsStringSync();
+  final testFile = File(p.join(toolDir, '${testPrefix}_tests.json'));
+  final testsJson = testFile.readAsStringSync();
 
-  var testArray = jsonDecode(testsJson) as List;
+  final testArray = jsonDecode(testsJson) as List;
 
-  var sections = <String, List<CommonMarkTestCase>>{};
+  final sections = <String, List<CommonMarkTestCase>>{};
 
-  for (var exampleMap in testArray) {
-    var exampleTest =
+  for (final exampleMap in testArray) {
+    final exampleTest =
         CommonMarkTestCase.fromJson(exampleMap as Map<String, dynamic>);
 
-    var sectionList =
+    final sectionList =
         sections.putIfAbsent(exampleTest.section, () => <CommonMarkTestCase>[]);
 
     sectionList.add(exampleTest);
@@ -119,10 +119,10 @@
     return CompareResult(testCase, output, CompareLevel.strict);
   }
 
-  var expectedParsed = parseFragment(testCase.html);
-  var actual = parseFragment(output);
+  final expectedParsed = parseFragment(testCase.html);
+  final actual = parseFragment(output);
 
-  var looseMatch = _compareHtml(expectedParsed.children, actual.children);
+  final looseMatch = _compareHtml(expectedParsed.children, actual.children);
 
   if (!looseMatch && verboseFail) {
     _printVerboseFailure(config.baseUrl, 'FAIL', testCase, output);
@@ -160,8 +160,8 @@
   }
 
   for (var childNum = 0; childNum < expectedElements.length; childNum++) {
-    var expected = expectedElements[childNum];
-    var actual = actualElements[childNum];
+    final expected = expectedElements[childNum];
+    final actual = actualElements[childNum];
 
     if (expected.runtimeType != actual.runtimeType) {
       return false;
@@ -175,15 +175,15 @@
       return false;
     }
 
-    var expectedAttrKeys = expected.attributes.keys.toList();
+    final expectedAttrKeys = expected.attributes.keys.toList();
     expectedAttrKeys.sort();
 
-    var actualAttrKeys = actual.attributes.keys.toList();
+    final actualAttrKeys = actual.attributes.keys.toList();
     actualAttrKeys.sort();
 
     for (var attrNum = 0; attrNum < actualAttrKeys.length; attrNum++) {
-      var expectedAttrKey = expectedAttrKeys[attrNum];
-      var actualAttrKey = actualAttrKeys[attrNum];
+      final expectedAttrKey = expectedAttrKeys[attrNum];
+      final actualAttrKey = actualAttrKeys[attrNum];
 
       if (expectedAttrKey != actualAttrKey) {
         return false;
@@ -195,7 +195,7 @@
       }
     }
 
-    var childrenEqual = _compareHtml(expected.children, actual.children);
+    final childrenEqual = _compareHtml(expected.children, actual.children);
 
     if (!childrenEqual) {
       return false;
diff --git a/pkgs/markdown/tool/update_blns.dart b/pkgs/markdown/tool/update_blns.dart
index 09c1959..589b52f 100644
--- a/pkgs/markdown/tool/update_blns.dart
+++ b/pkgs/markdown/tool/update_blns.dart
@@ -7,11 +7,11 @@
 final _blnsFilePath = 'test/blns.dart';
 
 Future<void> main() async {
-  var client = HttpClient();
+  final client = HttpClient();
   List<String> json;
   try {
-    var request = await client.getUrl(Uri.parse(_blnsJsonRawUrl));
-    var response = await request.close();
+    final request = await client.getUrl(Uri.parse(_blnsJsonRawUrl));
+    final response = await request.close();
     json = (jsonDecode(await response
             .cast<List<int>>()
             .transform(utf8.decoder)
@@ -20,7 +20,7 @@
   } finally {
     client.close();
   }
-  var blnsContent = StringBuffer('''
+  final blnsContent = StringBuffer('''
 // GENERATED FILE. DO NOT EDIT.
 //
 // This file was generated from big-list-of-naughty-strings's JSON file:
@@ -31,8 +31,8 @@
 
 ''');
   blnsContent.writeln('const blns = <String>[');
-  for (var str in json) {
-    var escaped = str
+  for (final str in json) {
+    final escaped = str
         .replaceAll(r'\', r'\\')
         .replaceAll("'", r"\'")
         .replaceAll(r'$', r'\$');
diff --git a/pkgs/markdown/tool/update_emojis.dart b/pkgs/markdown/tool/update_emojis.dart
index ebdf803..864d07a 100644
--- a/pkgs/markdown/tool/update_emojis.dart
+++ b/pkgs/markdown/tool/update_emojis.dart
@@ -10,15 +10,15 @@
 final _emojisFilePath = 'lib/src/emojis.dart';
 
 Future<void> main() async {
-  var client = HttpClient();
-  var request = await client.getUrl(Uri.parse(_emojisJsonRawUrl));
-  var response = await request.close();
-  var json = jsonDecode(
+  final client = HttpClient();
+  final request = await client.getUrl(Uri.parse(_emojisJsonRawUrl));
+  final response = await request.close();
+  final json = jsonDecode(
           await response.cast<List<int>>().transform(utf8.decoder).join(''))
       .map((String alias, dynamic info) =>
           MapEntry(alias, info.cast<String, dynamic>()))
       .cast<String, Map<String, dynamic>>();
-  var emojisContent = StringBuffer('''
+  final emojisContent = StringBuffer('''
 // GENERATED FILE. DO NOT EDIT.
 //
 // This file was generated from emojilib's emoji data file:
@@ -28,7 +28,7 @@
 ''');
   emojisContent.writeln('const emojis = <String, String>{');
   var emojiCount = 0;
-  var ignored = <String>[];
+  final ignored = <String>[];
   json.forEach((String alias, Map<String, dynamic> info) {
     if (info['char'] != null) {
       emojisContent.writeln("  '$alias': '${info['char']}',");