import 'dart:async'; import 'dart:io'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:spotiflac_android/widgets/player_artwork.dart'; void main() { testWidgets( 'track changes retain the decoded cover while the next image loads', (tester) async { final cache = PaintingBinding.instance.imageCache; addTearDown(() { cache.clear(); cache.clearLiveImages(); }); Future pixel(Color color) async { final recorder = ui.PictureRecorder(); Canvas(recorder).drawColor(color, BlendMode.src); final picture = recorder.endRecording(); final image = await picture.toImage(1, 1); picture.dispose(); return image; } final first = await tester.runAsync(() => pixel(Colors.red)); final second = await tester.runAsync(() => pixel(Colors.blue)); final pending = Completer(); cache.putIfAbsent( FileImage(File('/cover-first.png')), () => OneFrameImageStreamCompleter( Future.value(ImageInfo(image: first!)), ), ); cache.putIfAbsent( FileImage(File('/cover-second.png')), () => OneFrameImageStreamCompleter(pending.future), ); Widget app(String path) => MaterialApp( home: PlayerArtwork( artUri: path, colorScheme: const ColorScheme.dark(), ), ); await tester.pumpWidget(app('/cover-first.png')); await tester.pump(); final displayed = tester.widget(find.byType(RawImage)).image; expect(displayed, isNotNull); await tester.pumpWidget(app('/cover-second.png')); expect( tester.widget(find.byType(RawImage)).image, same(displayed), ); expect(find.byIcon(Icons.music_note), findsNothing); pending.complete(ImageInfo(image: second!)); await tester.pumpAndSettle(); expect( tester.widget(find.byType(RawImage)).image, isNot(same(displayed)), ); expect(find.byIcon(Icons.music_note), findsNothing); await tester.pumpWidget(const SizedBox()); }, ); }