[cross_file] Move from flutter/plugins. (#305)
diff --git a/packages/flutter_template_images/CHANGELOG.md b/packages/flutter_template_images/CHANGELOG.md
index 829ec72..7442d0b 100644
--- a/packages/flutter_template_images/CHANGELOG.md
+++ b/packages/flutter_template_images/CHANGELOG.md
@@ -1,7 +1,12 @@
-## [1.0.1] - 2020-03-30
+## 2.0.0
+
+* Move assets common to all app templates to a new `app_shared` directory.
+* Create `list_detail_app` directory and assets to support new app template.
+
+## 1.0.1
 
 * Moved Windows app template icon for new folder structure.
 
-## [1.0.0] - 2020-03-16
+## 1.0.0
 
 * Windows app template icon.
diff --git a/packages/flutter_template_images/pubspec.yaml b/packages/flutter_template_images/pubspec.yaml
index 8187da5..8b51fb2 100644
--- a/packages/flutter_template_images/pubspec.yaml
+++ b/packages/flutter_template_images/pubspec.yaml
@@ -1,6 +1,6 @@
 name: flutter_template_images
 description: Image files for use in flutter_tools templates.
-version: 1.0.1
+version: 2.0.0
 homepage: https://github.com/flutter/packages/tree/master/packages/flutter_template_images
 
 environment:
diff --git a/packages/flutter_template_images/templates/app/web/icons/Icon-maskable-192.png.copy.tmpl b/packages/flutter_template_images/templates/app_shared/web/icons/Icon-maskable-192.png.copy.tmpl
similarity index 100%
rename from packages/flutter_template_images/templates/app/web/icons/Icon-maskable-192.png.copy.tmpl
rename to packages/flutter_template_images/templates/app_shared/web/icons/Icon-maskable-192.png.copy.tmpl
Binary files differ
diff --git a/packages/flutter_template_images/templates/app/web/icons/Icon-maskable-512.png.copy.tmpl b/packages/flutter_template_images/templates/app_shared/web/icons/Icon-maskable-512.png.copy.tmpl
similarity index 100%
rename from packages/flutter_template_images/templates/app/web/icons/Icon-maskable-512.png.copy.tmpl
rename to packages/flutter_template_images/templates/app_shared/web/icons/Icon-maskable-512.png.copy.tmpl
Binary files differ
diff --git a/packages/flutter_template_images/templates/app/windows.tmpl/runner/resources/app_icon.ico b/packages/flutter_template_images/templates/app_shared/windows.tmpl/runner/resources/app_icon.ico
similarity index 100%
rename from packages/flutter_template_images/templates/app/windows.tmpl/runner/resources/app_icon.ico
rename to packages/flutter_template_images/templates/app_shared/windows.tmpl/runner/resources/app_icon.ico
Binary files differ
diff --git a/packages/flutter_template_images/templates/list_detail_app/assets/images/2.0x/flutter_logo.png b/packages/flutter_template_images/templates/list_detail_app/assets/images/2.0x/flutter_logo.png
new file mode 100644
index 0000000..b65164d
--- /dev/null
+++ b/packages/flutter_template_images/templates/list_detail_app/assets/images/2.0x/flutter_logo.png
Binary files differ
diff --git a/packages/flutter_template_images/templates/list_detail_app/assets/images/3.0x/flutter_logo.png b/packages/flutter_template_images/templates/list_detail_app/assets/images/3.0x/flutter_logo.png
new file mode 100644
index 0000000..97e5dc9
--- /dev/null
+++ b/packages/flutter_template_images/templates/list_detail_app/assets/images/3.0x/flutter_logo.png
Binary files differ
diff --git a/packages/flutter_template_images/templates/list_detail_app/assets/images/flutter_logo.png b/packages/flutter_template_images/templates/list_detail_app/assets/images/flutter_logo.png
new file mode 100644
index 0000000..b5c6ca7
--- /dev/null
+++ b/packages/flutter_template_images/templates/list_detail_app/assets/images/flutter_logo.png
Binary files differ
diff --git a/packages/palette_generator/CHANGELOG.md b/packages/palette_generator/CHANGELOG.md
index 81ff943..786dcc7 100644
--- a/packages/palette_generator/CHANGELOG.md
+++ b/packages/palette_generator/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.3.0
+
+* Migrated to null safety.
+
 ## 0.2.4+1
 
 * Removed a `dart:async` import that isn't required for \>=Dart 2.1.
diff --git a/packages/palette_generator/example/lib/main.dart b/packages/palette_generator/example/lib/main.dart
index 5d14baa..e445e5b 100644
--- a/packages/palette_generator/example/lib/main.dart
+++ b/packages/palette_generator/example/lib/main.dart
@@ -41,20 +41,20 @@
 class ImageColors extends StatefulWidget {
   /// Creates the home page.
   const ImageColors({
-    Key key,
+    Key? key,
     this.title,
-    this.image,
+    required this.image,
     this.imageSize,
   }) : super(key: key);
 
   /// The title that is shown at the top of the page.
-  final String title;
+  final String? title;
 
   /// This is the image provider that is used to load the colors from.
   final ImageProvider image;
 
   /// The dimensions of the image.
-  final Size imageSize;
+  final Size? imageSize;
 
   @override
   _ImageColorsState createState() {
@@ -63,22 +63,24 @@
 }
 
 class _ImageColorsState extends State<ImageColors> {
-  Rect region;
-  Rect dragRegion;
-  Offset startDrag;
-  Offset currentDrag;
-  PaletteGenerator paletteGenerator;
+  Rect? region;
+  Rect? dragRegion;
+  Offset? startDrag;
+  Offset? currentDrag;
+  PaletteGenerator? paletteGenerator;
 
   final GlobalKey imageKey = GlobalKey();
 
   @override
   void initState() {
     super.initState();
-    region = Offset.zero & widget.imageSize;
+    if (widget.imageSize != null) {
+      region = Offset.zero & widget.imageSize!;
+    }
     _updatePaletteGenerator(region);
   }
 
-  Future<void> _updatePaletteGenerator(Rect newRegion) async {
+  Future<void> _updatePaletteGenerator(Rect? newRegion) async {
     paletteGenerator = await PaletteGenerator.fromImageProvider(
       widget.image,
       size: widget.imageSize,
@@ -90,20 +92,21 @@
 
   // Called when the user starts to drag
   void _onPanDown(DragDownDetails details) {
-    final RenderBox box = imageKey.currentContext.findRenderObject();
+    final RenderBox box =
+        imageKey.currentContext!.findRenderObject() as RenderBox;
     final Offset localPosition = box.globalToLocal(details.globalPosition);
     setState(() {
       startDrag = localPosition;
-      currentDrag = startDrag;
-      dragRegion = Rect.fromPoints(startDrag, currentDrag);
+      currentDrag = localPosition;
+      dragRegion = Rect.fromPoints(localPosition, localPosition);
     });
   }
 
   // Called as the user drags: just updates the region, not the colors.
   void _onPanUpdate(DragUpdateDetails details) {
     setState(() {
-      currentDrag += details.delta;
-      dragRegion = Rect.fromPoints(startDrag, currentDrag);
+      currentDrag = currentDrag! + details.delta;
+      dragRegion = Rect.fromPoints(startDrag!, currentDrag!);
     });
   }
 
@@ -118,11 +121,16 @@
 
   // Called when the drag ends. Sets the region, and updates the colors.
   Future<void> _onPanEnd(DragEndDetails details) async {
-    Rect newRegion =
-        (Offset.zero & imageKey.currentContext.size).intersect(dragRegion);
-    if (newRegion.size.width < 4 && newRegion.size.width < 4) {
-      newRegion = Offset.zero & imageKey.currentContext.size;
+    final Size? imageSize = imageKey.currentContext?.size;
+    Rect? newRegion;
+
+    if (imageSize != null) {
+      newRegion = (Offset.zero & imageSize).intersect(dragRegion!);
+      if (newRegion.size.width < 4 && newRegion.size.width < 4) {
+        newRegion = Offset.zero & imageSize;
+      }
     }
+
     await _updatePaletteGenerator(newRegion);
     setState(() {
       region = newRegion;
@@ -136,7 +144,7 @@
     return Scaffold(
       backgroundColor: _kBackgroundColor,
       appBar: AppBar(
-        title: Text(widget.title),
+        title: Text(widget.title ?? ''),
       ),
       body: Column(
         mainAxisSize: MainAxisSize.max,
@@ -155,8 +163,8 @@
                 Image(
                   key: imageKey,
                   image: widget.image,
-                  width: widget.imageSize.width,
-                  height: widget.imageSize.height,
+                  width: widget.imageSize?.width,
+                  height: widget.imageSize?.height,
                 ),
                 // This is the selection rectangle.
                 Positioned.fromRect(
@@ -189,19 +197,20 @@
   ///
   /// The [generator] is optional. If it is null, then the display will
   /// just be an empty container.
-  const PaletteSwatches({Key key, this.generator}) : super(key: key);
+  const PaletteSwatches({Key? key, this.generator}) : super(key: key);
 
   /// The [PaletteGenerator] that contains all of the swatches that we're going
   /// to display.
-  final PaletteGenerator generator;
+  final PaletteGenerator? generator;
 
   @override
   Widget build(BuildContext context) {
     final List<Widget> swatches = <Widget>[];
-    if (generator == null || generator.colors.isEmpty) {
+    final PaletteGenerator? paletteGen = generator;
+    if (paletteGen == null || paletteGen.colors.isEmpty) {
       return Container();
     }
-    for (Color color in generator.colors) {
+    for (Color color in paletteGen.colors) {
       swatches.add(PaletteSwatch(color: color));
     }
     return Column(
@@ -213,17 +222,18 @@
           children: swatches,
         ),
         Container(height: 30.0),
-        PaletteSwatch(label: 'Dominant', color: generator.dominantColor?.color),
         PaletteSwatch(
-            label: 'Light Vibrant', color: generator.lightVibrantColor?.color),
-        PaletteSwatch(label: 'Vibrant', color: generator.vibrantColor?.color),
+            label: 'Dominant', color: paletteGen.dominantColor?.color),
         PaletteSwatch(
-            label: 'Dark Vibrant', color: generator.darkVibrantColor?.color),
+            label: 'Light Vibrant', color: paletteGen.lightVibrantColor?.color),
+        PaletteSwatch(label: 'Vibrant', color: paletteGen.vibrantColor?.color),
         PaletteSwatch(
-            label: 'Light Muted', color: generator.lightMutedColor?.color),
-        PaletteSwatch(label: 'Muted', color: generator.mutedColor?.color),
+            label: 'Dark Vibrant', color: paletteGen.darkVibrantColor?.color),
         PaletteSwatch(
-            label: 'Dark Muted', color: generator.darkMutedColor?.color),
+            label: 'Light Muted', color: paletteGen.lightMutedColor?.color),
+        PaletteSwatch(label: 'Muted', color: paletteGen.mutedColor?.color),
+        PaletteSwatch(
+            label: 'Dark Muted', color: paletteGen.darkMutedColor?.color),
       ],
     );
   }
@@ -234,19 +244,20 @@
 class PaletteSwatch extends StatelessWidget {
   /// Creates a PaletteSwatch.
   ///
-  /// If the [color] argument is omitted, then the swatch will show a
-  /// placeholder instead, to indicate that there is no color.
+  /// If the [paletteColor] has property `isTargetColorFound` as `false`,
+  /// then the swatch will show a placeholder instead, to indicate
+  /// that there is no color.
   const PaletteSwatch({
-    Key key,
+    Key? key,
     this.color,
     this.label,
   }) : super(key: key);
 
-  /// The color of the swatch. May be null.
-  final Color color;
+  /// The color of the swatch.
+  final Color? color;
 
   /// The optional label to display next to the swatch.
-  final String label;
+  final String? label;
 
   @override
   Widget build(BuildContext context) {
@@ -292,7 +303,7 @@
           children: <Widget>[
             swatch,
             Container(width: 5.0),
-            Text(label),
+            Text(label!),
           ],
         ),
       );
diff --git a/packages/palette_generator/example/pubspec.yaml b/packages/palette_generator/example/pubspec.yaml
index 879d4a2..b2b0cbd 100644
--- a/packages/palette_generator/example/pubspec.yaml
+++ b/packages/palette_generator/example/pubspec.yaml
@@ -1,16 +1,16 @@
 name: image_colors
 description: A simple example of how to use the PaletteGenerator to load the palette from an image file.
-
+publish_to: none
 version: 0.1.0
 
-publish_to: none
+environment:
+  sdk: ">=2.12.0 <3.0.0"
 
 dependencies:
-  cupertino_icons: ^0.1.2
   flutter:
     sdk: flutter
   palette_generator:
-    path: ..
+    path: ../
 
 dev_dependencies:
   flutter_test:
@@ -19,7 +19,4 @@
 flutter:
   uses-material-design: true
   assets:
-   - assets/landscape.png
-
-environment:
-  sdk: ">=2.2.0 <3.0.0"
+    - assets/landscape.png
diff --git a/packages/palette_generator/lib/palette_generator.dart b/packages/palette_generator/lib/palette_generator.dart
index 02200ca..2869155 100644
--- a/packages/palette_generator/lib/palette_generator.dart
+++ b/packages/palette_generator/lib/palette_generator.dart
@@ -66,11 +66,10 @@
   /// [PaletteGenerator.fromImage] static function. This constructor is mainly
   /// used for cases when you have your own source of color information and
   /// would like to use the target selection and scoring methods here.
-  ///
-  /// The [paletteColors] argument must not be null.
-  PaletteGenerator.fromColors(this.paletteColors, {this.targets})
-      : assert(paletteColors != null),
-        selectedSwatches = <PaletteTarget, PaletteColor>{} {
+  PaletteGenerator.fromColors(
+    this.paletteColors, {
+    this.targets = const <PaletteTarget>[],
+  }) : selectedSwatches = <PaletteTarget, PaletteColor>{} {
     _sortSwatches();
     _selectSwatches();
   }
@@ -92,16 +91,15 @@
   /// The [targets] are a list of target color types, specified by creating
   /// custom [PaletteTarget]s. By default, this is the list of targets in
   /// [PaletteTarget.baseTargets].
-  ///
-  /// The [image] must not be null.
   static Future<PaletteGenerator> fromImage(
     ui.Image image, {
-    Rect region,
-    int maximumColorCount,
-    List<PaletteFilter> filters,
-    List<PaletteTarget> targets,
+    Rect? region,
+    int maximumColorCount = _defaultCalculateNumberColors,
+    List<PaletteFilter> filters = const <PaletteFilter>[
+      avoidRedBlackWhitePaletteFilter
+    ],
+    List<PaletteTarget> targets = const <PaletteTarget>[],
   }) async {
-    assert(image != null);
     assert(region == null || region != Rect.zero);
     assert(
         region == null ||
@@ -113,8 +111,6 @@
                 region.bottomRight.dy <= image.height),
         'Region $region is outside the image ${image.width}x${image.height}');
 
-    filters ??= <PaletteFilter>[avoidRedBlackWhitePaletteFilter];
-    maximumColorCount ??= _defaultCalculateNumberColors;
     final _ColorCutQuantizer quantizer = _ColorCutQuantizer(
       image,
       maxColors: maximumColorCount,
@@ -153,38 +149,36 @@
   /// The [timeout] describes how long to wait for the image to load before
   /// giving up on it. A value of Duration.zero implies waiting forever. The
   /// default timeout is 15 seconds.
-  ///
-  /// The [imageProvider] and [timeout] arguments must not be null.
   static Future<PaletteGenerator> fromImageProvider(
     ImageProvider imageProvider, {
-    Size size,
-    Rect region,
-    int maximumColorCount,
-    List<PaletteFilter> filters,
-    List<PaletteTarget> targets,
+    Size? size,
+    Rect? region,
+    int maximumColorCount = _defaultCalculateNumberColors,
+    List<PaletteFilter> filters = const <PaletteFilter>[
+      avoidRedBlackWhitePaletteFilter
+    ],
+    List<PaletteTarget> targets = const <PaletteTarget>[],
     Duration timeout = const Duration(seconds: 15),
   }) async {
-    assert(imageProvider != null);
-    assert(timeout != null);
-    assert(region == null || (region != null && size != null));
+    assert(region == null || size != null);
     assert(region == null || region != Rect.zero);
     assert(
         region == null ||
             (region.topLeft.dx >= 0.0 && region.topLeft.dy >= 0.0),
-        'Region $region is outside the image ${size.width}x${size.height}');
-    assert(region == null || size.contains(region.topLeft),
+        'Region $region is outside the image ${size!.width}x${size.height}');
+    assert(region == null || size!.contains(region.topLeft),
         'Region $region is outside the image $size');
     assert(
         region == null ||
-            (region.bottomRight.dx <= size.width &&
+            (region.bottomRight.dx <= size!.width &&
                 region.bottomRight.dy <= size.height),
         'Region $region is outside the image $size');
     final ImageStream stream = imageProvider.resolve(
       ImageConfiguration(size: size, devicePixelRatio: 1.0),
     );
     final Completer<ui.Image> imageCompleter = Completer<ui.Image>();
-    Timer loadFailureTimeout;
-    ImageStreamListener listener;
+    Timer? loadFailureTimeout;
+    late ImageStreamListener listener;
     listener = ImageStreamListener((ImageInfo info, bool synchronousCall) {
       loadFailureTimeout?.cancel();
       stream.removeListener(listener);
@@ -202,7 +196,7 @@
     }
     stream.addListener(listener);
     final ui.Image image = await imageCompleter.future;
-    ui.Rect newRegion = region;
+    ui.Rect? newRegion = region;
     if (size != null && region != null) {
       final double scale = image.width / size.width;
       newRegion = Rect.fromLTRB(
@@ -246,34 +240,34 @@
 
   /// Returns a vibrant color from the palette. Might be null if an appropriate
   /// target color could not be found.
-  PaletteColor get vibrantColor => selectedSwatches[PaletteTarget.vibrant];
+  PaletteColor? get vibrantColor => selectedSwatches[PaletteTarget.vibrant];
 
   /// Returns a light and vibrant color from the palette. Might be null if an
   /// appropriate target color could not be found.
-  PaletteColor get lightVibrantColor =>
+  PaletteColor? get lightVibrantColor =>
       selectedSwatches[PaletteTarget.lightVibrant];
 
   /// Returns a dark and vibrant color from the palette. Might be null if an
   /// appropriate target color could not be found.
-  PaletteColor get darkVibrantColor =>
+  PaletteColor? get darkVibrantColor =>
       selectedSwatches[PaletteTarget.darkVibrant];
 
   /// Returns a muted color from the palette. Might be null if an appropriate
   /// target color could not be found.
-  PaletteColor get mutedColor => selectedSwatches[PaletteTarget.muted];
+  PaletteColor? get mutedColor => selectedSwatches[PaletteTarget.muted];
 
   /// Returns a muted and light color from the palette. Might be null if an
   /// appropriate target color could not be found.
-  PaletteColor get lightMutedColor =>
+  PaletteColor? get lightMutedColor =>
       selectedSwatches[PaletteTarget.lightMuted];
 
   /// Returns a muted and dark color from the palette. Might be null if an
   /// appropriate target color could not be found.
-  PaletteColor get darkMutedColor => selectedSwatches[PaletteTarget.darkMuted];
+  PaletteColor? get darkMutedColor => selectedSwatches[PaletteTarget.darkMuted];
 
   /// The dominant color (the color with the largest population).
-  PaletteColor get dominantColor => _dominantColor;
-  PaletteColor _dominantColor;
+  PaletteColor? get dominantColor => _dominantColor;
+  PaletteColor? _dominantColor;
 
   void _sortSwatches() {
     if (paletteColors.isEmpty) {
@@ -288,18 +282,22 @@
   }
 
   void _selectSwatches() {
-    final Set<PaletteTarget> allTargets = Set<PaletteTarget>.from(
-        (targets ?? <PaletteTarget>[]) + PaletteTarget.baseTargets);
+    final Set<PaletteTarget> allTargets =
+        Set<PaletteTarget>.from(targets + PaletteTarget.baseTargets);
     final Set<Color> usedColors = <Color>{};
     for (PaletteTarget target in allTargets) {
       target._normalizeWeights();
-      selectedSwatches[target] = _generateScoredTarget(target, usedColors);
+      final PaletteColor? targetScore =
+          _generateScoredTarget(target, usedColors);
+      if (targetScore != null) {
+        selectedSwatches[target] = targetScore;
+      }
     }
   }
 
-  PaletteColor _generateScoredTarget(
+  PaletteColor? _generateScoredTarget(
       PaletteTarget target, Set<Color> usedColors) {
-    final PaletteColor maxScoreSwatch =
+    final PaletteColor? maxScoreSwatch =
         _getMaxScoredSwatchForTarget(target, usedColors);
     if (maxScoreSwatch != null && target.isExclusive) {
       // If we have a color, and the target is exclusive, add the color to the
@@ -309,10 +307,10 @@
     return maxScoreSwatch;
   }
 
-  PaletteColor _getMaxScoredSwatchForTarget(
+  PaletteColor? _getMaxScoredSwatchForTarget(
       PaletteTarget target, Set<Color> usedColors) {
     double maxScore = 0.0;
-    PaletteColor maxScoreSwatch;
+    PaletteColor? maxScoreSwatch;
     for (PaletteColor paletteColor in paletteColors) {
       if (_shouldBeScoredForTarget(paletteColor, target, usedColors)) {
         final double score = _generateScore(paletteColor, target);
@@ -352,9 +350,9 @@
       valueScore = target.lightnessWeight *
           (1.0 - (hslColor.lightness - target.targetLightness).abs());
     }
-    if (target.populationWeight > 0.0) {
+    if (_dominantColor != null && target.populationWeight > 0.0) {
       populationScore = target.populationWeight *
-          (paletteColor.population / _dominantColor.population);
+          (paletteColor.population / _dominantColor!.population);
     }
 
     return saturationScore + valueScore + populationScore;
@@ -382,8 +380,6 @@
 ///   * [PaletteGenerator], a class for selecting color palettes from images.
 class PaletteTarget with Diagnosticable {
   /// Creates a [PaletteTarget] for custom palette selection.
-  ///
-  /// None of the arguments can be null.
   PaletteTarget({
     this.minimumSaturation = 0.0,
     this.targetSaturation = 0.5,
@@ -392,37 +388,31 @@
     this.targetLightness = 0.5,
     this.maximumLightness = 1.0,
     this.isExclusive = true,
-  })  : assert(minimumSaturation != null),
-        assert(targetSaturation != null),
-        assert(maximumSaturation != null),
-        assert(minimumLightness != null),
-        assert(targetLightness != null),
-        assert(maximumLightness != null),
-        assert(isExclusive != null);
+  });
 
-  /// The minimum saturation value for this target. Must not be null.
+  /// The minimum saturation value for this target.
   final double minimumSaturation;
 
-  /// The target saturation value for this target. Must not be null.
+  /// The target saturation value for this target.
   final double targetSaturation;
 
-  /// The maximum saturation value for this target. Must not be null.
+  /// The maximum saturation value for this target.
   final double maximumSaturation;
 
-  /// The minimum lightness value for this target. Must not be null.
+  /// The minimum lightness value for this target.
   final double minimumLightness;
 
-  /// The target lightness value for this target. Must not be null.
+  /// The target lightness value for this target.
   final double targetLightness;
 
-  /// The maximum lightness value for this target. Must not be null.
+  /// The maximum lightness value for this target.
   final double maximumLightness;
 
   /// Returns whether any color selected for this target is exclusive for this
   /// target only.
   ///
   /// If false, then the color can also be selected for other targets. Defaults
-  /// to true.  Must not be null.
+  /// to true.
   final bool isExclusive;
 
   /// The weight of importance that a color's saturation value has on selection.
@@ -610,11 +600,8 @@
 ///   * [PaletteGenerator], a class for selecting color palettes from images.
 class PaletteColor with Diagnosticable {
   /// Generate a [PaletteColor].
-  ///
-  /// The `color` and `population` parameters must not be null.
-  PaletteColor(this.color, this.population)
-      : assert(color != null),
-        assert(population != null);
+  PaletteColor(this.color, this.population);
+
   static const double _minContrastTitleText = 3.0;
   static const double _minContrastBodyText = 4.5;
 
@@ -630,29 +617,29 @@
     if (_titleTextColor == null) {
       _ensureTextColorsGenerated();
     }
-    return _titleTextColor;
+    return _titleTextColor!;
   }
 
-  Color _titleTextColor;
+  Color? _titleTextColor;
 
   /// The color of body text for use with this palette color.
   Color get bodyTextColor {
     if (_bodyTextColor == null) {
       _ensureTextColorsGenerated();
     }
-    return _bodyTextColor;
+    return _bodyTextColor!;
   }
 
-  Color _bodyTextColor;
+  Color? _bodyTextColor;
 
   void _ensureTextColorsGenerated() {
     if (_titleTextColor == null || _bodyTextColor == null) {
       const Color white = Color(0xffffffff);
       const Color black = Color(0xff000000);
       // First check white, as most colors will be dark
-      final int lightBodyAlpha =
+      final int? lightBodyAlpha =
           _calculateMinimumAlpha(white, color, _minContrastBodyText);
-      final int lightTitleAlpha =
+      final int? lightTitleAlpha =
           _calculateMinimumAlpha(white, color, _minContrastTitleText);
 
       if (lightBodyAlpha != null && lightTitleAlpha != null) {
@@ -662,12 +649,12 @@
         return;
       }
 
-      final int darkBodyAlpha =
+      final int? darkBodyAlpha =
           _calculateMinimumAlpha(black, color, _minContrastBodyText);
-      final int darkTitleAlpha =
+      final int? darkTitleAlpha =
           _calculateMinimumAlpha(black, color, _minContrastTitleText);
 
-      if (darkBodyAlpha != null && darkBodyAlpha != null) {
+      if (darkBodyAlpha != null && darkTitleAlpha != null) {
         // If we found valid dark values, use them and return
         _bodyTextColor = black.withAlpha(darkBodyAlpha);
         _titleTextColor = black.withAlpha(darkTitleAlpha);
@@ -676,12 +663,12 @@
 
       // If we reach here then we can not find title and body values which use
       // the same lightness, we need to use mismatched values
-      _bodyTextColor = lightBodyAlpha != null //
+      _bodyTextColor = lightBodyAlpha != null
           ? white.withAlpha(lightBodyAlpha)
-          : black.withAlpha(darkBodyAlpha);
-      _titleTextColor = lightTitleAlpha != null //
+          : black.withAlpha(darkBodyAlpha ?? 255);
+      _titleTextColor = lightTitleAlpha != null
           ? white.withAlpha(lightTitleAlpha)
-          : black.withAlpha(darkTitleAlpha);
+          : black.withAlpha(darkTitleAlpha ?? 255);
     }
   }
 
@@ -710,10 +697,8 @@
   //
   // Returns the alpha value in the range 0-255, or null if no value could be
   // calculated.
-  static int _calculateMinimumAlpha(
+  static int? _calculateMinimumAlpha(
       Color foreground, Color background, double minContrastRatio) {
-    assert(foreground != null);
-    assert(background != null);
     assert(background.alpha == 0xff,
         'The background cannot be translucent: $background.');
     double contrastCalculator(Color fg, Color bg, int alpha) {
@@ -744,8 +729,6 @@
     double minContrastRatio,
     _ContrastCalculator calculator,
   ) {
-    assert(foreground != null);
-    assert(background != null);
     assert(background.alpha == 0xff,
         'The background cannot be translucent: $background.');
     const int minAlphaSearchMaxIterations = 10;
@@ -844,9 +827,7 @@
 /// A box that represents a volume in the RGB color space.
 class _ColorVolumeBox {
   _ColorVolumeBox(
-      this._lowerIndex, this._upperIndex, this.histogram, this.colors)
-      : assert(histogram != null),
-        assert(colors != null) {
+      this._lowerIndex, this._upperIndex, this.histogram, this.colors) {
     _fitMinimumBox();
   }
 
@@ -858,15 +839,15 @@
   int _upperIndex;
 
   // The population of colors within this box.
-  int _population;
+  late int _population;
 
   // Bounds in each of the dimensions.
-  int _minRed;
-  int _maxRed;
-  int _minGreen;
-  int _maxGreen;
-  int _minBlue;
-  int _maxBlue;
+  late int _minRed;
+  late int _maxRed;
+  late int _minGreen;
+  late int _maxGreen;
+  late int _minBlue;
+  late int _maxBlue;
 
   int getVolume() {
     return (_maxRed - _minRed + 1) *
@@ -895,7 +876,7 @@
     int count = 0;
     for (int i = _lowerIndex; i <= _upperIndex; i++) {
       final Color color = colors[i];
-      count += histogram[color].value;
+      count += histogram[color]!.value;
       if (color.red > maxRed) {
         maxRed = color.red;
       }
@@ -982,7 +963,6 @@
           final int bValue = makeValue(b.blue, b.green, b.red);
           return aValue.compareTo(bValue);
       }
-      return 0;
     }
 
     // We need to sort the colors in this box based on the longest color
@@ -993,7 +973,7 @@
     colors.replaceRange(_lowerIndex, _upperIndex + 1, colorSubset);
     final int median = (_population / 2).round();
     for (int i = 0, count = 0; i <= colorSubset.length; i++) {
-      count += histogram[colorSubset[i]].value;
+      count += histogram[colorSubset[i]]!.value;
       if (count >= median) {
         // We never want to split on the upperIndex, as this will result in the
         // same box.
@@ -1010,7 +990,7 @@
     int totalPopulation = 0;
     for (int i = _lowerIndex; i <= _upperIndex; i++) {
       final Color color = colors[i];
-      final int colorPopulation = histogram[color].value;
+      final int colorPopulation = histogram[color]!.value;
       totalPopulation += colorPopulation;
       redSum += colorPopulation * color.red;
       greenSum += colorPopulation * color.green;
@@ -1038,12 +1018,12 @@
       <int, Map<int, Map<int, _ColorCount>>>{};
   final DoubleLinkedQueue<Color> _keys = DoubleLinkedQueue<Color>();
 
-  _ColorCount operator [](Color color) {
-    final Map<int, Map<int, _ColorCount>> redMap = _hist[color.red];
+  _ColorCount? operator [](Color color) {
+    final Map<int, Map<int, _ColorCount>>? redMap = _hist[color.red];
     if (redMap == null) {
       return null;
     }
-    final Map<int, _ColorCount> blueMap = redMap[color.blue];
+    final Map<int, _ColorCount>? blueMap = redMap[color.blue];
     if (blueMap == null) {
       return null;
     }
@@ -1057,13 +1037,13 @@
 
     bool newColor = false;
 
-    Map<int, Map<int, _ColorCount>> redMap = _hist[red];
+    Map<int, Map<int, _ColorCount>>? redMap = _hist[red];
     if (redMap == null) {
       _hist[red] = redMap = <int, Map<int, _ColorCount>>{};
       newColor = true;
     }
 
-    Map<int, _ColorCount> blueMap = redMap[blue];
+    Map<int, _ColorCount>? blueMap = redMap[blue];
     if (blueMap == null) {
       redMap[blue] = blueMap = <int, _ColorCount>{};
       newColor = true;
@@ -1082,7 +1062,7 @@
   void removeWhere(bool predicate(Color key)) {
     for (Color key in _keys) {
       if (predicate(key)) {
-        _hist[key.red][key.blue][key.green] = null;
+        _hist[key.red]?[key.blue]?.remove(key.green);
       }
     }
     _keys.removeWhere((Color color) => predicate(color));
@@ -1102,10 +1082,8 @@
     this.image, {
     this.maxColors = PaletteGenerator._defaultCalculateNumberColors,
     this.region,
-    this.filters,
-  })  : assert(image != null),
-        assert(maxColors != null),
-        assert(region == null || region != Rect.zero),
+    this.filters = const <PaletteFilter>[avoidRedBlackWhitePaletteFilter],
+  })  : assert(region == null || region != Rect.zero),
         _paletteColors = <PaletteColor>[];
 
   FutureOr<List<PaletteColor>> get quantizedColors async {
@@ -1120,11 +1098,11 @@
   final List<PaletteColor> _paletteColors;
 
   final int maxColors;
-  final Rect region;
+  final Rect? region;
   final List<PaletteFilter> filters;
 
   Iterable<Color> _getImagePixels(ByteData pixels, int width, int height,
-      {Rect region}) sync* {
+      {Rect? region}) sync* {
     final int rowStride = width * 4;
     int rowStart;
     int rowEnd;
@@ -1161,7 +1139,7 @@
 
   bool _shouldIgnoreColor(Color color) {
     final HSLColor hslColor = HSLColor.fromColor(color);
-    if (filters != null && filters.isNotEmpty) {
+    if (filters.isNotEmpty) {
       for (PaletteFilter filter in filters) {
         if (!filter(hslColor)) {
           return true;
@@ -1187,13 +1165,16 @@
       );
     }
 
-    final ByteData imageData =
+    final ByteData? imageData =
         await image.toByteData(format: ui.ImageByteFormat.rawRgba);
+    if (imageData == null) {
+      throw 'Failed to encode the image.';
+    }
     final Iterable<Color> pixels =
         _getImagePixels(imageData, image.width, image.height, region: region);
     final _ColorHistogram hist = _ColorHistogram();
-    Color currentColor;
-    _ColorCount currentColorCount;
+    Color? currentColor;
+    _ColorCount? currentColorCount;
 
     for (Color pixel in pixels) {
       // Update the histogram, but only for non-zero alpha values, and for the
@@ -1212,7 +1193,7 @@
           hist[colorKey] = currentColorCount = _ColorCount();
         }
       }
-      currentColorCount.value = currentColorCount.value + 1;
+      currentColorCount!.value = currentColorCount.value + 1;
     }
     // Now let's remove any colors that the filters want to ignore.
     hist.removeWhere((Color color) {
@@ -1223,7 +1204,7 @@
       // the colors.
       _paletteColors.clear();
       for (Color color in hist.keys) {
-        _paletteColors.add(PaletteColor(color, hist[color].value));
+        _paletteColors.add(PaletteColor(color, hist[color]!.value));
       }
     } else {
       // We need use quantization to reduce the number of colors
@@ -1263,7 +1244,7 @@
   void _splitBoxes(PriorityQueue<_ColorVolumeBox> queue, final int maxSize) {
     while (queue.length < maxSize) {
       final _ColorVolumeBox colorVolumeBox = queue.removeFirst();
-      if (colorVolumeBox != null && colorVolumeBox.canSplit()) {
+      if (colorVolumeBox.canSplit()) {
         // First split the box, and offer the result
         queue.add(colorVolumeBox.splitBox());
         // Then offer the box back
diff --git a/packages/palette_generator/pubspec.yaml b/packages/palette_generator/pubspec.yaml
index 79ea891..8bc401b 100644
--- a/packages/palette_generator/pubspec.yaml
+++ b/packages/palette_generator/pubspec.yaml
@@ -1,19 +1,18 @@
 name: palette_generator
 description: Flutter package for generating palette colors from a source image.
 homepage: https://github.com/flutter/packages/tree/master/packages/palette_generator
-version: 0.2.4+1
+version: 0.3.0
+
+environment:
+  sdk: ">=2.12.0 <3.0.0"
+  flutter: ">=1.15.21"
 
 dependencies:
-  collection: ^1.14.6
+  collection: ^1.15.0
   flutter:
     sdk: flutter
-  path: ^1.6.1
+  path: ^1.8.0
 
 dev_dependencies:
   flutter_test:
     sdk: flutter
-  mockito: ^3.0.0
-
-environment:
-  sdk: ">=2.2.0 <3.0.0"
-  flutter: ">=1.15.21"
diff --git a/packages/palette_generator/test/palette_generator_test.dart b/packages/palette_generator/test/palette_generator_test.dart
index e85fd6e..336ec8c 100644
--- a/packages/palette_generator/test/palette_generator_test.dart
+++ b/packages/palette_generator/test/palette_generator_test.dart
@@ -73,7 +73,7 @@
 
   test('PaletteGenerator works on 1-pixel wide blue image', () async {
     final PaletteGenerator palette =
-        await PaletteGenerator.fromImageProvider(testImages['tall_blue']);
+        await PaletteGenerator.fromImageProvider(testImages['tall_blue']!);
     expect(palette.paletteColors.length, equals(1));
     expect(palette.paletteColors[0].color,
         within<Color>(distance: 8, from: const Color(0xff0000ff)));
@@ -81,7 +81,7 @@
 
   test('PaletteGenerator works on 1-pixel high red image', () async {
     final PaletteGenerator palette =
-        await PaletteGenerator.fromImageProvider(testImages['wide_red']);
+        await PaletteGenerator.fromImageProvider(testImages['wide_red']!);
     expect(palette.paletteColors.length, equals(1));
     expect(palette.paletteColors[0].color,
         within<Color>(distance: 8, from: const Color(0xffff0000)));
@@ -89,18 +89,19 @@
 
   test('PaletteGenerator finds dominant color and text colors', () async {
     final PaletteGenerator palette =
-        await PaletteGenerator.fromImageProvider(testImages['dominant']);
+        await PaletteGenerator.fromImageProvider(testImages['dominant']!);
     expect(palette.paletteColors.length, equals(3));
-    expect(palette.dominantColor.color,
+    expect(palette.dominantColor, isNotNull);
+    expect(palette.dominantColor!.color,
         within<Color>(distance: 8, from: const Color(0xff0000ff)));
-    expect(palette.dominantColor.titleTextColor,
+    expect(palette.dominantColor!.titleTextColor,
         within<Color>(distance: 8, from: const Color(0x8affffff)));
-    expect(palette.dominantColor.bodyTextColor,
+    expect(palette.dominantColor!.bodyTextColor,
         within<Color>(distance: 8, from: const Color(0xb2ffffff)));
   });
 
   test('PaletteGenerator works with regions', () async {
-    final ImageProvider imageProvider = testImages['dominant'];
+    final ImageProvider imageProvider = testImages['dominant']!;
     Rect region = const Rect.fromLTRB(0.0, 0.0, 100.0, 100.0);
     const Size size = Size(100.0, 100.0);
     PaletteGenerator palette = await PaletteGenerator.fromImageProvider(
@@ -108,27 +109,30 @@
         region: region,
         size: size);
     expect(palette.paletteColors.length, equals(3));
-    expect(palette.dominantColor.color,
+    expect(palette.dominantColor, isNotNull);
+    expect(palette.dominantColor!.color,
         within<Color>(distance: 8, from: const Color(0xff0000ff)));
 
     region = const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0);
     palette = await PaletteGenerator.fromImageProvider(imageProvider,
         region: region, size: size);
     expect(palette.paletteColors.length, equals(1));
-    expect(palette.dominantColor.color,
+    expect(palette.dominantColor, isNotNull);
+    expect(palette.dominantColor!.color,
         within<Color>(distance: 8, from: const Color(0xffff0000)));
 
     region = const Rect.fromLTRB(0.0, 0.0, 30.0, 20.0);
     palette = await PaletteGenerator.fromImageProvider(imageProvider,
         region: region, size: size);
     expect(palette.paletteColors.length, equals(3));
-    expect(palette.dominantColor.color,
+    expect(palette.dominantColor, isNotNull);
+    expect(palette.dominantColor!.color,
         within<Color>(distance: 8, from: const Color(0xff00ff00)));
   });
 
   test('PaletteGenerator works as expected on a real image', () async {
     final PaletteGenerator palette =
-        await PaletteGenerator.fromImageProvider(testImages['landscape']);
+        await PaletteGenerator.fromImageProvider(testImages['landscape']!);
     final List<PaletteColor> expectedSwatches = <PaletteColor>[
       PaletteColor(const Color(0xff3f630c), 10137),
       PaletteColor(const Color(0xff3c4b2a), 4773),
@@ -150,24 +154,30 @@
     final Iterable<Color> expectedColors =
         expectedSwatches.map<Color>((PaletteColor swatch) => swatch.color);
     expect(palette.paletteColors, containsAll(expectedSwatches));
-    expect(palette.vibrantColor.color,
+    expect(palette.vibrantColor, isNotNull);
+    expect(palette.lightVibrantColor, isNotNull);
+    expect(palette.darkVibrantColor, isNotNull);
+    expect(palette.mutedColor, isNotNull);
+    expect(palette.lightMutedColor, isNotNull);
+    expect(palette.darkMutedColor, isNotNull);
+    expect(palette.vibrantColor!.color,
         within<Color>(distance: 8, from: const Color(0xfff6b835)));
-    expect(palette.lightVibrantColor.color,
+    expect(palette.lightVibrantColor!.color,
         within<Color>(distance: 8, from: const Color(0xff82b2e9)));
-    expect(palette.darkVibrantColor.color,
+    expect(palette.darkVibrantColor!.color,
         within<Color>(distance: 8, from: const Color(0xff3f630c)));
-    expect(palette.mutedColor.color,
+    expect(palette.mutedColor!.color,
         within<Color>(distance: 8, from: const Color(0xff6c7fa2)));
-    expect(palette.lightMutedColor.color,
+    expect(palette.lightMutedColor!.color,
         within<Color>(distance: 8, from: const Color(0xffc4b2b2)));
-    expect(palette.darkMutedColor.color,
+    expect(palette.darkMutedColor!.color,
         within<Color>(distance: 8, from: const Color(0xff3c4b2a)));
     expect(palette.colors, containsAllInOrder(expectedColors));
     expect(palette.colors.length, equals(palette.paletteColors.length));
   });
 
   test('PaletteGenerator limits max colors', () async {
-    final ImageProvider imageProvider = testImages['landscape'];
+    final ImageProvider imageProvider = testImages['landscape']!;
     PaletteGenerator palette = await PaletteGenerator.fromImageProvider(
         imageProvider,
         maximumColorCount: 32);
@@ -181,7 +191,7 @@
   });
 
   test('PaletteGenerator Filters work', () async {
-    final ImageProvider imageProvider = testImages['landscape'];
+    final ImageProvider imageProvider = testImages['landscape']!;
     // First, test that supplying the default filter is the same as not supplying one.
     List<PaletteFilter> filters = <PaletteFilter>[
       avoidRedBlackWhitePaletteFilter
@@ -210,7 +220,8 @@
     final Iterable<Color> expectedColors =
         expectedSwatches.map<Color>((PaletteColor swatch) => swatch.color);
     expect(palette.paletteColors, containsAll(expectedSwatches));
-    expect(palette.dominantColor.color,
+    expect(palette.dominantColor, isNotNull);
+    expect(palette.dominantColor!.color,
         within<Color>(distance: 8, from: const Color(0xff3f630c)));
     expect(palette.colors, containsAllInOrder(expectedColors));
 
@@ -240,7 +251,8 @@
         blueSwatches.map<Color>((PaletteColor swatch) => swatch.color);
 
     expect(palette.paletteColors, containsAll(blueSwatches));
-    expect(palette.dominantColor.color,
+    expect(palette.dominantColor, isNotNull);
+    expect(palette.dominantColor!.color,
         within<Color>(distance: 8, from: const Color(0xff4c5c75)));
     expect(palette.colors, containsAllInOrder(expectedBlues));
 
@@ -270,7 +282,8 @@
         blueGreenSwatches.map<Color>((PaletteColor swatch) => swatch.color);
 
     expect(palette.paletteColors, containsAll(blueGreenSwatches));
-    expect(palette.dominantColor.color,
+    expect(palette.dominantColor, isNotNull);
+    expect(palette.dominantColor!.color,
         within<Color>(distance: 8, from: const Color(0xffc8e8f8)));
     expect(palette.colors, containsAllInOrder(expectedBlueGreens));
 
@@ -284,7 +297,7 @@
   });
 
   test('PaletteGenerator targets work', () async {
-    final ImageProvider imageProvider = testImages['landscape'];
+    final ImageProvider imageProvider = testImages['landscape']!;
     // Passing an empty set of targets works the same as passing a null targets
     // list.
     PaletteGenerator palette = await PaletteGenerator.fromImageProvider(
@@ -313,14 +326,18 @@
     expect(palette.darkMutedColor, isNotNull);
     expect(palette.selectedSwatches.length,
         equals(PaletteTarget.baseTargets.length + 2));
-    expect(palette.selectedSwatches[saturationExtremeTargets[0]].color,
-        equals(const Color(0xfff6b835)));
-    expect(palette.selectedSwatches[saturationExtremeTargets[1]].color,
-        equals(const Color(0xff6e80a2)));
+    final PaletteColor? selectedSwatchesFirst =
+        palette.selectedSwatches[saturationExtremeTargets[0]];
+    final PaletteColor? selectedSwatchesSecond =
+        palette.selectedSwatches[saturationExtremeTargets[1]];
+    expect(selectedSwatchesFirst, isNotNull);
+    expect(selectedSwatchesSecond, isNotNull);
+    expect(selectedSwatchesFirst!.color, equals(const Color(0xfff6b835)));
+    expect(selectedSwatchesSecond!.color, equals(const Color(0xff6e80a2)));
   });
 
   test('PaletteGenerator produces consistent results', () async {
-    final ImageProvider imageProvider = testImages['landscape'];
+    final ImageProvider imageProvider = testImages['landscape']!;
 
     PaletteGenerator lastPalette =
         await PaletteGenerator.fromImageProvider(imageProvider);
@@ -334,8 +351,10 @@
       expect(palette.mutedColor, equals(lastPalette.mutedColor));
       expect(palette.lightMutedColor, equals(lastPalette.lightMutedColor));
       expect(palette.darkMutedColor, equals(lastPalette.darkMutedColor));
-      expect(palette.dominantColor.color,
-          within<Color>(distance: 8, from: lastPalette.dominantColor.color));
+      expect(palette.dominantColor, isNotNull);
+      expect(lastPalette.dominantColor, isNotNull);
+      expect(palette.dominantColor!.color,
+          within<Color>(distance: 8, from: lastPalette.dominantColor!.color));
       lastPalette = palette;
     }
   });
diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index 0e5faee..5497e80 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 0.1.22
+
+* Java code generator enhancements:
+  * Added linter tests to CI.
+  * Fixed some linter issues in the Java code.
+
 ## 0.1.21
 
 * Fixed decode method on generated Flutter classes that use null-safety and have
diff --git a/packages/pigeon/README.md b/packages/pigeon/README.md
index f597767..30fa261 100644
--- a/packages/pigeon/README.md
+++ b/packages/pigeon/README.md
@@ -68,6 +68,48 @@
 
 Note: Generics for List and Map aren't supported yet.
 
+## Asynchronous Handlers
+
+By default Pigeon will generate synchronous handlers for messages.  If you want
+to be able to respond to a message asynchronously you can use the `@async`
+annotation as of version 0.1.20.
+
+Example:
+
+```dart
+class Value {
+  int number;
+}
+
+@HostApi()
+abstract class Api2Host {
+  @async
+  Value calculate(Value value);
+}
+```
+
+Generates:
+
+```objc
+// Objc
+@protocol Api2Host
+-(void)calculate:(nullable Value *)input 
+      completion:(void(^)(Value *_Nullable, FlutterError *_Nullable))completion;
+@end
+```
+
+```java
+// Java
+public interface Result<T> {
+   void success(T result);
+}
+
+/** Generated interface from Pigeon that represents a handler of messages from Flutter.*/
+public interface Api2Host {
+   void calculate(Value arg, Result<Value> result);
+}
+```
+
 ## Feedback
 
 File an issue in [flutter/flutter](https://github.com/flutter/flutter) with the
diff --git a/packages/pigeon/example/README.md b/packages/pigeon/example/README.md
index 8b8409d..0c8fdb8 100644
--- a/packages/pigeon/example/README.md
+++ b/packages/pigeon/example/README.md
@@ -120,7 +120,7 @@
 Kotlin can be found at
 [gaaclarke/flutter_plugin_example](https://github.com/gaaclarke/pigeon_plugin_example).
 
-## Swift Add-to-app Example
+## Swift / Kotlin Add-to-app Example
 
 A full example of using Pigeon for add-to-app with Swift on iOS can be found at
-[gaaclarke/GiantsA2A](https://github.com/gaaclarke/GiantsA2A).
+[samples/add_to_app/books](https://github.com/flutter/samples/tree/master/add_to_app/books).
diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart
index d196e26..294962c 100644
--- a/packages/pigeon/lib/generator_tools.dart
+++ b/packages/pigeon/lib/generator_tools.dart
@@ -8,7 +8,7 @@
 import 'ast.dart';
 
 /// The current version of pigeon. This must match the version in pubspec.yaml.
-const String pigeonVersion = '0.1.21';
+const String pigeonVersion = '0.1.22';
 
 /// Read all the content from [stdin] to a String.
 String readStdin() {
diff --git a/packages/pigeon/lib/java_generator.dart b/packages/pigeon/lib/java_generator.dart
index c79a18e..51d21f7 100644
--- a/packages/pigeon/lib/java_generator.dart
+++ b/packages/pigeon/lib/java_generator.dart
@@ -15,7 +15,7 @@
   'Int64List': 'long[]',
   'Float64List': 'double[]',
   'List': 'List<Object>',
-  'Map': 'Map<String, Object>',
+  'Map': 'Map<Object, Object>',
 };
 
 /// Options that control how Java code will be generated.
@@ -169,38 +169,18 @@
         if (func.argType != 'void') {
           indent.writeln('Map<String, Object> inputMap = argInput.toMap();');
         }
-        indent.write('if (callback != null)');
-        indent.scoped('{', '}', () {
-          indent.write('channel.send($sendArgument, channelReply -> ');
-          indent.scoped('{', '});', () {
-            if (func.returnType == 'void') {
-              indent.writeln('callback.reply(null);');
-            } else {
-              indent.writeln('Map outputMap = (Map)channelReply;');
-              indent.writeln('@SuppressWarnings("ConstantConditions")');
-              indent.writeln(
-                  '${func.returnType} output = ${func.returnType}.fromMap(outputMap);');
-              indent.writeln('callback.reply(output);');
-            }
-          });
+        indent.write('channel.send($sendArgument, channelReply -> ');
+        indent.scoped('{', '});', () {
+          if (func.returnType == 'void') {
+            indent.writeln('callback.reply(null);');
+          } else {
+            indent.writeln('Map outputMap = (Map)channelReply;');
+            indent.writeln('@SuppressWarnings("ConstantConditions")');
+            indent.writeln(
+                '${func.returnType} output = ${func.returnType}.fromMap(outputMap);');
+            indent.writeln('callback.reply(output);');
+          }
         });
-        indent.write(' else ');
-        indent.scoped('{', '}', () {
-          indent.writeln('channel.send($sendArgument, null);');
-        });
-      });
-
-      if (func.argType == 'void') {
-        indent.write('public void ${func.name}() ');
-      } else {
-        indent.write('public void ${func.name}(${func.argType} argInput) ');
-      }
-      indent.scoped('{', '}', () {
-        if (func.argType == 'void') {
-          indent.writeln('${func.name}(null);');
-        } else {
-          indent.writeln('${func.name}(argInput, null);');
-        }
       });
     }
   });
diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml
index e534e2d..7560a04 100644
--- a/packages/pigeon/pubspec.yaml
+++ b/packages/pigeon/pubspec.yaml
@@ -1,5 +1,5 @@
 name: pigeon
-version: 0.1.21 # This must match the version in lib/generator_tools.dart
+version: 0.1.22 # This must match the version in lib/generator_tools.dart
 description: Code generator tool to make communication between Flutter and the host platform type-safe and easier.
 homepage: https://github.com/flutter/packages/tree/master/packages/pigeon
 dependencies:
diff --git a/packages/pigeon/run_tests.sh b/packages/pigeon/run_tests.sh
index ae5aa1e..d3dbe0a 100755
--- a/packages/pigeon/run_tests.sh
+++ b/packages/pigeon/run_tests.sh
@@ -8,6 +8,10 @@
 # exit when any command fails
 set -ex
 
+JAVA_LINTER=checkstyle-8.41-all.jar
+JAVA_FORMATTER=google-java-format-1.3-all-deps.jar
+GOOGLE_CHECKS=google_checks.xml
+
 # TODO(blasten): Enable on stable when possible.
 # https://github.com/flutter/flutter/issues/75187
 if [[ "$CHANNEL" == "stable" ]]; then
@@ -76,6 +80,9 @@
     exit 1
   fi
 
+  java -jar $JAVA_FORMATTER $temp_dir/Pigeon.java > $temp_dir/Pigeon.java
+  java -jar $JAVA_LINTER -c $GOOGLE_CHECKS $temp_dir/Pigeon.java
+
   dartfmt -w $temp_dir/pigeon.dart
   dartanalyzer $temp_dir/pigeon.dart --fatal-infos --fatal-warnings --packages ./e2e_tests/test_objc/.packages
 
@@ -99,11 +106,25 @@
 }
 
 ###############################################################################
+# Get java linter / formatter
+###############################################################################
+if [ ! -f "$JAVA_LINTER" ]; then
+  curl -L https://github.com/checkstyle/checkstyle/releases/download/checkstyle-8.41/$JAVA_LINTER > $JAVA_LINTER
+fi
+if [ ! -f "$JAVA_FORMATTER" ]; then
+  curl -L https://github.com/google/google-java-format/releases/download/google-java-format-1.3/$JAVA_FORMATTER > $JAVA_FORMATTER
+fi
+if [ ! -f "$GOOGLE_CHECKS" ]; then
+  curl -L https://raw.githubusercontent.com/checkstyle/checkstyle/master/src/main/resources/$GOOGLE_CHECKS > $GOOGLE_CHECKS
+fi
+
+###############################################################################
 # Dart analysis and unit tests
 ###############################################################################
 pub get
-dartanalyzer bin lib
-pub run test test/
+dart analyze bin
+dart analyze lib
+dart --no-sound-null-safety test
 
 ###############################################################################
 # Execute without arguments test
diff --git a/packages/pigeon/test/java_generator_test.dart b/packages/pigeon/test/java_generator_test.dart
index c453231..398ca69 100644
--- a/packages/pigeon/test/java_generator_test.dart
+++ b/packages/pigeon/test/java_generator_test.dart
@@ -257,7 +257,7 @@
     generateJava(javaOptions, root, sink);
     final String code = sink.toString();
     expect(code, contains('public static class Foobar'));
-    expect(code, contains('private Map<String, Object> field1;'));
+    expect(code, contains('private Map<Object, Object> field1;'));
   });
 
   test('gen nested', () {