Clear ImageCache on MemoryPressure (#53959)

diff --git a/packages/flutter/lib/src/painting/binding.dart b/packages/flutter/lib/src/painting/binding.dart
index d5f00a9..900d68e 100644
--- a/packages/flutter/lib/src/painting/binding.dart
+++ b/packages/flutter/lib/src/painting/binding.dart
@@ -99,6 +99,12 @@
     imageCache.clearLiveImages();
   }
 
+  @override
+  void handleMemoryPressure() {
+    super.handleMemoryPressure();
+    imageCache.clear();
+  }
+
   /// Listenable that notifies when the available fonts on the system have
   /// changed.
   ///
diff --git a/packages/flutter/lib/src/services/binding.dart b/packages/flutter/lib/src/services/binding.dart
index 227b907..0c4ceed 100644
--- a/packages/flutter/lib/src/services/binding.dart
+++ b/packages/flutter/lib/src/services/binding.dart
@@ -48,13 +48,32 @@
     return const _DefaultBinaryMessenger._();
   }
 
+
+  /// Called when the operating system notifies the application of a memory
+  /// pressure situation.
+  ///
+  /// This method exposes the `memoryPressure` notification from
+  /// [SystemChannels.system].
+  @protected
+  @mustCallSuper
+  void handleMemoryPressure() { }
+
   /// Handler called for messages received on the [SystemChannels.system]
   /// message channel.
   ///
   /// Other bindings may override this to respond to incoming system messages.
   @protected
   @mustCallSuper
-  Future<void> handleSystemMessage(Object systemMessage) async { }
+  Future<void> handleSystemMessage(Object systemMessage) async {
+    final Map<String, dynamic> message = systemMessage as Map<String, dynamic>;
+    final String type = message['type'] as String;
+    switch (type) {
+      case 'memoryPressure':
+        handleMemoryPressure();
+        break;
+    }
+    return;
+  }
 
   /// Adds relevant licenses to the [LicenseRegistry].
   ///
diff --git a/packages/flutter/lib/src/widgets/binding.dart b/packages/flutter/lib/src/widgets/binding.dart
index d511001..a8f0183 100644
--- a/packages/flutter/lib/src/widgets/binding.dart
+++ b/packages/flutter/lib/src/widgets/binding.dart
@@ -655,32 +655,13 @@
       observer.didChangeAppLifecycleState(state);
   }
 
-  /// Called when the operating system notifies the application of a memory
-  /// pressure situation.
-  ///
-  /// Notifies all the observers using
-  /// [WidgetsBindingObserver.didHaveMemoryPressure].
-  ///
-  /// This method exposes the `memoryPressure` notification from
-  /// [SystemChannels.system].
+  @override
   void handleMemoryPressure() {
+    super.handleMemoryPressure();
     for (final WidgetsBindingObserver observer in _observers)
       observer.didHaveMemoryPressure();
   }
 
-  @override
-  Future<void> handleSystemMessage(Object systemMessage) async {
-    await super.handleSystemMessage(systemMessage);
-    final Map<String, dynamic> message = systemMessage as Map<String, dynamic>;
-    final String type = message['type'] as String;
-    switch (type) {
-      case 'memoryPressure':
-        handleMemoryPressure();
-        break;
-    }
-    return;
-  }
-
   bool _needToReportFirstFrame = true;
 
   final Completer<void> _firstFrameCompleter = Completer<void>();
diff --git a/packages/flutter/test/painting/binding_test.dart b/packages/flutter/test/painting/binding_test.dart
index f216b1a..74b5515 100644
--- a/packages/flutter/test/painting/binding_test.dart
+++ b/packages/flutter/test/painting/binding_test.dart
@@ -2,8 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'dart:typed_data' show Uint8List;
-import 'dart:ui';
+import 'dart:ui' as ui;
 
 import 'package:flutter/foundation.dart';
 import 'package:flutter/services.dart';
@@ -11,21 +10,22 @@
 import 'package:flutter/widgets.dart';
 import 'package:flutter/painting.dart';
 
-import 'image_data.dart';
-import 'painting_utils.dart';
 
 void main() {
-  final PaintingBindingSpy binding = PaintingBindingSpy();
+  testWidgets('didHaveMemoryPressure clears imageCache', (WidgetTester tester) async {
+    imageCache.putIfAbsent(1, () => OneFrameImageStreamCompleter(
+      Future<ImageInfo>.value(ImageInfo(
+        image: FakeImage(),
+        scale: 1.0,
+      ),
+    )));
 
-  test('instantiateImageCodec used for loading images', () async {
-    expect(binding.instantiateImageCodecCalledCount, 0);
-
-    final Uint8List bytes = Uint8List.fromList(kTransparentImage);
-    final MemoryImage memoryImage = MemoryImage(bytes);
-    memoryImage.load(memoryImage, (Uint8List bytes, {int cacheWidth, int cacheHeight}) {
-      return PaintingBinding.instance.instantiateImageCodec(bytes, cacheWidth: cacheWidth, cacheHeight: cacheHeight);
-    });
-    expect(binding.instantiateImageCodecCalledCount, 1);
+    await tester.idle();
+    expect(imageCache.currentSize, 1);
+    final ByteData message = const JSONMessageCodec().encodeMessage(
+      <String, dynamic>{'type': 'memoryPressure'});
+    await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('flutter/system', message, (_) { });
+    expect(imageCache.currentSize, 0);
   });
 
   test('evict clears live references', () async {
@@ -84,7 +84,7 @@
   void unlocked() {}
 
   @override
-  Window get window => throw UnimplementedError();
+  ui.Window get window => throw UnimplementedError();
 }
 
 class TestPaintingBinding extends TestBindingBase with ServicesBinding, PaintingBinding {
@@ -111,4 +111,20 @@
     liveClearCount += 1;
     super.clearLiveImages();
   }
-}
\ No newline at end of file
+}
+
+class FakeImage implements ui.Image {
+  @override
+  void dispose() {}
+
+  @override
+  int get height => 10;
+
+  @override
+  Future<ByteData> toByteData({ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) {
+    throw UnimplementedError();
+  }
+
+  @override
+  int get width => 10;
+}