Allow changing a clipper's type (ShapeBorderClipper,_BottomAppBarClipper). (#14937)

The downcast was crashing when toggling hasNotch for BottomAppBar.
diff --git a/packages/flutter/lib/src/material/bottom_app_bar.dart b/packages/flutter/lib/src/material/bottom_app_bar.dart
index 2bb5692..515bea7 100644
--- a/packages/flutter/lib/src/material/bottom_app_bar.dart
+++ b/packages/flutter/lib/src/material/bottom_app_bar.dart
@@ -152,7 +152,11 @@
   }
 
   @override
-  bool shouldReclip(covariant _BottomAppBarClipper oldClipper) {
-    return oldClipper.geometry != geometry;
+  bool shouldReclip(CustomClipper<Path> oldClipper) {
+    if (oldClipper.runtimeType != _BottomAppBarClipper)
+      return true;
+
+    final _BottomAppBarClipper typedOldClipper = oldClipper;
+    return typedOldClipper.geometry != geometry;
   }
 }
diff --git a/packages/flutter/lib/src/rendering/proxy_box.dart b/packages/flutter/lib/src/rendering/proxy_box.dart
index 779a923..b1629d6 100644
--- a/packages/flutter/lib/src/rendering/proxy_box.dart
+++ b/packages/flutter/lib/src/rendering/proxy_box.dart
@@ -1080,8 +1080,11 @@
   }
 
   @override
-  bool shouldReclip(covariant ShapeBorderClipper oldClipper) {
-    return oldClipper.shape != shape;
+  bool shouldReclip(CustomClipper<Path> oldClipper) {
+    if (oldClipper.runtimeType != ShapeBorderClipper)
+      return true;
+    final ShapeBorderClipper typedOldClipper = oldClipper;
+    return typedOldClipper.shape != shape;
   }
 }
 
diff --git a/packages/flutter/test/material/bottom_app_bar_test.dart b/packages/flutter/test/material/bottom_app_bar_test.dart
index 9ebb5da..17489fb 100644
--- a/packages/flutter/test/material/bottom_app_bar_test.dart
+++ b/packages/flutter/test/material/bottom_app_bar_test.dart
@@ -86,6 +86,40 @@
     expect(physicalShape.color, const Color(0xff0000ff));
   });
 
+  // This is a regression test for a bug we had where toggling hasNotch
+  // will crash, as the shouldReclip method of ShapeBorderClipper or
+  // _BottomAppBarClipper will try an illegal downcast.
+  testWidgets('toggle hasNotch', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      new MaterialApp(
+        home: const Scaffold(
+          bottomNavigationBar: const BottomAppBar(
+            hasNotch: true,
+          ),
+        ),
+      ),
+    );
+
+    await tester.pumpWidget(
+      new MaterialApp(
+        home: const Scaffold(
+          bottomNavigationBar: const BottomAppBar(
+            hasNotch: false,
+          ),
+        ),
+      ),
+    );
+
+    await tester.pumpWidget(
+      new MaterialApp(
+        home: const Scaffold(
+          bottomNavigationBar: const BottomAppBar(
+            hasNotch: true,
+          ),
+        ),
+      ),
+    );
+  });
   // TODO(amirh): test a BottomAppBar with hasNotch=false and an overlapping
   // FAB.
   //