Make FutureBuilder handle SynchronousFuture correctly, reland SynchronousFuture usage in test assets (#115173)
* Make FutureBuilder handle SynchronousFuture correctly * Reland "Load assets in flutter_test without turning event loop. (#115123)" (#115156)" This reverts commit 3895786f9854a73c92406f7f8ccf8db06d3581b7.
This commit is contained in:
parent
0e9ee3679c
commit
9e314ff749
@ -38,11 +38,11 @@ class SynchronousFuture<T> implements Future<T> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<R> then<R>(FutureOr<R> Function(T value) onValue, { Function? onError }) {
|
Future<R> then<R>(FutureOr<R> Function(T value) onValue, { Function? onError }) {
|
||||||
final dynamic result = onValue(_value);
|
final FutureOr<R> result = onValue(_value);
|
||||||
if (result is Future<R>) {
|
if (result is Future<R>) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return SynchronousFuture<R>(result as R);
|
return SynchronousFuture<R>(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -247,17 +247,24 @@ abstract class CachingAssetBundle extends AssetBundle {
|
|||||||
/// An [AssetBundle] that loads resources using platform messages.
|
/// An [AssetBundle] that loads resources using platform messages.
|
||||||
class PlatformAssetBundle extends CachingAssetBundle {
|
class PlatformAssetBundle extends CachingAssetBundle {
|
||||||
@override
|
@override
|
||||||
Future<ByteData> load(String key) async {
|
Future<ByteData> load(String key) {
|
||||||
final Uint8List encoded = utf8.encoder.convert(Uri(path: Uri.encodeFull(key)).path);
|
final Uint8List encoded = utf8.encoder.convert(Uri(path: Uri.encodeFull(key)).path);
|
||||||
final ByteData? asset =
|
final Future<ByteData>? future = ServicesBinding.instance.defaultBinaryMessenger.send('flutter/assets', encoded.buffer.asByteData())?.then((ByteData? asset) {
|
||||||
await ServicesBinding.instance.defaultBinaryMessenger.send('flutter/assets', encoded.buffer.asByteData());
|
if (asset == null) {
|
||||||
if (asset == null) {
|
throw FlutterError.fromParts(<DiagnosticsNode>[
|
||||||
|
_errorSummaryWithKey(key),
|
||||||
|
ErrorDescription('The asset does not exist or has empty data.'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return asset;
|
||||||
|
});
|
||||||
|
if (future == null) {
|
||||||
throw FlutterError.fromParts(<DiagnosticsNode>[
|
throw FlutterError.fromParts(<DiagnosticsNode>[
|
||||||
_errorSummaryWithKey(key),
|
_errorSummaryWithKey(key),
|
||||||
ErrorDescription('The asset does not exist or has empty data.'),
|
ErrorDescription('The asset does not exist or has empty data.'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
return asset;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -645,7 +645,11 @@ class _FutureBuilderState<T> extends State<FutureBuilder<T>> {
|
|||||||
}());
|
}());
|
||||||
|
|
||||||
});
|
});
|
||||||
_snapshot = _snapshot.inState(ConnectionState.waiting);
|
// An implementation like `SynchronousFuture` may have already called the
|
||||||
|
// .then closure. Do not overwrite it in that case.
|
||||||
|
if (_snapshot.connectionState != ConnectionState.done) {
|
||||||
|
_snapshot = _snapshot.inState(ConnectionState.waiting);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
@ -87,6 +88,20 @@ void main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
group('FutureBuilder', () {
|
group('FutureBuilder', () {
|
||||||
|
testWidgets('gives expected snapshot with SynchronousFuture', (WidgetTester tester) async {
|
||||||
|
final SynchronousFuture<String> future = SynchronousFuture<String>('flutter');
|
||||||
|
await tester.pumpWidget(FutureBuilder<String>(
|
||||||
|
future: future,
|
||||||
|
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
|
||||||
|
expect(snapshot.connectionState, ConnectionState.done);
|
||||||
|
expect(snapshot.data, 'flutter');
|
||||||
|
expect(snapshot.error, null);
|
||||||
|
expect(snapshot.stackTrace, null);
|
||||||
|
return const Placeholder();
|
||||||
|
},
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('gracefully handles transition from null future', (WidgetTester tester) async {
|
testWidgets('gracefully handles transition from null future', (WidgetTester tester) async {
|
||||||
final GlobalKey key = GlobalKey();
|
final GlobalKey key = GlobalKey();
|
||||||
await tester.pumpWidget(FutureBuilder<String>(
|
await tester.pumpWidget(FutureBuilder<String>(
|
||||||
|
@ -6,8 +6,8 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
// ignore: deprecated_member_use
|
// ignore: deprecated_member_use
|
||||||
import 'package:test_api/test_api.dart' as test_package;
|
import 'package:test_api/test_api.dart' as test_package;
|
||||||
@ -42,7 +42,7 @@ void mockFlutterAssets() {
|
|||||||
/// platform messages.
|
/// platform messages.
|
||||||
SystemChannels.navigation.setMockMethodCallHandler((MethodCall methodCall) async {});
|
SystemChannels.navigation.setMockMethodCallHandler((MethodCall methodCall) async {});
|
||||||
|
|
||||||
ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData? message) async {
|
ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData? message) {
|
||||||
assert(message != null);
|
assert(message != null);
|
||||||
String key = utf8.decode(message!.buffer.asUint8List());
|
String key = utf8.decode(message!.buffer.asUint8List());
|
||||||
File asset = File(path.join(assetFolderPath, key));
|
File asset = File(path.join(assetFolderPath, key));
|
||||||
@ -62,7 +62,7 @@ void mockFlutterAssets() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Uint8List encoded = Uint8List.fromList(asset.readAsBytesSync());
|
final Uint8List encoded = Uint8List.fromList(asset.readAsBytesSync());
|
||||||
return Future<ByteData>.value(encoded.buffer.asByteData());
|
return SynchronousFuture<ByteData>(encoded.buffer.asByteData());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
@ -90,4 +91,13 @@ void main() {
|
|||||||
binding.idle();
|
binding.idle();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Assets in the tester can be loaded without turning event loop', (WidgetTester tester) async {
|
||||||
|
bool responded = false;
|
||||||
|
// The particular asset does not matter, as long as it exists.
|
||||||
|
rootBundle.load('AssetManifest.json').then((ByteData data) {
|
||||||
|
responded = true;
|
||||||
|
});
|
||||||
|
expect(responded, true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user