blob: 0336036f00ee28dce9afab436c93c2640856b915 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:core';
import 'dart:ffi' as ffi;
import 'dart:io';
import 'dart:isolate';
import 'dart:typed_data';
import 'dart:ui';
void main() {}
@pragma('vm:entry-point')
void customEntrypoint() {
sayHiFromCustomEntrypoint();
}
@pragma('vm:external-name', 'SayHiFromCustomEntrypoint')
external void sayHiFromCustomEntrypoint();
@pragma('vm:entry-point')
void customEntrypoint1() {
sayHiFromCustomEntrypoint1();
sayHiFromCustomEntrypoint2();
sayHiFromCustomEntrypoint3();
}
@pragma('vm:external-name', 'SayHiFromCustomEntrypoint1')
external void sayHiFromCustomEntrypoint1();
@pragma('vm:external-name', 'SayHiFromCustomEntrypoint2')
external void sayHiFromCustomEntrypoint2();
@pragma('vm:external-name', 'SayHiFromCustomEntrypoint3')
external void sayHiFromCustomEntrypoint3();
@pragma('vm:entry-point')
void terminateExitCodeHandler() {
final ProcessResult result = Process.runSync('ls', <String>[]);
}
@pragma('vm:entry-point')
void executableNameNotNull() {
notifyStringValue(Platform.executable);
}
@pragma('vm:entry-point')
void implicitViewNotNull() {
notifyBoolValue(PlatformDispatcher.instance.implicitView != null);
}
@pragma('vm:external-name', 'NotifyStringValue')
external void notifyStringValue(String value);
@pragma('vm:external-name', 'NotifyBoolValue')
external void notifyBoolValue(bool value);
@pragma('vm:entry-point')
void invokePlatformTaskRunner() {
PlatformDispatcher.instance.sendPlatformMessage('OhHi', null, null);
}
@pragma('vm:entry-point')
void invokePlatformThreadIsolate() {
signalNativeTest();
runOnPlatformThread(ffiSignalNativeTest);
}
Float64List kTestTransform = () {
final Float64List values = Float64List(16);
values[0] = 1.0; // scaleX
values[4] = 2.0; // skewX
values[12] = 3.0; // transX
values[1] = 4.0; // skewY
values[5] = 5.0; // scaleY
values[13] = 6.0; // transY
values[3] = 7.0; // pers0
values[7] = 8.0; // pers1
values[15] = 9.0; // pers2
return values;
}();
@pragma('vm:external-name', 'SignalNativeTest')
external void signalNativeTest();
@pragma('vm:external-name', 'SignalNativeCount')
external void signalNativeCount(int count);
@pragma('vm:external-name', 'SignalNativeMessage')
external void signalNativeMessage(String message);
@pragma('vm:external-name', 'NotifySemanticsEnabled')
external void notifySemanticsEnabled(bool enabled);
@pragma('vm:external-name', 'NotifyAccessibilityFeatures')
external void notifyAccessibilityFeatures(bool reduceMotion);
@pragma('vm:external-name', 'NotifySemanticsAction')
external void notifySemanticsAction(int nodeId, int action, List<int> data);
@ffi.Native<ffi.Void Function()>(symbol: 'FFISignalNativeTest')
external void ffiSignalNativeTest();
/// Returns a future that completes when
/// `PlatformDispatcher.instance.onSemanticsEnabledChanged` fires.
Future<void> get semanticsChanged {
final Completer<void> semanticsChanged = Completer<void>();
PlatformDispatcher.instance.onSemanticsEnabledChanged =
semanticsChanged.complete;
return semanticsChanged.future;
}
/// Returns a future that completes when
/// `PlatformDispatcher.instance.onAccessibilityFeaturesChanged` fires.
Future<void> get accessibilityFeaturesChanged {
final Completer<void> featuresChanged = Completer<void>();
PlatformDispatcher.instance.onAccessibilityFeaturesChanged =
featuresChanged.complete;
return featuresChanged.future;
}
Future<SemanticsActionEvent> get semanticsActionEvent {
final Completer<SemanticsActionEvent> actionReceived =
Completer<SemanticsActionEvent>();
PlatformDispatcher.instance.onSemanticsActionEvent =
(SemanticsActionEvent action) {
actionReceived.complete(action);
};
return actionReceived.future;
}
@pragma('vm:entry-point')
Future<void> a11y_main() async {
// 1: Return initial state (semantics disabled).
notifySemanticsEnabled(PlatformDispatcher.instance.semanticsEnabled);
// 2: Await semantics enabled from embedder.
await semanticsChanged;
notifySemanticsEnabled(PlatformDispatcher.instance.semanticsEnabled);
// 3: Return initial state of accessibility features.
notifyAccessibilityFeatures(
PlatformDispatcher.instance.accessibilityFeatures.reduceMotion);
// 4: Await accessibility features changed from embedder.
await accessibilityFeaturesChanged;
notifyAccessibilityFeatures(
PlatformDispatcher.instance.accessibilityFeatures.reduceMotion);
// 5: Fire semantics update.
final SemanticsUpdateBuilder builder = SemanticsUpdateBuilder()
..updateNode(
id: 42,
identifier: '',
label: 'A: root',
labelAttributes: <StringAttribute>[],
rect: Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
transform: kTestTransform,
childrenInTraversalOrder: Int32List.fromList(<int>[84, 96]),
childrenInHitTestOrder: Int32List.fromList(<int>[96, 84]),
actions: 0,
flags: 0,
maxValueLength: 0,
currentValueLength: 0,
textSelectionBase: 0,
textSelectionExtent: 0,
platformViewId: 0,
scrollChildren: 0,
scrollIndex: 0,
scrollPosition: 0.0,
scrollExtentMax: 0.0,
scrollExtentMin: 0.0,
elevation: 0.0,
thickness: 0.0,
hint: '',
hintAttributes: <StringAttribute>[],
value: '',
valueAttributes: <StringAttribute>[],
increasedValue: '',
increasedValueAttributes: <StringAttribute>[],
decreasedValue: '',
decreasedValueAttributes: <StringAttribute>[],
tooltip: 'tooltip',
textDirection: TextDirection.ltr,
additionalActions: Int32List(0),
headingLevel: 0
)
..updateNode(
id: 84,
identifier: '',
label: 'B: leaf',
labelAttributes: <StringAttribute>[],
rect: Rect.fromLTRB(40.0, 40.0, 80.0, 80.0),
transform: kTestTransform,
actions: 0,
flags: 0,
maxValueLength: 0,
currentValueLength: 0,
textSelectionBase: 0,
textSelectionExtent: 0,
platformViewId: 0,
scrollChildren: 0,
scrollIndex: 0,
scrollPosition: 0.0,
scrollExtentMax: 0.0,
scrollExtentMin: 0.0,
elevation: 0.0,
thickness: 0.0,
hint: '',
hintAttributes: <StringAttribute>[],
value: '',
valueAttributes: <StringAttribute>[],
increasedValue: '',
increasedValueAttributes: <StringAttribute>[],
decreasedValue: '',
decreasedValueAttributes: <StringAttribute>[],
tooltip: 'tooltip',
textDirection: TextDirection.ltr,
additionalActions: Int32List(0),
childrenInHitTestOrder: Int32List(0),
childrenInTraversalOrder: Int32List(0),
headingLevel: 0
)
..updateNode(
id: 96,
identifier: '',
label: 'C: branch',
labelAttributes: <StringAttribute>[],
rect: Rect.fromLTRB(40.0, 40.0, 80.0, 80.0),
transform: kTestTransform,
childrenInTraversalOrder: Int32List.fromList(<int>[128]),
childrenInHitTestOrder: Int32List.fromList(<int>[128]),
actions: 0,
flags: 0,
maxValueLength: 0,
currentValueLength: 0,
textSelectionBase: 0,
textSelectionExtent: 0,
platformViewId: 0,
scrollChildren: 0,
scrollIndex: 0,
scrollPosition: 0.0,
scrollExtentMax: 0.0,
scrollExtentMin: 0.0,
elevation: 0.0,
thickness: 0.0,
hint: '',
hintAttributes: <StringAttribute>[],
value: '',
valueAttributes: <StringAttribute>[],
increasedValue: '',
increasedValueAttributes: <StringAttribute>[],
decreasedValue: '',
decreasedValueAttributes: <StringAttribute>[],
tooltip: 'tooltip',
textDirection: TextDirection.ltr,
additionalActions: Int32List(0),
headingLevel: 0
)
..updateNode(
id: 128,
identifier: '',
label: 'D: leaf',
labelAttributes: <StringAttribute>[],
rect: Rect.fromLTRB(40.0, 40.0, 80.0, 80.0),
transform: kTestTransform,
additionalActions: Int32List.fromList(<int>[21]),
platformViewId: 0x3f3,
actions: 0,
flags: 0,
maxValueLength: 0,
currentValueLength: 0,
textSelectionBase: 0,
textSelectionExtent: 0,
scrollChildren: 0,
scrollIndex: 0,
scrollPosition: 0.0,
scrollExtentMax: 0.0,
scrollExtentMin: 0.0,
elevation: 0.0,
thickness: 0.0,
hint: '',
hintAttributes: <StringAttribute>[],
value: '',
valueAttributes: <StringAttribute>[],
increasedValue: '',
increasedValueAttributes: <StringAttribute>[],
decreasedValue: '',
decreasedValueAttributes: <StringAttribute>[],
tooltip: 'tooltip',
textDirection: TextDirection.ltr,
childrenInHitTestOrder: Int32List(0),
childrenInTraversalOrder: Int32List(0),
headingLevel: 0
)
..updateCustomAction(
id: 21,
label: 'Archive',
hint: 'archive message',
);
PlatformDispatcher.instance.views.first.updateSemantics(builder.build());
signalNativeTest();
// 6: Await semantics action from embedder.
final SemanticsActionEvent data = await semanticsActionEvent;
final List<int> actionArgs = <int>[
(data.arguments! as ByteData).getInt8(0),
(data.arguments! as ByteData).getInt8(1)
];
notifySemanticsAction(data.nodeId, data.type.index, actionArgs);
// 7: Await semantics disabled from embedder.
await semanticsChanged;
notifySemanticsEnabled(PlatformDispatcher.instance.semanticsEnabled);
}
@pragma('vm:entry-point')
Future<void> a11y_string_attributes() async {
// 1: Wait until semantics are enabled.
if (!PlatformDispatcher.instance.semanticsEnabled) {
await semanticsChanged;
}
// 2: Update semantics with string attributes.
final SemanticsUpdateBuilder builder = SemanticsUpdateBuilder()
..updateNode(
id: 42,
identifier: 'identifier',
label: 'What is the meaning of life?',
labelAttributes: <StringAttribute>[
LocaleStringAttribute(
range: TextRange(start: 0, end: 'What is the meaning of life?'.length),
locale: Locale('en'),
),
SpellOutStringAttribute(
range: TextRange(start: 0, end: 1),
),
],
rect: Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
transform: kTestTransform,
childrenInTraversalOrder: Int32List.fromList(<int>[84, 96]),
childrenInHitTestOrder: Int32List.fromList(<int>[96, 84]),
actions: 0,
flags: 0,
maxValueLength: 0,
currentValueLength: 0,
textSelectionBase: 0,
textSelectionExtent: 0,
platformViewId: 0,
scrollChildren: 0,
scrollIndex: 0,
scrollPosition: 0.0,
scrollExtentMax: 0.0,
scrollExtentMin: 0.0,
elevation: 0.0,
thickness: 0.0,
hint: "It's a number",
hintAttributes: <StringAttribute>[
LocaleStringAttribute(
range: TextRange(start: 0, end: 1),
locale: Locale('en'),
),
LocaleStringAttribute(
range: TextRange(start: 2, end: 3),
locale: Locale('fr'),
),
],
value: '42',
valueAttributes: <StringAttribute>[
LocaleStringAttribute(
range: TextRange(start: 0, end: '42'.length),
locale: Locale('en', 'US'),
),
],
increasedValue: '43',
increasedValueAttributes: <StringAttribute>[
SpellOutStringAttribute(
range: TextRange(start: 0, end: 1),
),
SpellOutStringAttribute(
range: TextRange(start: 1, end: 2),
),
],
decreasedValue: '41',
decreasedValueAttributes: <StringAttribute>[],
tooltip: 'tooltip',
textDirection: TextDirection.ltr,
additionalActions: Int32List(0),
headingLevel: 0,
);
PlatformDispatcher.instance.views.first.updateSemantics(builder.build());
signalNativeTest();
}
@pragma('vm:entry-point')
void platform_messages_response() {
PlatformDispatcher.instance.onPlatformMessage =
(String name, ByteData? data, PlatformMessageResponseCallback? callback) {
callback!(data);
};
signalNativeTest();
}
@pragma('vm:entry-point')
void platform_messages_no_response() {
PlatformDispatcher.instance.onPlatformMessage =
(String name, ByteData? data, PlatformMessageResponseCallback? callback) {
final Uint8List list = data!.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
signalNativeMessage(utf8.decode(list));
// This does nothing because no one is listening on the other side. But complete the loop anyway
// to make sure all null checking on response handles in the engine is in place.
callback!(data);
};
signalNativeTest();
}
@pragma('vm:entry-point')
void null_platform_messages() {
PlatformDispatcher.instance.onPlatformMessage =
(String name, ByteData? data, PlatformMessageResponseCallback? callback) {
// This checks if the platform_message null data is converted to Flutter null.
signalNativeMessage((null == data).toString());
callback!(data);
};
signalNativeTest();
}
Picture CreateSimplePicture() {
final Paint blackPaint = Paint();
final Paint whitePaint = Paint()..color = Color.fromARGB(255, 255, 255, 255);
final PictureRecorder baseRecorder = PictureRecorder();
final Canvas canvas = Canvas(baseRecorder);
canvas.drawRect(Rect.fromLTRB(0.0, 0.0, 1000.0, 1000.0), blackPaint);
canvas.drawRect(Rect.fromLTRB(10.0, 10.0, 990.0, 990.0), whitePaint);
return baseRecorder.endRecording();
}
@pragma('vm:entry-point')
void can_composite_platform_views() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
builder.addPicture(Offset(1.0, 1.0), CreateSimplePicture());
builder.pushOffset(1.0, 2.0);
builder.addPlatformView(42, width: 123.0, height: 456.0);
builder.addPicture(Offset(1.0, 1.0), CreateSimplePicture());
builder.pop(); // offset
signalNativeTest(); // Signal 2
PlatformDispatcher.instance.views.first.render(builder.build());
};
signalNativeTest(); // Signal 1
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void can_composite_platform_views_with_opacity() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
// Root node
builder.pushOffset(1.0, 2.0);
// First sibling layer (no platform view, should be cached)
builder.pushOpacity(127);
builder.addPicture(Offset(1.0, 1.0), CreateSimplePicture());
builder.pop();
// Second sibling layer (platform view, should not be cached)
builder.pushOpacity(127);
builder.addPlatformView(42, width: 123.0, height: 456.0);
builder.pop();
// Third sibling layer (no platform view, should be cached)
builder.pushOpacity(127);
builder.addPicture(Offset(2.0, 1.0), CreateSimplePicture());
builder.pop();
signalNativeTest(); // Signal 2
PlatformDispatcher.instance.views.first.render(builder.build());
};
signalNativeTest(); // Signal 1
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void can_composite_with_opacity() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
builder.pushOpacity(127);
builder.addPicture(Offset(1.0, 1.0), CreateSimplePicture());
builder.pop(); // offset
signalNativeTest(); // Signal 2
PlatformDispatcher.instance.views.first.render(builder.build());
};
signalNativeTest(); // Signal 1
PlatformDispatcher.instance.scheduleFrame();
}
Picture CreateColoredBox(Color color, Size size) {
final Paint paint = Paint();
paint.color = color;
final PictureRecorder baseRecorder = PictureRecorder();
final Canvas canvas = Canvas(baseRecorder);
canvas.drawRect(Rect.fromLTRB(0.0, 0.0, size.width, size.height), paint);
return baseRecorder.endRecording();
}
@pragma('vm:entry-point')
void can_composite_platform_views_with_known_scene() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final Color red = Color.fromARGB(127, 255, 0, 0);
final Color blue = Color.fromARGB(127, 0, 0, 255);
final Color gray = Color.fromARGB(127, 127, 127, 127);
final Size size = Size(50.0, 150.0);
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
// 10 (Index 0)
builder.addPicture(
Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
builder.pushOffset(20.0, 20.0);
// 20 (Index 1)
builder.addPlatformView(1,
width: size.width, height: size.height); // green - platform
builder.pop();
// 30 (Index 2)
builder.addPicture(
Offset(30.0, 30.0), CreateColoredBox(blue, size)); // blue - flutter
builder.pushOffset(40.0, 40.0);
// 40 (Index 3)
builder.addPlatformView(2,
width: size.width, height: size.height); // magenta - platform
builder.pop();
// 50 (Index 4)
builder.addPicture(
Offset(50.0, 50.0), CreateColoredBox(gray, size)); // gray - flutter
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
signalNativeTest(); // Signal 2
};
signalNativeTest(); // Signal 1
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void can_composite_platform_views_transparent_overlay() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final Color red = Color.fromARGB(127, 255, 0, 0);
final Color blue = Color.fromARGB(127, 0, 0, 255);
final Color transparent = Color(0xFFFFFF);
final Size size = Size(50.0, 150.0);
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
// 10 (Index 0)
builder.addPicture(
Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
builder.pushOffset(20.0, 20.0);
// 20 (Index 1)
builder.addPlatformView(1,
width: size.width, height: size.height); // green - platform
builder.pop();
// 30 (Index 2)
builder.addPicture(
Offset(30.0, 30.0), CreateColoredBox(transparent, size)); // transparent picture, no layer should be created.
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
signalNativeTest(); // Signal 2
};
signalNativeTest(); // Signal 1
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void can_composite_platform_views_no_overlay() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final Color red = Color.fromARGB(127, 255, 0, 0);
final Color blue = Color.fromARGB(127, 0, 0, 255);
final Size size = Size(50.0, 150.0);
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
// 10 (Index 0)
builder.addPicture(
Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
builder.pushOffset(20.0, 20.0);
// 20 (Index 1)
builder.addPlatformView(1,
width: size.width, height: size.height); // green - platform
builder.pop();
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
signalNativeTest(); // Signal 2
};
signalNativeTest(); // Signal 1
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void can_composite_platform_views_with_root_layer_only() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final Color red = Color.fromARGB(127, 255, 0, 0);
final Size size = Size(50.0, 150.0);
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
// 10 (Index 0)
builder.addPicture(
Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
signalNativeTest(); // Signal 2
};
signalNativeTest(); // Signal 1
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void can_composite_platform_views_with_platform_layer_on_bottom() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final Color red = Color.fromARGB(127, 255, 0, 0);
final Size size = Size(50.0, 150.0);
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
// 10 (Index 0)
builder.addPicture(
Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
builder.pushOffset(20.0, 20.0);
// 20 (Index 1)
builder.addPlatformView(1,
width: size.width, height: size.height); // green - platform
builder.pop();
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
signalNativeTest(); // Signal 2
};
signalNativeTest(); // Signal 1
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:external-name', 'SignalBeginFrame')
external void signalBeginFrame();
@pragma('vm:entry-point')
Future<void> texture_destruction_callback_called_without_custom_compositor() async {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final Color red = Color.fromARGB(127, 255, 0, 0);
final Size size = Size(50.0, 150.0);
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
builder.addPicture(
Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void can_render_scene_without_custom_compositor() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final Color red = Color.fromARGB(127, 255, 0, 0);
final Color green = Color.fromARGB(127, 0, 255, 0);
final Color blue = Color.fromARGB(127, 0, 0, 255);
final Color magenta = Color.fromARGB(127, 255, 0, 255);
final Color gray = Color.fromARGB(127, 127, 127, 127);
final Size size = Size(50.0, 150.0);
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
builder.addPicture(
Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
builder.addPicture(
Offset(20.0, 20.0), CreateColoredBox(green, size)); // green - flutter
builder.addPicture(
Offset(30.0, 30.0), CreateColoredBox(blue, size)); // blue - flutter
builder.addPicture(Offset(40.0, 40.0),
CreateColoredBox(magenta, size)); // magenta - flutter
builder.addPicture(
Offset(50.0, 50.0), CreateColoredBox(gray, size)); // gray - flutter
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
Picture CreateGradientBox(Size size) {
final Paint paint = Paint();
final List<Color> rainbow = <Color>[
Color.fromARGB(255, 255, 0, 0), // red
Color.fromARGB(255, 255, 165, 0), // orange
Color.fromARGB(255, 255, 255, 0), // yellow
Color.fromARGB(255, 0, 255, 0), // green
Color.fromARGB(255, 0, 0, 255), // blue
Color.fromARGB(255, 75, 0, 130), // indigo
Color.fromARGB(255, 238, 130, 238), // violet
];
final List<double> stops = <double>[
(1.0 / 7.0),
(2.0 / 7.0),
(3.0 / 7.0),
(4.0 / 7.0),
(5.0 / 7.0),
(6.0 / 7.0),
(7.0 / 7.0),
];
paint.shader = Gradient.linear(
Offset(0.0, 0.0), Offset(size.width, size.height), rainbow, stops);
final PictureRecorder baseRecorder = PictureRecorder();
final Canvas canvas = Canvas(baseRecorder);
canvas.drawRect(Rect.fromLTRB(0.0, 0.0, size.width, size.height), paint);
return baseRecorder.endRecording();
}
@pragma('vm:external-name', 'EchoKeyEvent')
external void _echoKeyEvent(int change, int timestamp, int physical,
int logical, int charCode, bool synthesized, int deviceType);
// Convert `kind` in enum form to its integer form.
//
// It performs a reversed mapping from `UnserializeKeyEventType`
// in shell/platform/embedder/tests/embedder_unittests.cc.
int _serializeKeyEventType(KeyEventType change) {
switch (change) {
case KeyEventType.up:
return 1;
case KeyEventType.down:
return 2;
case KeyEventType.repeat:
return 3;
}
}
// Convert `deviceType` in enum form to its integer form.
//
// It performs a reversed mapping from `UnserializeKeyEventDeviceType`
// in shell/platform/embedder/tests/embedder_unittests.cc.
int _serializeKeyEventDeviceType(KeyEventDeviceType deviceType) {
switch (deviceType) {
case KeyEventDeviceType.keyboard:
return 1;
case KeyEventDeviceType.directionalPad:
return 2;
case KeyEventDeviceType.gamepad:
return 3;
case KeyEventDeviceType.joystick:
return 4;
case KeyEventDeviceType.hdmi:
return 5;
}
}
// Echo the event data with `_echoKeyEvent`, and returns synthesized as handled.
@pragma('vm:entry-point')
Future<void> key_data_echo() async {
PlatformDispatcher.instance.onKeyData = (KeyData data) {
_echoKeyEvent(
_serializeKeyEventType(data.type),
data.timeStamp.inMicroseconds,
data.physical,
data.logical,
data.character == null ? 0 : data.character!.codeUnitAt(0),
data.synthesized,
_serializeKeyEventDeviceType(data.deviceType),
);
return data.synthesized;
};
signalNativeTest();
}
// After platform channel 'test/starts_echo' receives a message, starts echoing
// the event data with `_echoKeyEvent`, and returns synthesized as handled.
@pragma('vm:entry-point')
Future<void> key_data_late_echo() async {
channelBuffers.setListener('test/starts_echo',
(ByteData? data, PlatformMessageResponseCallback callback) {
PlatformDispatcher.instance.onKeyData = (KeyData data) {
_echoKeyEvent(
_serializeKeyEventType(data.type),
data.timeStamp.inMicroseconds,
data.physical,
data.logical,
data.character == null ? 0 : data.character!.codeUnitAt(0),
data.synthesized,
_serializeKeyEventDeviceType(data.deviceType),
);
return data.synthesized;
};
callback(null);
});
signalNativeTest();
}
@pragma('vm:entry-point')
void render_implicit_view() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final Size size = Size(800.0, 600.0);
final Color red = Color.fromARGB(127, 255, 0, 0);
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
builder.addPicture(
Offset(0.0, 0.0), CreateColoredBox(red, size));
builder.pop();
PlatformDispatcher.instance.implicitView?.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void render_all_views() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
for (final FlutterView view in PlatformDispatcher.instance.views) {
final Size size = Size(800.0, 600.0);
final Color red = Color.fromARGB(127, 255, 0, 0);
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
builder.addPicture(
Offset(0.0, 0.0), CreateColoredBox(red, size));
builder.pop();
view.render(builder.build());
}
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void render_gradient() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final Size size = Size(800.0, 600.0);
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
builder.addPicture(
Offset(0.0, 0.0), CreateGradientBox(size)); // gradient - flutter
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void render_texture() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final Size size = Size(800.0, 600.0);
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
builder.addTexture(/*textureId*/ 1, width: size.width, height: size.height);
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void render_gradient_on_non_root_backing_store() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final Size size = Size(800.0, 600.0);
final Color red = Color.fromARGB(127, 255, 0, 0);
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
// Even though this is occluded, add something so it is not elided.
builder.addPicture(
Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
builder.addPlatformView(1, width: 100, height: 200); // undefined - platform
builder.addPicture(
Offset(0.0, 0.0), CreateGradientBox(size)); // gradient - flutter
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void verify_b141980393() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
// The platform view in the test case is screen sized but with margins of 31
// and 37 points from the top and bottom.
const double topMargin = 31.0;
const double bottomMargin = 37.0;
final Size platformViewSize = Size(800.0, 600.0 - topMargin - bottomMargin);
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(
0.0, // x
topMargin // y
);
// The web view in example.
builder.addPlatformView(1337,
width: platformViewSize.width, height: platformViewSize.height);
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void can_display_platform_view_with_pixel_ratio() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
builder.pushTransform(Float64List.fromList(<double>[
2.0,
0.0,
0.0,
0.0,
0.0,
2.0,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
1.0
])); // base
builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(400.0, 300.0)));
builder.pushOffset(0.0, 20.0); // offset
builder.addPlatformView(42, width: 400.0, height: 280.0);
builder.pop(); // offset
builder.addPicture(Offset(0.0, 0.0),
CreateColoredBox(Color.fromARGB(128, 255, 0, 0), Size(400.0, 300.0)));
builder.pop(); // base
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void can_receive_locale_updates() {
PlatformDispatcher.instance.onLocaleChanged = () {
signalNativeCount(PlatformDispatcher.instance.locales.length);
};
signalNativeTest();
}
// Verifies behavior tracked in https://github.com/flutter/flutter/issues/43732
@pragma('vm:entry-point')
void verify_b143464703() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0); // base
// Background
builder.addPicture(
Offset(0.0, 0.0),
CreateColoredBox(
Color.fromARGB(255, 128, 128, 128), Size(1024.0, 600.0)));
builder.pushOpacity(128);
builder.addPicture(Offset(10.0, 10.0),
CreateColoredBox(Color.fromARGB(255, 0, 0, 255), Size(25.0, 25.0)));
builder.pop(); // opacity 128
// The top bar and the platform view are pushed to the side.
builder.pushOffset(135.0, 0.0); // 1
builder.pushOpacity(128); // opacity
// Platform view offset from the top
builder.pushOffset(0.0, 60.0); // 2
builder.addPlatformView(42, width: 1024.0, height: 540.0);
builder.pop(); // 2
// Top bar
builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(1024.0, 60.0)));
builder.pop(); // opacity
builder.pop(); // 1
builder.pop(); // base
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void push_frames_over_and_over() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
builder.addPicture(
Offset(0.0, 0.0),
CreateColoredBox(
Color.fromARGB(255, 128, 128, 128), Size(1024.0, 600.0)));
builder.pushOpacity(128);
builder.addPlatformView(42, width: 1024.0, height: 540.0);
builder.pop();
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
signalNativeTest();
PlatformDispatcher.instance.scheduleFrame();
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void platform_view_mutators() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0); // base
builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(800.0, 600.0)));
builder.pushOpacity(128);
builder.pushClipRect(Rect.fromLTWH(10.0, 10.0, 800.0 - 20.0, 600.0 - 20.0));
builder.pushClipRRect(RRect.fromLTRBR(
10.0, 10.0, 800.0 - 10.0, 600.0 - 10.0, Radius.circular(14.0)));
builder.addPlatformView(42, width: 800.0, height: 600.0);
builder.pop(); // clip rrect
builder.pop(); // clip rect
builder.pop(); // opacity
builder.pop(); // base
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void platform_view_mutators_with_pixel_ratio() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0); // base
builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(400.0, 300.0)));
builder.pushOpacity(128);
builder.pushClipRect(Rect.fromLTWH(5.0, 5.0, 400.0 - 10.0, 300.0 - 10.0));
builder.pushClipRRect(RRect.fromLTRBR(
5.0, 5.0, 400.0 - 5.0, 300.0 - 5.0, Radius.circular(7.0)));
builder.addPlatformView(42, width: 400.0, height: 300.0);
builder.pop(); // clip rrect
builder.pop(); // clip rect
builder.pop(); // opacity
builder.pop(); // base
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void empty_scene() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
PlatformDispatcher.instance.views.first.render(SceneBuilder().build());
signalNativeTest();
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void scene_with_no_container() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(400.0, 300.0)));
PlatformDispatcher.instance.views.first.render(builder.build());
signalNativeTest();
};
PlatformDispatcher.instance.scheduleFrame();
}
Picture CreateArcEndCapsPicture() {
final PictureRecorder baseRecorder = PictureRecorder();
final Canvas canvas = Canvas(baseRecorder);
final style = Paint()
..strokeWidth = 12.0
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.miter;
style.color = Color.fromARGB(255, 255, 0, 0);
canvas.drawArc(
Rect.fromLTRB(0.0, 0.0, 500.0, 500.0), 1.57, 1.0, false, style);
return baseRecorder.endRecording();
}
@pragma('vm:entry-point')
void arc_end_caps_correct() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
builder.addPicture(Offset(0.0, 0.0), CreateArcEndCapsPicture());
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void scene_builder_with_clips() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
builder.pushClipRect(Rect.fromLTRB(10.0, 10.0, 390.0, 290.0));
builder.addPlatformView(42, width: 400.0, height: 300.0);
builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(400.0, 300.0)));
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void scene_builder_with_complex_clips() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
builder.pushClipRect(Rect.fromLTRB(0.0, 0.0, 1024.0, 600.0));
builder.pushOffset(512.0, 0.0);
builder.pushClipRect(Rect.fromLTRB(0.0, 0.0, 512.0, 600.0));
builder.pushOffset(-256.0, 0.0);
builder.pushClipRect(Rect.fromLTRB(0.0, 0.0, 1024.0, 600.0));
builder.addPlatformView(42, width: 1024.0, height: 600.0);
builder.addPicture(
Offset(0.0, 0.0), CreateGradientBox(Size(1024.0, 600.0)));
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:external-name', 'SendObjectToNativeCode')
external void sendObjectToNativeCode(dynamic object);
@pragma('vm:entry-point')
void objects_can_be_posted() {
final ReceivePort port = ReceivePort();
port.listen((dynamic message) {
sendObjectToNativeCode(message);
});
signalNativeCount(port.sendPort.nativePort);
}
@pragma('vm:entry-point')
void empty_scene_posts_zero_layers_to_compositor() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
// Should not render anything.
builder.pushClipRect(Rect.fromLTRB(0.0, 0.0, 300.0, 200.0));
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void compositor_can_post_only_platform_views() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
builder.addPlatformView(42, width: 300.0, height: 200.0);
builder.addPlatformView(24, width: 300.0, height: 200.0);
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void render_targets_are_recycled() {
int frameCount = 0;
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
for (int i = 0; i < 10; i++) {
builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(30.0, 20.0)));
builder.addPlatformView(42 + i, width: 30.0, height: 20.0);
}
PlatformDispatcher.instance.views.first.render(builder.build());
frameCount++;
if (frameCount == 8) {
signalNativeTest();
} else {
PlatformDispatcher.instance.scheduleFrame();
}
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void render_targets_are_in_stable_order() {
int frameCount = 0;
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
for (int i = 0; i < 10; i++) {
builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(30.0, 20.0)));
builder.addPlatformView(42 + i, width: 30.0, height: 20.0);
}
PlatformDispatcher.instance.views.first.render(builder.build());
PlatformDispatcher.instance.scheduleFrame();
frameCount++;
if (frameCount == 8) {
signalNativeTest();
}
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:external-name', 'NativeArgumentsCallback')
external void nativeArgumentsCallback(List<String> args);
@pragma('vm:entry-point')
void custom_logger(List<String> args) {
print('hello world');
}
@pragma('vm:entry-point')
void dart_entrypoint_args(List<String> args) {
nativeArgumentsCallback(args);
}
@pragma('vm:external-name', 'SnapshotsCallback')
external void snapshotsCallback(Image bigImage, Image smallImage);
@pragma('vm:entry-point')
Future<void> snapshot_large_scene(int maxSize) async {
// Set width to double the max size, which will result in height being half the max size after scaling.
double width = maxSize * 2.0, height = maxSize.toDouble();
PictureRecorder recorder = PictureRecorder();
{
final Canvas canvas = Canvas(recorder, Rect.fromLTWH(0, 0, width, height));
final Paint paint = Paint();
// Bottom left
paint.color = Color.fromARGB(255, 100, 255, 100);
canvas.drawRect(Rect.fromLTWH(0, height / 2, width / 2, height / 2), paint);
// Top right
paint.color = Color.fromARGB(255, 100, 100, 255);
canvas.drawRect(Rect.fromLTWH(width / 2, 0, width / 2, height / 2), paint);
}
Picture picture = recorder.endRecording();
final Image bigImage = await picture.toImage(width.toInt(), height.toInt());
// The max size varies across hardware/drivers, so normalize the result to a smaller target size in
// order to reliably test against an image fixture.
double smallWidth = 128, smallHeight = 64;
recorder = PictureRecorder();
{
final Canvas canvas =
Canvas(recorder, Rect.fromLTWH(0, 0, smallWidth, smallHeight));
canvas.scale(smallWidth / bigImage.width);
canvas.drawImage(bigImage, Offset.zero, Paint());
}
picture = recorder.endRecording();
final Image smallImage =
await picture.toImage(smallWidth.toInt(), smallHeight.toInt());
snapshotsCallback(bigImage, smallImage);
}
@pragma('vm:entry-point')
void invalid_backingstore() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final Color red = Color.fromARGB(127, 255, 0, 0);
final Size size = Size(50.0, 150.0);
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
builder.addPicture(
Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.onDrawFrame = () {
signalNativeTest();
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void can_schedule_frame() {
PlatformDispatcher.instance.onBeginFrame = (Duration beginTime) {
signalNativeCount(beginTime.inMicroseconds);
};
signalNativeTest();
}
@pragma('vm:entry-point')
void add_view_schedules_frame() {
PlatformDispatcher.instance.onBeginFrame = (Duration beginTime) {
for (final FlutterView view in PlatformDispatcher.instance.views) {
if (view.viewId == 123) {
signalNativeCount(beginTime.inMicroseconds);
}
}
};
signalNativeTest();
}
void drawSolidColor(Color c) {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
builder.addPicture(
Offset.zero,
CreateColoredBox(
c, PlatformDispatcher.instance.views.first.physicalSize));
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void draw_solid_red() {
drawSolidColor(const Color.fromARGB(255, 255, 0, 0));
}
@pragma('vm:entry-point')
void draw_solid_green() {
drawSolidColor(const Color.fromARGB(255, 0, 255, 0));
}
@pragma('vm:entry-point')
void draw_solid_blue() {
drawSolidColor(const Color.fromARGB(255, 0, 0, 255));
}
@pragma('vm:entry-point')
void pointer_data_packet() {
PlatformDispatcher.instance.onPointerDataPacket =
(PointerDataPacket packet) {
signalNativeCount(packet.data.length);
for (final PointerData pointerData in packet.data) {
signalNativeMessage(pointerData.toString());
}
};
signalNativeTest();
}
@pragma('vm:entry-point')
void pointer_data_packet_view_id() {
PlatformDispatcher.instance.onPointerDataPacket = (PointerDataPacket packet) {
assert(packet.data.length == 1);
for (final PointerData pointerData in packet.data) {
signalNativeMessage('ViewID: ${pointerData.viewId}');
}
};
signalNativeTest();
}
Map<int, Size> _getAllViewSizes() {
final Map<int, Size> result = <int, Size>{};
for (final FlutterView view in PlatformDispatcher.instance.views) {
result[view.viewId] = view.physicalSize;
}
return result;
}
List<int> _findDifferences(Map<int, Size> a, Map<int, Size> b) {
final Set<int> result = <int>{};
a.forEach((int viewId, Size sizeA) {
if (!b.containsKey(viewId) || b[viewId] != sizeA) {
result.add(viewId);
}
});
b.forEach((int viewId, Size sizeB) {
if (!a.containsKey(viewId)) {
result.add(viewId);
}
});
return result.toList()..sort();
}
@pragma('vm:entry-point')
void window_metrics_event_view_id() {
Map<int, Size> sizes = _getAllViewSizes();
PlatformDispatcher.instance.onMetricsChanged = () {
final Map<int, Size> newSizes = _getAllViewSizes();
final List<int> differences = _findDifferences(sizes, newSizes);
sizes = newSizes;
signalNativeMessage('Changed: $differences');
};
signalNativeTest();
}
@pragma('vm:entry-point')
void window_metrics_event_all_view_ids() {
PlatformDispatcher.instance.onMetricsChanged = () {
final List<int> viewIds =
PlatformDispatcher.instance.views.map((view) => view.viewId).toList();
viewIds.sort();
signalNativeMessage('View IDs: [${viewIds.join(', ')}]');
};
signalNativeTest();
}
@pragma('vm:entry-point')
Future<void> channel_listener_response() async {
channelBuffers.setListener('test/listen',
(ByteData? data, PlatformMessageResponseCallback callback) {
callback(null);
});
signalNativeTest();
}
@pragma('vm:entry-point')
void render_gradient_retained() {
OffsetEngineLayer? offsetLayer; // Retain the offset layer.
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final Size size = Size(800.0, 600.0);
final SceneBuilder builder = SceneBuilder();
offsetLayer = builder.pushOffset(0.0, 0.0, oldLayer: offsetLayer);
// display_list_layer will comparing the display_list
// no need to retain the picture
builder.addPicture(
Offset(0.0, 0.0), CreateGradientBox(size)); // gradient - flutter
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void render_impeller_gl_test() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
final Paint paint = Paint();
paint.color = Color.fromARGB(255, 0, 0, 255);
final PictureRecorder baseRecorder = PictureRecorder();
final Canvas canvas = Canvas(baseRecorder);
canvas.drawPaint(Paint()..color = Color.fromARGB(255, 255, 0, 0));
canvas.drawRect(Rect.fromLTRB(20.0, 20.0, 200.0, 150.0), paint);
builder.addPicture(Offset.zero, baseRecorder.endRecording());
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
};
PlatformDispatcher.instance.scheduleFrame();
}