enable lint prefer_interpolation_to_compose_strings (#83407)

diff --git a/dev/benchmarks/macrobenchmarks/lib/src/picture_cache.dart b/dev/benchmarks/macrobenchmarks/lib/src/picture_cache.dart
index a5fe1f3..ee9d379 100644
--- a/dev/benchmarks/macrobenchmarks/lib/src/picture_cache.dart
+++ b/dev/benchmarks/macrobenchmarks/lib/src/picture_cache.dart
@@ -213,9 +213,9 @@
     if (count < 10000) {
       return count.toString();
     } else if (count < 100000) {
-      return (count / 10000).toStringAsPrecision(2) + 'w';
+      return '${(count / 10000).toStringAsPrecision(2)}w';
     } else {
-      return (count / 10000).floor().toString() + 'w';
+      return '${(count / 10000).floor()}w';
     }
   }
 
diff --git a/dev/benchmarks/test_apps/stocks/lib/stock_row.dart b/dev/benchmarks/test_apps/stocks/lib/stock_row.dart
index 8d1fce1..8b94c72 100644
--- a/dev/benchmarks/test_apps/stocks/lib/stock_row.dart
+++ b/dev/benchmarks/test_apps/stocks/lib/stock_row.dart
@@ -33,7 +33,7 @@
     final String lastSale = '\$${stock.lastSale.toStringAsFixed(2)}';
     String changeInPrice = '${stock.percentChange.toStringAsFixed(2)}%';
     if (stock.percentChange > 0)
-      changeInPrice = '+' + changeInPrice;
+      changeInPrice = '+$changeInPrice';
     return InkWell(
       key: ValueKey<String>(stock.symbol),
       onTap: _getHandler(onPressed),
diff --git a/dev/benchmarks/test_apps/stocks/lib/stock_symbol_viewer.dart b/dev/benchmarks/test_apps/stocks/lib/stock_symbol_viewer.dart
index d546550..ac7ecc9 100644
--- a/dev/benchmarks/test_apps/stocks/lib/stock_symbol_viewer.dart
+++ b/dev/benchmarks/test_apps/stocks/lib/stock_symbol_viewer.dart
@@ -22,7 +22,7 @@
     final String lastSale = '\$${stock.lastSale.toStringAsFixed(2)}';
     String changeInPrice = '${stock.percentChange.toStringAsFixed(2)}%';
     if (stock.percentChange > 0)
-      changeInPrice = '+' + changeInPrice;
+      changeInPrice = '+$changeInPrice';
 
     final TextStyle headings = Theme.of(context).textTheme.bodyText1!;
     return Container(
diff --git a/dev/bots/analyze.dart b/dev/bots/analyze.dart
index 429b9b3..ed8f93f 100644
--- a/dev/bots/analyze.dart
+++ b/dev/bots/analyze.dart
@@ -243,8 +243,8 @@
   await _verifyNoMissingLicenseForExtension(workingDirectory, 'swift', overrideMinimumMatches ?? 10, _generateLicense('// '));
   await _verifyNoMissingLicenseForExtension(workingDirectory, 'gradle', overrideMinimumMatches ?? 80, _generateLicense('// '));
   await _verifyNoMissingLicenseForExtension(workingDirectory, 'gn', overrideMinimumMatches ?? 0, _generateLicense('# '));
-  await _verifyNoMissingLicenseForExtension(workingDirectory, 'sh', overrideMinimumMatches ?? 1, '#!/usr/bin/env bash\n' + _generateLicense('# '));
-  await _verifyNoMissingLicenseForExtension(workingDirectory, 'bat', overrideMinimumMatches ?? 1, '@ECHO off\n' + _generateLicense('REM '));
+  await _verifyNoMissingLicenseForExtension(workingDirectory, 'sh', overrideMinimumMatches ?? 1, '#!/usr/bin/env bash\n${_generateLicense('# ')}');
+  await _verifyNoMissingLicenseForExtension(workingDirectory, 'bat', overrideMinimumMatches ?? 1, '@ECHO off\n${_generateLicense('REM ')}');
   await _verifyNoMissingLicenseForExtension(workingDirectory, 'ps1', overrideMinimumMatches ?? 1, _generateLicense('# '));
   await _verifyNoMissingLicenseForExtension(workingDirectory, 'html', overrideMinimumMatches ?? 1, '<!DOCTYPE HTML>\n<!-- ${_generateLicense('')} -->', trailingBlank: false);
   await _verifyNoMissingLicenseForExtension(workingDirectory, 'xml', overrideMinimumMatches ?? 1, '<!-- ${_generateLicense('')} -->');
@@ -252,7 +252,7 @@
 
 Future<void> _verifyNoMissingLicenseForExtension(String workingDirectory, String extension, int minimumMatches, String license, { bool trailingBlank = true }) async {
   assert(!license.endsWith('\n'));
-  final String licensePattern = license + '\n' + (trailingBlank ? '\n' : '');
+  final String licensePattern = '$license\n${trailingBlank ? '\n' : ''}';
   final List<String> errors = <String>[];
   await for (final File file in _allFiles(workingDirectory, extension, minimumMatches: minimumMatches)) {
     final String contents = file.readAsStringSync().replaceAll('\r\n', '\n');
@@ -317,13 +317,13 @@
     .map<String>((Directory entity) => path.basename(entity.path))
     .toList()..sort();
   if (!_listEquals<String>(packages, directories)) {
-    errors.add(
-      'flutter/lib/*.dart does not match flutter/lib/src/*/:\n'
-      'These are the exported packages:\n' +
-      packages.map<String>((String path) => '  lib/$path.dart').join('\n') +
-      'These are the directories:\n' +
-      directories.map<String>((String path) => '  lib/src/$path/').join('\n')
-    );
+    errors.add(<String>[
+      'flutter/lib/*.dart does not match flutter/lib/src/*/:',
+      'These are the exported packages:',
+      ...packages.map<String>((String path) => '  lib/$path.dart'),
+      'These are the directories:',
+      ...directories.map<String>((String path) => '  lib/src/$path/')
+    ].join('\n'));
   }
   // Verify that the imports are well-ordered.
   final Map<String, Set<String>> dependencyMap = <String, Set<String>>{};
@@ -347,7 +347,7 @@
         continue;
       // Sanity check before performing _deepSearch, to ensure there's no rogue
       // dependencies.
-      final String validFilenames = dependencyMap.keys.map((String name) => name + '.dart').join(', ');
+      final String validFilenames = dependencyMap.keys.map((String name) => '$name.dart').join(', ');
       errors.add(
         '$key imported package:flutter/$dependency.dart '
         'which is not one of the valid exports { $validFilenames }.\n'
@@ -359,10 +359,7 @@
   for (final String package in dependencyMap.keys) {
     final List<String> loop = _deepSearch<String>(dependencyMap, package);
     if (loop != null) {
-      errors.add(
-        '${yellow}Dependency loop:$reset ' +
-        loop.join(' depends on ')
-      );
+      errors.add('${yellow}Dependency loop:$reset ${loop.join(' depends on ')}');
     }
   }
   // Fail if any errors
diff --git a/dev/bots/test.dart b/dev/bots/test.dart
index f02426c..49248d6 100644
--- a/dev/bots/test.dart
+++ b/dev/bots/test.dart
@@ -636,7 +636,7 @@
     await _runFlutterTest(
       path.join(flutterRoot, 'packages', 'flutter'),
       options: <String>['--dart-define=dart.vm.product=true', ...soundNullSafetyOptions],
-      tests: <String>[ 'test_release' + path.separator ],
+      tests: <String>['test_release${path.separator}'],
     );
   }
 
diff --git a/dev/bots/test/analyze_test.dart b/dev/bots/test/analyze_test.dart
index 52f88d2..1bffc6c 100644
--- a/dev/bots/test/analyze_test.dart
+++ b/dev/bots/test/analyze_test.dart
@@ -41,28 +41,31 @@
 
   test('analyze.dart - verifyDeprecations', () async {
     final String result = await capture(() => verifyDeprecations(testRootPath, minimumMatches: 2), exitCode: 1);
+    final String lines = <String>[
+        'test/analyze-test-input/root/packages/foo/deprecation.dart:12: Deprecation notice does not match required pattern.',
+        'test/analyze-test-input/root/packages/foo/deprecation.dart:18: Deprecation notice should be a grammatically correct sentence and start with a capital letter; see style guide: STYLE_GUIDE_URL',
+        'test/analyze-test-input/root/packages/foo/deprecation.dart:25: Deprecation notice should be a grammatically correct sentence and end with a period.',
+        'test/analyze-test-input/root/packages/foo/deprecation.dart:29: Deprecation notice does not match required pattern.',
+        'test/analyze-test-input/root/packages/foo/deprecation.dart:32: Deprecation notice does not match required pattern.',
+        'test/analyze-test-input/root/packages/foo/deprecation.dart:37: Deprecation notice does not match required pattern.',
+        'test/analyze-test-input/root/packages/foo/deprecation.dart:41: Deprecation notice does not match required pattern.',
+        'test/analyze-test-input/root/packages/foo/deprecation.dart:48: End of deprecation notice does not match required pattern.',
+        'test/analyze-test-input/root/packages/foo/deprecation.dart:51: Unexpected deprecation notice indent.',
+        'test/analyze-test-input/root/packages/foo/deprecation.dart:70: Deprecation notice does not accurately indicate a dev branch version number; please see RELEASES_URL to find the latest dev build version number.',
+        'test/analyze-test-input/root/packages/foo/deprecation.dart:76: Deprecation notice does not accurately indicate a dev branch version number; please see RELEASES_URL to find the latest dev build version number.',
+        'test/analyze-test-input/root/packages/foo/deprecation.dart:82: Deprecation notice does not accurately indicate a dev branch version number; please see RELEASES_URL to find the latest dev build version number.',
+        'test/analyze-test-input/root/packages/foo/deprecation.dart:99: Deprecation notice does not match required pattern. You might have used double quotes (") for the string instead of single quotes (\').',
+      ]
+      .map((String line) {
+        return line
+          .replaceAll('/', Platform.isWindows ? r'\' : '/')
+          .replaceAll('STYLE_GUIDE_URL', 'https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo')
+          .replaceAll('RELEASES_URL', 'https://flutter.dev/docs/development/tools/sdk/releases');
+      })
+      .join('\n');
     expect(result,
       '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
-      +
-      (
-        'test/analyze-test-input/root/packages/foo/deprecation.dart:12: Deprecation notice does not match required pattern.\n'
-        'test/analyze-test-input/root/packages/foo/deprecation.dart:18: Deprecation notice should be a grammatically correct sentence and start with a capital letter; see style guide: STYLE_GUIDE_URL\n'
-        'test/analyze-test-input/root/packages/foo/deprecation.dart:25: Deprecation notice should be a grammatically correct sentence and end with a period.\n'
-        'test/analyze-test-input/root/packages/foo/deprecation.dart:29: Deprecation notice does not match required pattern.\n'
-        'test/analyze-test-input/root/packages/foo/deprecation.dart:32: Deprecation notice does not match required pattern.\n'
-        'test/analyze-test-input/root/packages/foo/deprecation.dart:37: Deprecation notice does not match required pattern.\n'
-        'test/analyze-test-input/root/packages/foo/deprecation.dart:41: Deprecation notice does not match required pattern.\n'
-        'test/analyze-test-input/root/packages/foo/deprecation.dart:48: End of deprecation notice does not match required pattern.\n'
-        'test/analyze-test-input/root/packages/foo/deprecation.dart:51: Unexpected deprecation notice indent.\n'
-        'test/analyze-test-input/root/packages/foo/deprecation.dart:70: Deprecation notice does not accurately indicate a dev branch version number; please see RELEASES_URL to find the latest dev build version number.\n'
-        'test/analyze-test-input/root/packages/foo/deprecation.dart:76: Deprecation notice does not accurately indicate a dev branch version number; please see RELEASES_URL to find the latest dev build version number.\n'
-        'test/analyze-test-input/root/packages/foo/deprecation.dart:82: Deprecation notice does not accurately indicate a dev branch version number; please see RELEASES_URL to find the latest dev build version number.\n'
-        'test/analyze-test-input/root/packages/foo/deprecation.dart:99: Deprecation notice does not match required pattern. You might have used double quotes (") for the string instead of single quotes (\').\n'
-        .replaceAll('/', Platform.isWindows ? r'\' : '/')
-        .replaceAll('STYLE_GUIDE_URL', 'https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo')
-        .replaceAll('RELEASES_URL', 'https://flutter.dev/docs/development/tools/sdk/releases')
-      )
-      +
+      '$lines\n'
       'See: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes\n'
       '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
     );
@@ -70,15 +73,12 @@
 
   test('analyze.dart - verifyNoMissingLicense', () async {
     final String result = await capture(() => verifyNoMissingLicense(testRootPath, checkMinimums: false), exitCode: 1);
+    final String lines = 'test/analyze-test-input/root/packages/foo/foo.dart'
+      .replaceAll('/', Platform.isWindows ? r'\' : '/');
     expect(result,
       '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
-      +
-      (
-        'The following 1 file does not have the right license header:\n'
-        'test/analyze-test-input/root/packages/foo/foo.dart\n'
-        .replaceAll('/', Platform.isWindows ? r'\' : '/')
-      )
-      +
+      'The following 1 file does not have the right license header:\n'
+      '$lines\n'
       'The expected license header is:\n'
       '// Copyright 2014 The Flutter Authors. All rights reserved.\n'
       '// Use of this source code is governed by a BSD-style license that can be\n'
@@ -90,15 +90,15 @@
 
   test('analyze.dart - verifyNoTrailingSpaces', () async {
     final String result = await capture(() => verifyNoTrailingSpaces(testRootPath, minimumMatches: 2), exitCode: 1);
+    final String lines = <String>[
+        'test/analyze-test-input/root/packages/foo/spaces.txt:5: trailing U+0020 space character',
+        'test/analyze-test-input/root/packages/foo/spaces.txt:9: trailing blank line',
+      ]
+      .map((String line) => line.replaceAll('/', Platform.isWindows ? r'\' : '/'))
+      .join('\n');
     expect(result,
       '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
-      +
-      (
-        'test/analyze-test-input/root/packages/foo/spaces.txt:5: trailing U+0020 space character\n'
-        'test/analyze-test-input/root/packages/foo/spaces.txt:9: trailing blank line\n'
-        .replaceAll('/', Platform.isWindows ? r'\' : '/')
-      )
-      +
+      '$lines\n'
       '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
     );
   });
diff --git a/dev/devicelab/bin/tasks/plugin_lint_mac.dart b/dev/devicelab/bin/tasks/plugin_lint_mac.dart
index dbef483..b682383 100644
--- a/dev/devicelab/bin/tasks/plugin_lint_mac.dart
+++ b/dev/devicelab/bin/tasks/plugin_lint_mac.dart
@@ -224,7 +224,7 @@
 
       section('Build Objective-C application with Swift and Objective-C plugins as frameworks');
 
-      objcPodfileContent = 'use_frameworks!\n' + objcPodfileContent;
+      objcPodfileContent = 'use_frameworks!\n$objcPodfileContent';
       objcPodfile.writeAsStringSync(objcPodfileContent, flush: true);
 
       await inDirectory(objcAppPath, () async {
diff --git a/dev/devicelab/lib/framework/apk_utils.dart b/dev/devicelab/lib/framework/apk_utils.dart
index c1d148d..c8d8e92 100644
--- a/dev/devicelab/lib/framework/apk_utils.dart
+++ b/dev/devicelab/lib/framework/apk_utils.dart
@@ -445,7 +445,7 @@
   ];
   final String gradle = path.join(workingDirectory, Platform.isWindows ? 'gradlew.bat' : './gradlew');
   print('┌── $gradle');
-  print('│ ' + File(path.join(workingDirectory, gradle)).readAsLinesSync().join('\n│ '));
+  print(File(path.join(workingDirectory, gradle)).readAsLinesSync().map((String line) => '| $line').join('\n'));
   print('└─────────────────────────────────────────────────────────────────────────────────────');
   print(
     'Running Gradle:\n'
diff --git a/dev/devicelab/lib/framework/cocoon.dart b/dev/devicelab/lib/framework/cocoon.dart
index 749c39d..d4959c2 100644
--- a/dev/devicelab/lib/framework/cocoon.dart
+++ b/dev/devicelab/lib/framework/cocoon.dart
@@ -126,7 +126,7 @@
     if (resultFile.existsSync()) {
       resultFile.deleteSync();
     }
-    logger.fine('Writing results: ' + json.encode(updateRequest));
+    logger.fine('Writing results: ${json.encode(updateRequest)}');
     resultFile.createSync();
     resultFile.writeAsStringSync(json.encode(updateRequest));
   }
diff --git a/dev/devicelab/lib/framework/running_processes.dart b/dev/devicelab/lib/framework/running_processes.dart
index 0be5180..b0b2ce0 100644
--- a/dev/devicelab/lib/framework/running_processes.dart
+++ b/dev/devicelab/lib/framework/running_processes.dart
@@ -148,7 +148,7 @@
       rawTime = '0$rawTime';
     }
     if (rawTime[4] == '/') {
-      rawTime = rawTime.substring(0, 3) + '0' + rawTime.substring(3);
+      rawTime = '${rawTime.substring(0, 3)}0${rawTime.substring(3)}';
     }
     final String year = rawTime.substring(6, 10);
     final String month = rawTime.substring(3, 5);
diff --git a/dev/devicelab/lib/framework/utils.dart b/dev/devicelab/lib/framework/utils.dart
index 7ba1349..05ee37c 100644
--- a/dev/devicelab/lib/framework/utils.dart
+++ b/dev/devicelab/lib/framework/utils.dart
@@ -564,7 +564,8 @@
 }
 
 String jsonEncode(dynamic data) {
-  return const JsonEncoder.withIndent('  ').convert(data) + '\n';
+  final String jsonValue = const JsonEncoder.withIndent('  ').convert(data);
+  return '$jsonValue\n';
 }
 
 Future<void> getNewGallery(String revision, Directory galleryDir) async {
diff --git a/dev/devicelab/lib/tasks/hot_mode_tests.dart b/dev/devicelab/lib/tasks/hot_mode_tests.dart
index 8e478de..bc33b3a 100644
--- a/dev/devicelab/lib/tasks/hot_mode_tests.dart
+++ b/dev/devicelab/lib/tasks/hot_mode_tests.dart
@@ -95,7 +95,7 @@
             if (hotReloadCount == 2) {
               // Trigger a framework invalidation (370 libraries) without modifying the source
               flutterFrameworkSource.writeAsStringSync(
-                flutterFrameworkSource.readAsStringSync() + '\n'
+                '${flutterFrameworkSource.readAsStringSync()}\n'
               );
               process.stdin.writeln('r');
               hotReloadCount += 1;
diff --git a/dev/devicelab/lib/tasks/perf_tests.dart b/dev/devicelab/lib/tasks/perf_tests.dart
index 7630fed..da4fb9a 100644
--- a/dev/devicelab/lib/tasks/perf_tests.dart
+++ b/dev/devicelab/lib/tasks/perf_tests.dart
@@ -1004,7 +1004,7 @@
     sizeMetrics['${metric}_dart2js_size'] = _parseDu(result.stdout as String);
 
     await Process.run('gzip',<String>['-k', '9', fileName]);
-    final ProcessResult resultGzip = await Process.run('du', <String>['-k', fileName + '.gz']);
+    final ProcessResult resultGzip = await Process.run('du', <String>['-k', '$fileName.gz']);
     sizeMetrics['${metric}_dart2js_size_gzip'] = _parseDu(resultGzip.stdout as String);
 
     return sizeMetrics;
diff --git a/dev/devicelab/test/cocoon_test.dart b/dev/devicelab/test/cocoon_test.dart
index 03c9353..460b055 100644
--- a/dev/devicelab/test/cocoon_test.dart
+++ b/dev/devicelab/test/cocoon_test.dart
@@ -217,7 +217,7 @@
 
     test('reads token from service account file with whitespace', () {
       final File serviceAccountFile = fs.file(serviceAccountTokenPath)..createSync();
-      serviceAccountFile.writeAsStringSync(serviceAccountToken + ' \n');
+      serviceAccountFile.writeAsStringSync('$serviceAccountToken \n');
       final AuthenticatedCocoonClient client = AuthenticatedCocoonClient(serviceAccountTokenPath, filesystem: fs);
       expect(client.serviceAccountToken, serviceAccountToken);
     });
diff --git a/dev/integration_tests/flutter_gallery/lib/demo/calculator/logic.dart b/dev/integration_tests/flutter_gallery/lib/demo/calculator/logic.dart
index 9f832a6..e92a765 100644
--- a/dev/integration_tests/flutter_gallery/lib/demo/calculator/logic.dart
+++ b/dev/integration_tests/flutter_gallery/lib/demo/calculator/logic.dart
@@ -36,9 +36,9 @@
   static double _parse(String stringRep) {
     String toParse = stringRep;
     if (toParse.startsWith('.'))
-      toParse = '0' + toParse;
+      toParse = '0$toParse';
     if (toParse.endsWith('.'))
-      toParse = toParse + '0';
+      toParse = '${toParse}0';
     return double.parse(toParse);
   }
 }
@@ -189,7 +189,8 @@
       case ExpressionState.LeadingNeg:
       case ExpressionState.Number:
         final ExpressionToken last = outList.removeLast()!;
-        newToken = FloatToken(last.stringRep! + '.');
+        final String value = last.stringRep!;
+        newToken = FloatToken('$value.');
         break;
       case ExpressionState.Point:
       case ExpressionState.NumberWithPoint:
diff --git a/dev/integration_tests/flutter_gallery/lib/demo/material/chip_demo.dart b/dev/integration_tests/flutter_gallery/lib/demo/material/chip_demo.dart
index 8bd8481..5b17239 100644
--- a/dev/integration_tests/flutter_gallery/lib/demo/material/chip_demo.dart
+++ b/dev/integration_tests/flutter_gallery/lib/demo/material/chip_demo.dart
@@ -220,7 +220,8 @@
     if (_selectedAction.isEmpty) {
       return '';
     }
-    return _capitalize(_results[_selectedAction]!) + '!';
+    final String value = _capitalize(_results[_selectedAction]!);
+    return '$value!';
   }
 
   @override
diff --git a/dev/integration_tests/flutter_gallery/lib/demo/material/text_form_field_demo.dart b/dev/integration_tests/flutter_gallery/lib/demo/material/text_form_field_demo.dart
index 2f5dc6a..395e81c 100644
--- a/dev/integration_tests/flutter_gallery/lib/demo/material/text_form_field_demo.dart
+++ b/dev/integration_tests/flutter_gallery/lib/demo/material/text_form_field_demo.dart
@@ -317,17 +317,20 @@
         selectionIndex++;
     }
     if (newTextLength >= 4) {
-      newText.write(newValue.text.substring(0, usedSubstringIndex = 3) + ') ');
+      final String value = newValue.text.substring(0, usedSubstringIndex = 3);
+      newText.write('$value) ');
       if (newValue.selection.end >= 3)
         selectionIndex += 2;
     }
     if (newTextLength >= 7) {
-      newText.write(newValue.text.substring(3, usedSubstringIndex = 6) + '-');
+      final String value = newValue.text.substring(3, usedSubstringIndex = 6);
+      newText.write('$value-');
       if (newValue.selection.end >= 6)
         selectionIndex++;
     }
     if (newTextLength >= 11) {
-      newText.write(newValue.text.substring(6, usedSubstringIndex = 10) + ' ');
+      final String value = newValue.text.substring(6, usedSubstringIndex = 10);
+      newText.write('$value ');
       if (newValue.selection.end >= 10)
         selectionIndex++;
     }
diff --git a/dev/manual_tests/lib/text.dart b/dev/manual_tests/lib/text.dart
index 82a6d33..6facdf2 100644
--- a/dev/manual_tests/lib/text.dart
+++ b/dev/manual_tests/lib/text.dart
@@ -457,7 +457,8 @@
       case 65: // random emoji
         return String.fromCharCode(0x1F000 + _random.nextInt(0x9FF));
       case 66:
-        return 'Z{' + zalgo(_random, _random.nextInt(4) + 2) + '}Z';
+        final String value = zalgo(_random, _random.nextInt(4) + 2);
+        return 'Z{$value}Z';
       case 67:
         return 'Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν';
       case 68:
@@ -963,7 +964,10 @@
       if (mounted && intrinsicKey.currentContext?.size?.height != controlKey.currentContext?.size?.height) {
         debugPrint('Found some text that unexpectedly renders at different heights.');
         debugPrint('Text: $_text');
-        debugPrint(_text?.runes.map<String>((int index) => 'U+' + index.toRadixString(16).padLeft(4, '0')).join(' '));
+        debugPrint(_text?.runes.map<String>((int index) {
+          final String hexa = index.toRadixString(16).padLeft(4, '0');
+          return 'U+$hexa';
+        }).join(' '));
         setState(() {
           _ticker.stop();
         });
diff --git a/dev/tools/gen_keycodes/bin/gen_keycodes.dart b/dev/tools/gen_keycodes/bin/gen_keycodes.dart
index 4c1d3ae..eae21ae 100644
--- a/dev/tools/gen_keycodes/bin/gen_keycodes.dart
+++ b/dev/tools/gen_keycodes/bin/gen_keycodes.dart
@@ -185,8 +185,10 @@
 
     // Write data files
     const JsonEncoder encoder = JsonEncoder.withIndent('  ');
-    File(parsedArguments['physical-data'] as String).writeAsStringSync(encoder.convert(physicalData.toJson()) + '\n');
-    File(parsedArguments['logical-data'] as String).writeAsStringSync(encoder.convert(logicalData.toJson()) + '\n');
+    final String physicalJson = encoder.convert(physicalData.toJson());
+    File(parsedArguments['physical-data'] as String).writeAsStringSync('$physicalJson\n');
+    final String logicalJson = encoder.convert(logicalData.toJson());
+    File(parsedArguments['logical-data'] as String).writeAsStringSync('$logicalJson\n');
   } else {
     physicalData = PhysicalKeyData.fromJson(json.decode(await File(parsedArguments['physical-data'] as String).readAsString()) as Map<String, dynamic>);
     logicalData = LogicalKeyData.fromJson(json.decode(await File(parsedArguments['logical-data'] as String).readAsString()) as Map<String, dynamic>);
diff --git a/dev/tools/java_and_objc_doc.dart b/dev/tools/java_and_objc_doc.dart
index 21fbd7f..2cdaefb 100644
--- a/dev/tools/java_and_objc_doc.dart
+++ b/dev/tools/java_and_objc_doc.dart
@@ -37,7 +37,7 @@
 
     // On failure print a short snipped from the body in case it's helpful.
     final int bodyLength = min(1024, response.body.length);
-    stderr.writeln('Response status code ${response.statusCode}. Body: ' + response.body.substring(0, bodyLength));
+    stderr.writeln('Response status code ${response.statusCode}. Body: ${response.body.substring(0, bodyLength)}');
     sleep(const Duration(seconds: 1));
   }
   return responseBytes == null ? null : ZipDecoder().decodeBytes(responseBytes);
diff --git a/dev/tools/localization/bin/gen_localizations.dart b/dev/tools/localization/bin/gen_localizations.dart
index faa4899..45db77e 100644
--- a/dev/tools/localization/bin/gen_localizations.dart
+++ b/dev/tools/localization/bin/gen_localizations.dart
@@ -95,7 +95,7 @@
       languageToScriptCodes[locale.languageCode].add(locale.scriptCode);
     }
     if (locale.countryCode != null && locale.scriptCode != null) {
-      final LocaleInfo key = LocaleInfo.fromString(locale.languageCode + '_' + locale.scriptCode);
+      final LocaleInfo key = LocaleInfo.fromString('${locale.languageCode}_${locale.scriptCode}');
       languageAndScriptToCountryCodes[key] ??= <String>{};
       languageAndScriptToCountryCodes[key].add(locale.countryCode);
     }
@@ -150,7 +150,7 @@
       // Language has scriptCodes, so we need to properly fallback countries to corresponding
       // script default values before language default values.
       for (final String scriptCode in languageToScriptCodes[languageName]) {
-        final LocaleInfo scriptBaseLocale = LocaleInfo.fromString(languageName + '_' + scriptCode);
+        final LocaleInfo scriptBaseLocale = LocaleInfo.fromString('${languageName}_$scriptCode');
         output.writeln(generateClassDeclaration(
           scriptBaseLocale,
           generatedClassPrefix,
@@ -170,7 +170,7 @@
         for (final LocaleInfo locale in localeCodes) {
           if (locale.originalString == languageName)
             continue;
-          if (locale.originalString == languageName + '_' + scriptCode)
+          if (locale.originalString == '${languageName}_$scriptCode')
             continue;
           if (locale.scriptCode != scriptCode)
             continue;
@@ -217,12 +217,12 @@
       }
     }
 
-    final String scriptCodeMessage = scriptCodeCount == 0 ? '' : ' and $scriptCodeCount script' + (scriptCodeCount == 1 ? '' : 's');
+    final String scriptCodeMessage = scriptCodeCount == 0 ? '' : ' and $scriptCodeCount script${scriptCodeCount == 1 ? '' : 's'}';
     if (countryCodeCount == 0) {
       if (scriptCodeCount == 0)
         supportedLocales.writeln('///  * `$languageName` - ${describeLocale(languageName)}');
       else
-        supportedLocales.writeln('///  * `$languageName` - ${describeLocale(languageName)} (plus $scriptCodeCount script' + (scriptCodeCount == 1 ? '' : 's') + ')');
+        supportedLocales.writeln('///  * `$languageName` - ${describeLocale(languageName)} (plus $scriptCodeCount script${scriptCodeCount == 1 ? '' : 's'})');
 
     } else if (countryCodeCount == 1) {
       supportedLocales.writeln('///  * `$languageName` - ${describeLocale(languageName)} (plus one country variation$scriptCodeMessage)');
@@ -295,7 +295,7 @@
     case '$language': {
       switch (locale.scriptCode) {''');
       for (final String scriptCode in languageToScriptCodes[language]) {
-        final LocaleInfo scriptLocale = LocaleInfo.fromString(language + '_' + scriptCode);
+        final LocaleInfo scriptLocale = LocaleInfo.fromString('${language}_$scriptCode');
         output.writeln('''
         case '$scriptCode': {''');
         if (languageAndScriptToCountryCodes.containsKey(scriptLocale)) {
@@ -458,7 +458,7 @@
           throw Exception(
             '"$value" is not one of the ICU short time patterns supported '
             'by the material library. Here is the list of supported '
-            'patterns:\n  ' + _icuTimeOfDayToEnum.keys.join('\n  ')
+            'patterns:\n  ${_icuTimeOfDayToEnum.keys.join('\n  ')}'
           );
         }
         return _icuTimeOfDayToEnum[value];
@@ -467,7 +467,7 @@
           throw Exception(
             '"$value" is not one of the scriptCategory values supported '
             'by the material library. Here is the list of supported '
-            'values:\n  ' + _scriptCategoryToEnum.keys.join('\n  ')
+            'values:\n  ${_scriptCategoryToEnum.keys.join('\n  ')}'
           );
         }
         return _scriptCategoryToEnum[value];
diff --git a/dev/tools/localization/localizations_utils.dart b/dev/tools/localization/localizations_utils.dart
index ae1e445..fbf9bbc 100644
--- a/dev/tools/localization/localizations_utils.dart
+++ b/dev/tools/localization/localizations_utils.dart
@@ -96,9 +96,9 @@
       // Update the base string to reflect assumed scriptCodes.
       originalString = languageCode;
       if (scriptCode != null)
-        originalString += '_' + scriptCode;
+        originalString += '_$scriptCode';
       if (countryCode != null)
-        originalString += '_' + countryCode;
+        originalString += '_$countryCode';
     }
 
     return LocaleInfo(
@@ -202,7 +202,7 @@
       // Add an assumed locale to default to when there is no info on scriptOnly locales.
       locale = LocaleInfo.fromString(localeString, deriveScriptCode: true);
       if (locale.scriptCode != null) {
-        final LocaleInfo scriptLocale = LocaleInfo.fromString(locale.languageCode + '_' + locale.scriptCode);
+        final LocaleInfo scriptLocale = LocaleInfo.fromString('${locale.languageCode}_${locale.scriptCode}');
         if (!localeToResources.containsKey(scriptLocale)) {
           assumedLocales.add(scriptLocale);
           localeToResources[scriptLocale] ??= <String, String>{};
diff --git a/dev/tools/mega_gallery.dart b/dev/tools/mega_gallery.dart
index ee7ddce..7eead93 100644
--- a/dev/tools/mega_gallery.dart
+++ b/dev/tools/mega_gallery.dart
@@ -192,6 +192,6 @@
 String _comma(int count) {
   final String str = count.toString();
   if (str.length > 3)
-    return str.substring(0, str.length - 3) + ',' + str.substring(str.length - 3);
+    return '${str.substring(0, str.length - 3)},${str.substring(str.length - 3)}';
   return str;
 }