[flutter_tools] run web unit tests in sound null safety (#70799)
This commit is contained in:
parent
6d521e9aff
commit
a76289bb1b
@ -1126,13 +1126,13 @@ Future<void> _runFlutterWebTest(String workingDirectory, List<String> tests) asy
|
|||||||
'--concurrency=1', // do not parallelize on Cirrus, to reduce flakiness
|
'--concurrency=1', // do not parallelize on Cirrus, to reduce flakiness
|
||||||
'-v',
|
'-v',
|
||||||
'--platform=chrome',
|
'--platform=chrome',
|
||||||
|
'--sound-null-safety', // web tests do not autodetect yet.
|
||||||
...?flutterTestArgs,
|
...?flutterTestArgs,
|
||||||
...tests,
|
...tests,
|
||||||
],
|
],
|
||||||
workingDirectory: workingDirectory,
|
workingDirectory: workingDirectory,
|
||||||
environment: <String, String>{
|
environment: <String, String>{
|
||||||
'FLUTTER_WEB': 'true',
|
'FLUTTER_WEB': 'true',
|
||||||
'FLUTTER_LOW_RESOURCE_MODE': 'true',
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// @dart = 2.8
|
|
||||||
import 'dart:js' as js;
|
import 'dart:js' as js;
|
||||||
|
|
||||||
import 'package:flutter_driver/src/extension/_extension_web.dart';
|
import 'package:flutter_driver/src/extension/_extension_web.dart';
|
||||||
@ -10,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart';
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('test web_extension', () {
|
group('test web_extension', () {
|
||||||
Future<Map<String, dynamic>> Function(Map<String, String>) call;
|
late Future<Map<String, dynamic>> Function(Map<String, String>) call;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
call = (Map<String, String> args) async {
|
call = (Map<String, String> args) async {
|
||||||
|
@ -35,18 +35,9 @@ import '../globals.dart' as globals;
|
|||||||
import '../project.dart';
|
import '../project.dart';
|
||||||
import '../web/bootstrap.dart';
|
import '../web/bootstrap.dart';
|
||||||
import '../web/chrome.dart';
|
import '../web/chrome.dart';
|
||||||
|
import '../web/compile.dart';
|
||||||
import '../web/memory_fs.dart';
|
import '../web/memory_fs.dart';
|
||||||
|
|
||||||
/// Web rendering backend mode.
|
|
||||||
enum WebRendererMode {
|
|
||||||
/// Auto detects which rendering backend to use.
|
|
||||||
autoDetect,
|
|
||||||
/// Always uses canvaskit.
|
|
||||||
canvaskit,
|
|
||||||
/// Always uses html.
|
|
||||||
html,
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef DwdsLauncher = Future<Dwds> Function(
|
typedef DwdsLauncher = Future<Dwds> Function(
|
||||||
{@required AssetReader assetReader,
|
{@required AssetReader assetReader,
|
||||||
@required Stream<BuildResult> buildResults,
|
@required Stream<BuildResult> buildResults,
|
||||||
@ -549,46 +540,14 @@ class WebAssetServer implements AssetReader {
|
|||||||
return webSdkFile;
|
return webSdkFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const Map<WebRendererMode, Map<NullSafetyMode, Artifact>> _dartSdkJsArtifactMap =
|
|
||||||
<WebRendererMode, Map<NullSafetyMode, Artifact>> {
|
|
||||||
WebRendererMode.autoDetect: <NullSafetyMode, Artifact> {
|
|
||||||
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitAndHtmlSoundSdk,
|
|
||||||
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitAndHtmlSdk,
|
|
||||||
},
|
|
||||||
WebRendererMode.canvaskit: <NullSafetyMode, Artifact> {
|
|
||||||
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitSoundSdk,
|
|
||||||
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitSdk,
|
|
||||||
},
|
|
||||||
WebRendererMode.html: <NullSafetyMode, Artifact> {
|
|
||||||
NullSafetyMode.sound: Artifact.webPrecompiledSoundSdk,
|
|
||||||
NullSafetyMode.unsound: Artifact.webPrecompiledSdk,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const Map<WebRendererMode, Map<NullSafetyMode, Artifact>> _dartSdkJsMapArtifactMap =
|
|
||||||
<WebRendererMode, Map<NullSafetyMode, Artifact>> {
|
|
||||||
WebRendererMode.autoDetect: <NullSafetyMode, Artifact> {
|
|
||||||
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps,
|
|
||||||
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps,
|
|
||||||
},
|
|
||||||
WebRendererMode.canvaskit: <NullSafetyMode, Artifact> {
|
|
||||||
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps,
|
|
||||||
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitSdkSourcemaps,
|
|
||||||
},
|
|
||||||
WebRendererMode.html: <NullSafetyMode, Artifact> {
|
|
||||||
NullSafetyMode.sound: Artifact.webPrecompiledSoundSdkSourcemaps,
|
|
||||||
NullSafetyMode.unsound: Artifact.webPrecompiledSdkSourcemaps,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
File get _resolveDartSdkJsFile =>
|
File get _resolveDartSdkJsFile =>
|
||||||
globals.fs.file(globals.artifacts.getArtifactPath(
|
globals.fs.file(globals.artifacts.getArtifactPath(
|
||||||
_dartSdkJsArtifactMap[webRenderer][_nullSafetyMode]
|
kDartSdkJsArtifactMap[webRenderer][_nullSafetyMode]
|
||||||
));
|
));
|
||||||
|
|
||||||
File get _resolveDartSdkJsMapFile =>
|
File get _resolveDartSdkJsMapFile =>
|
||||||
globals.fs.file(globals.artifacts.getArtifactPath(
|
globals.fs.file(globals.artifacts.getArtifactPath(
|
||||||
_dartSdkJsMapArtifactMap[webRenderer][_nullSafetyMode]
|
kDartSdkJsMapArtifactMap[webRenderer][_nullSafetyMode]
|
||||||
));
|
));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
import 'package:package_config/package_config.dart';
|
||||||
|
|
||||||
import '../artifacts.dart';
|
import '../artifacts.dart';
|
||||||
import '../base/common.dart';
|
import '../base/common.dart';
|
||||||
@ -10,6 +11,7 @@ import '../base/file_system.dart';
|
|||||||
import '../build_info.dart';
|
import '../build_info.dart';
|
||||||
import '../bundle.dart';
|
import '../bundle.dart';
|
||||||
import '../compile.dart';
|
import '../compile.dart';
|
||||||
|
import '../dart/language_version.dart';
|
||||||
import '../globals.dart' as globals;
|
import '../globals.dart' as globals;
|
||||||
import '../web/compile.dart';
|
import '../web/compile.dart';
|
||||||
import '../web/memory_fs.dart';
|
import '../web/memory_fs.dart';
|
||||||
@ -27,13 +29,24 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
|
|||||||
@required List<String> testFiles,
|
@required List<String> testFiles,
|
||||||
@required BuildInfo buildInfo,
|
@required BuildInfo buildInfo,
|
||||||
}) async {
|
}) async {
|
||||||
if (buildInfo.nullSafetyMode == NullSafetyMode.sound) {
|
LanguageVersion languageVersion = LanguageVersion(2, 8);
|
||||||
throwToolExit('flutter test --platform=chrome does not currently support sound mode');
|
Artifact platformDillArtifact;
|
||||||
}
|
// TODO(jonahwilliams): to support autodetect this would need to partition the source code into a
|
||||||
|
// a sound and unsound set and perform separate compilations.
|
||||||
final List<String> extraFrontEndOptions = List<String>.of(buildInfo.extraFrontEndOptions ?? <String>[]);
|
final List<String> extraFrontEndOptions = List<String>.of(buildInfo.extraFrontEndOptions ?? <String>[]);
|
||||||
if (!extraFrontEndOptions.contains('--no-sound-null-safety')) {
|
if (buildInfo.nullSafetyMode == NullSafetyMode.unsound || buildInfo.nullSafetyMode == NullSafetyMode.autodetect) {
|
||||||
extraFrontEndOptions.add('--no-sound-null-safety');
|
platformDillArtifact = Artifact.webPlatformKernelDill;
|
||||||
|
if (!extraFrontEndOptions.contains('--no-sound-null-safety')) {
|
||||||
|
extraFrontEndOptions.add('--no-sound-null-safety');
|
||||||
|
}
|
||||||
|
} else if (buildInfo.nullSafetyMode == NullSafetyMode.sound) {
|
||||||
|
platformDillArtifact = Artifact.webPlatformSoundKernelDill;
|
||||||
|
languageVersion = nullSafeVersion;
|
||||||
|
if (!extraFrontEndOptions.contains('--sound-null-safety')) {
|
||||||
|
extraFrontEndOptions.add('--sound-null-safety');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final Directory outputDirectory = globals.fs.directory(testOutputDir)
|
final Directory outputDirectory = globals.fs.directory(testOutputDir)
|
||||||
..createSync(recursive: true);
|
..createSync(recursive: true);
|
||||||
final List<File> generatedFiles = <File>[];
|
final List<File> generatedFiles = <File>[];
|
||||||
@ -44,12 +57,12 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
|
|||||||
globals.fs.path.join(outputDirectory.path, '${relativeTestSegments.join('_')}.test.dart'));
|
globals.fs.path.join(outputDirectory.path, '${relativeTestSegments.join('_')}.test.dart'));
|
||||||
generatedFile
|
generatedFile
|
||||||
..createSync(recursive: true)
|
..createSync(recursive: true)
|
||||||
..writeAsStringSync(_generateEntrypoint(relativeTestSegments.join('/'), testFilePath));
|
..writeAsStringSync(_generateEntrypoint(relativeTestSegments.join('/'), testFilePath, languageVersion));
|
||||||
generatedFiles.add(generatedFile);
|
generatedFiles.add(generatedFile);
|
||||||
}
|
}
|
||||||
// Generate a fake main file that imports all tests to be executed. This will force
|
// Generate a fake main file that imports all tests to be executed. This will force
|
||||||
// each of them to be compiled.
|
// each of them to be compiled.
|
||||||
final StringBuffer buffer = StringBuffer('// @dart=2.8\n');
|
final StringBuffer buffer = StringBuffer('// @dart=${languageVersion.major}.${languageVersion.minor}\n');
|
||||||
for (final File generatedFile in generatedFiles) {
|
for (final File generatedFile in generatedFiles) {
|
||||||
buffer.writeln('import "${globals.fs.path.basename(generatedFile.path)}";');
|
buffer.writeln('import "${globals.fs.path.basename(generatedFile.path)}";');
|
||||||
}
|
}
|
||||||
@ -77,7 +90,7 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
|
|||||||
targetModel: TargetModel.dartdevc,
|
targetModel: TargetModel.dartdevc,
|
||||||
extraFrontEndOptions: extraFrontEndOptions,
|
extraFrontEndOptions: extraFrontEndOptions,
|
||||||
platformDill: globals.fs.file(globals.artifacts
|
platformDill: globals.fs.file(globals.artifacts
|
||||||
.getArtifactPath(Artifact.webPlatformKernelDill, mode: buildInfo.mode))
|
.getArtifactPath(platformDillArtifact, mode: buildInfo.mode))
|
||||||
.absolute.uri.toString(),
|
.absolute.uri.toString(),
|
||||||
dartDefines: buildInfo.dartDefines,
|
dartDefines: buildInfo.dartDefines,
|
||||||
librariesSpec: globals.fs.file(globals.artifacts
|
librariesSpec: globals.fs.file(globals.artifacts
|
||||||
@ -106,9 +119,9 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
|
|||||||
..write(codeFile, manifestFile, sourcemapFile, metadataFile);
|
..write(codeFile, manifestFile, sourcemapFile, metadataFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _generateEntrypoint(String relativeTestPath, String absolutePath) {
|
String _generateEntrypoint(String relativeTestPath, String absolutePath, LanguageVersion languageVersion) {
|
||||||
return '''
|
return '''
|
||||||
// @dart = 2.8
|
// @dart = ${languageVersion.major}.${languageVersion.minor}
|
||||||
import 'org-dartlang-app:///$relativeTestPath' as test;
|
import 'org-dartlang-app:///$relativeTestPath' as test;
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
import 'dart:html';
|
import 'dart:html';
|
||||||
@ -137,7 +150,7 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
|
|||||||
postMessageChannel().pipe(channel);
|
postMessageChannel().pipe(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamChannel serializeSuite(Function getMain(), {bool hidePrints = true, Future beforeLoad()}) => RemoteListener.start(getMain, hidePrints: hidePrints, beforeLoad: beforeLoad);
|
StreamChannel serializeSuite(Function getMain(), {bool hidePrints = true}) => RemoteListener.start(getMain, hidePrints: hidePrints);
|
||||||
|
|
||||||
StreamChannel suiteChannel(String name) {
|
StreamChannel suiteChannel(String name) {
|
||||||
var manager = SuiteChannelManager.current;
|
var manager = SuiteChannelManager.current;
|
||||||
|
@ -149,7 +149,6 @@ class TestGoldenComparatorProcess {
|
|||||||
final File testConfigFile = findTestConfigFile(globals.fs.file(testUri));
|
final File testConfigFile = findTestConfigFile(globals.fs.file(testUri));
|
||||||
// Generate comparator process for the file.
|
// Generate comparator process for the file.
|
||||||
return '''
|
return '''
|
||||||
// @dart=2.9
|
|
||||||
import 'dart:convert'; // ignore: dart_convert_import
|
import 'dart:convert'; // ignore: dart_convert_import
|
||||||
import 'dart:io'; // ignore: dart_io_import
|
import 'dart:io'; // ignore: dart_io_import
|
||||||
|
|
||||||
@ -165,12 +164,12 @@ void main() async {
|
|||||||
final commands = stdin
|
final commands = stdin
|
||||||
.transform<String>(utf8.decoder)
|
.transform<String>(utf8.decoder)
|
||||||
.transform<String>(const LineSplitter())
|
.transform<String>(const LineSplitter())
|
||||||
.map<Object>(jsonDecode);
|
.map<dynamic>(jsonDecode);
|
||||||
await for (final Object command in commands) {
|
await for (final dynamic command in commands) {
|
||||||
if (command is Map<String, dynamic>) {
|
if (command is Map<String, dynamic>) {
|
||||||
File imageFile = File(command['imageFile']);
|
File imageFile = File(command['imageFile'] as String);
|
||||||
Uri goldenKey = Uri.parse(command['key']);
|
Uri goldenKey = Uri.parse(command['key'] as String);
|
||||||
bool update = command['update'];
|
bool update = command['update'] as bool;
|
||||||
|
|
||||||
final bytes = await File(imageFile.path).readAsBytes();
|
final bytes = await File(imageFile.path).readAsBytes();
|
||||||
if (update) {
|
if (update) {
|
||||||
|
@ -32,6 +32,7 @@ import '../convert.dart';
|
|||||||
import '../dart/package_map.dart';
|
import '../dart/package_map.dart';
|
||||||
import '../project.dart';
|
import '../project.dart';
|
||||||
import '../web/chrome.dart';
|
import '../web/chrome.dart';
|
||||||
|
import '../web/compile.dart';
|
||||||
import '../web/memory_fs.dart';
|
import '../web/memory_fs.dart';
|
||||||
import 'flutter_web_goldens.dart';
|
import 'flutter_web_goldens.dart';
|
||||||
import 'test_compiler.dart';
|
import 'test_compiler.dart';
|
||||||
@ -158,13 +159,13 @@ class FlutterWebPlatform extends PlatformPlugin {
|
|||||||
'dart_stack_trace_mapper.js',
|
'dart_stack_trace_mapper.js',
|
||||||
));
|
));
|
||||||
|
|
||||||
/// The precompiled dart sdk.
|
File get _dartSdk => _fileSystem.file(_artifacts.getArtifactPath(kDartSdkJsArtifactMap[WebRendererMode.html][
|
||||||
File get _dartSdk => _fileSystem.file(_fileSystem.path.join(
|
buildInfo.nullSafetyMode == NullSafetyMode.sound ? NullSafetyMode.sound : NullSafetyMode.unsound
|
||||||
_artifacts.getArtifactPath(Artifact.flutterWebSdk),
|
]));
|
||||||
'kernel',
|
|
||||||
'amd',
|
File get _dartSdkSourcemaps => _fileSystem.file(_artifacts.getArtifactPath(kDartSdkJsMapArtifactMap[WebRendererMode.html][
|
||||||
'dart_sdk.js',
|
buildInfo.nullSafetyMode == NullSafetyMode.sound ? NullSafetyMode.sound : NullSafetyMode.unsound
|
||||||
));
|
]));
|
||||||
|
|
||||||
/// The precompiled test javascript.
|
/// The precompiled test javascript.
|
||||||
File get _testDartJs => _fileSystem.file(_fileSystem.path.join(
|
File get _testDartJs => _fileSystem.file(_fileSystem.path.join(
|
||||||
@ -223,6 +224,11 @@ class FlutterWebPlatform extends PlatformPlugin {
|
|||||||
_dartSdk.openRead(),
|
_dartSdk.openRead(),
|
||||||
headers: <String, String>{'Content-Type': 'text/javascript'},
|
headers: <String, String>{'Content-Type': 'text/javascript'},
|
||||||
);
|
);
|
||||||
|
} else if (request.requestedUri.path.contains('dart_sdk.js.map')) {
|
||||||
|
return shelf.Response.ok(
|
||||||
|
_dartSdkSourcemaps.openRead(),
|
||||||
|
headers: <String, String>{'Content-Type': 'text/javascript'},
|
||||||
|
);
|
||||||
} else if (request.requestedUri.path
|
} else if (request.requestedUri.path
|
||||||
.contains('dart_stack_trace_mapper.js')) {
|
.contains('dart_stack_trace_mapper.js')) {
|
||||||
return shelf.Response.ok(
|
return shelf.Response.ok(
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
|
import '../artifacts.dart';
|
||||||
import '../base/common.dart';
|
import '../base/common.dart';
|
||||||
import '../base/context.dart';
|
import '../base/context.dart';
|
||||||
import '../base/file_system.dart';
|
import '../base/file_system.dart';
|
||||||
@ -106,3 +107,45 @@ class WebCompilationProxy {
|
|||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Web rendering backend mode.
|
||||||
|
enum WebRendererMode {
|
||||||
|
/// Auto detects which rendering backend to use.
|
||||||
|
autoDetect,
|
||||||
|
/// Always uses canvaskit.
|
||||||
|
canvaskit,
|
||||||
|
/// Always uses html.
|
||||||
|
html,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The correct precompiled artifact to use for each build and render mode.
|
||||||
|
const Map<WebRendererMode, Map<NullSafetyMode, Artifact>> kDartSdkJsArtifactMap = <WebRendererMode, Map<NullSafetyMode, Artifact>>{
|
||||||
|
WebRendererMode.autoDetect: <NullSafetyMode, Artifact> {
|
||||||
|
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitAndHtmlSoundSdk,
|
||||||
|
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitAndHtmlSdk,
|
||||||
|
},
|
||||||
|
WebRendererMode.canvaskit: <NullSafetyMode, Artifact> {
|
||||||
|
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitSoundSdk,
|
||||||
|
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitSdk,
|
||||||
|
},
|
||||||
|
WebRendererMode.html: <NullSafetyMode, Artifact> {
|
||||||
|
NullSafetyMode.sound: Artifact.webPrecompiledSoundSdk,
|
||||||
|
NullSafetyMode.unsound: Artifact.webPrecompiledSdk,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The correct source map artifact to use for each build and render mode.
|
||||||
|
const Map<WebRendererMode, Map<NullSafetyMode, Artifact>> kDartSdkJsMapArtifactMap = <WebRendererMode, Map<NullSafetyMode, Artifact>>{
|
||||||
|
WebRendererMode.autoDetect: <NullSafetyMode, Artifact> {
|
||||||
|
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps,
|
||||||
|
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps,
|
||||||
|
},
|
||||||
|
WebRendererMode.canvaskit: <NullSafetyMode, Artifact> {
|
||||||
|
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps,
|
||||||
|
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitSdkSourcemaps,
|
||||||
|
},
|
||||||
|
WebRendererMode.html: <NullSafetyMode, Artifact> {
|
||||||
|
NullSafetyMode.sound: Artifact.webPrecompiledSoundSdkSourcemaps,
|
||||||
|
NullSafetyMode.unsound: Artifact.webPrecompiledSdkSourcemaps,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
@ -14,6 +14,7 @@ import 'package:flutter_tools/src/isolated/devfs_web.dart';
|
|||||||
import 'package:flutter_tools/src/compile.dart';
|
import 'package:flutter_tools/src/compile.dart';
|
||||||
import 'package:flutter_tools/src/convert.dart';
|
import 'package:flutter_tools/src/convert.dart';
|
||||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||||
|
import 'package:flutter_tools/src/web/compile.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
import 'package:package_config/package_config.dart';
|
import 'package:package_config/package_config.dart';
|
||||||
import 'package:shelf/shelf.dart';
|
import 'package:shelf/shelf.dart';
|
||||||
|
@ -121,8 +121,8 @@ class _PlatformBinaryMessenger extends BinaryMessenger {
|
|||||||
|
|
||||||
/// Sends a platform message from the platform side back to the framework.
|
/// Sends a platform message from the platform side back to the framework.
|
||||||
@override
|
@override
|
||||||
Future<ByteData> send(String channel, ByteData? message) {
|
Future<ByteData?> send(String channel, ByteData? message) {
|
||||||
final Completer<ByteData> completer = Completer<ByteData>();
|
final Completer<ByteData?> completer = Completer<ByteData?>();
|
||||||
ui.window.onPlatformMessage!(channel, message, (ByteData? reply) {
|
ui.window.onPlatformMessage!(channel, message, (ByteData? reply) {
|
||||||
try {
|
try {
|
||||||
completer.complete(reply);
|
completer.complete(reply);
|
||||||
|
@ -57,7 +57,7 @@ void main() {
|
|||||||
ServicesBinding.instance!.defaultBinaryMessenger
|
ServicesBinding.instance!.defaultBinaryMessenger
|
||||||
.setMessageHandler('test_send', (ByteData? data) {
|
.setMessageHandler('test_send', (ByteData? data) {
|
||||||
loggedMessages.add(codec.decodeMessage(data) as String);
|
loggedMessages.add(codec.decodeMessage(data) as String);
|
||||||
return null;
|
return Future<ByteData?>.value(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
await pluginBinaryMessenger.send(
|
await pluginBinaryMessenger.send(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user