-
Notifications
You must be signed in to change notification settings - Fork 6k
/
platform_dispatcher.dart
2187 lines (1981 loc) · 89.5 KB
/
platform_dispatcher.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// 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.
part of dart.ui;
/// Signature of callbacks that have no arguments and return no data.
typedef VoidCallback = void Function();
/// Signature for [PlatformDispatcher.onBeginFrame].
typedef FrameCallback = void Function(Duration duration);
/// Signature for [PlatformDispatcher.onReportTimings].
///
/// {@template dart.ui.TimingsCallback.list}
/// The callback takes a list of [FrameTiming] because it may not be
/// immediately triggered after each frame. Instead, Flutter tries to batch
/// frames together and send all their timings at once to decrease the
/// overhead (as this is available in the release mode). The list is sorted in
/// ascending order of time (earliest frame first). The timing of any frame
/// will be sent within about 1 second (100ms if in the profile/debug mode)
/// even if there are no later frames to batch. The timing of the first frame
/// will be sent immediately without batching.
/// {@endtemplate}
typedef TimingsCallback = void Function(List<FrameTiming> timings);
/// Signature for [PlatformDispatcher.onPointerDataPacket].
typedef PointerDataPacketCallback = void Function(PointerDataPacket packet);
/// Signature for [PlatformDispatcher.onKeyData].
///
/// The callback should return true if the key event has been handled by the
/// framework and should not be propagated further.
typedef KeyDataCallback = bool Function(KeyData data);
/// Signature for [PlatformDispatcher.onSemanticsAction].
typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args);
/// Signature for responses to platform messages.
///
/// Used as a parameter to [PlatformDispatcher.sendPlatformMessage] and
/// [PlatformDispatcher.onPlatformMessage].
typedef PlatformMessageResponseCallback = void Function(ByteData? data);
/// Signature for [PlatformDispatcher.onPlatformMessage].
// TODO(ianh): deprecate once framework uses [ChannelBuffers.setListener].
typedef PlatformMessageCallback = void Function(String name, ByteData? data, PlatformMessageResponseCallback? callback);
// Signature for _setNeedsReportTimings.
typedef _SetNeedsReportTimingsFunc = void Function(bool value);
/// Signature for [PlatformDispatcher.onConfigurationChanged].
typedef PlatformConfigurationChangedCallback = void Function(PlatformConfiguration configuration);
/// Signature for [PlatformDispatcher.onError].
///
/// If this method returns false, the engine may use some fallback method to
/// provide information about the error.
///
/// After calling this method, the process or the VM may terminate. Some severe
/// unhandled errors may not be able to call this method either, such as Dart
/// compilation errors or process terminating errors.
typedef ErrorCallback = bool Function(Object exception, StackTrace stackTrace);
// A gesture setting value that indicates it has not been set by the engine.
const double _kUnsetGestureSetting = -1.0;
// A message channel to receive KeyData from the platform.
//
// See embedder.cc::kFlutterKeyDataChannel for more information.
const String _kFlutterKeyDataChannel = 'flutter/keydata';
@pragma('vm:entry-point')
ByteData? _wrapUnmodifiableByteData(ByteData? byteData) =>
byteData == null ? null : UnmodifiableByteDataView(byteData);
/// A token that represents a root isolate.
class RootIsolateToken {
RootIsolateToken._(this._token);
/// An enumeration representing the root isolate (0 if not a root isolate).
final int _token;
/// The token for the root isolate that is executing this Dart code. If this
/// Dart code is not executing on a root isolate [instance] will be null.
static final RootIsolateToken? instance = () {
final int token = __getRootIsolateToken();
return token == 0 ? null : RootIsolateToken._(token);
}();
@FfiNative<Int64 Function()>('PlatformConfigurationNativeApi::GetRootIsolateToken')
external static int __getRootIsolateToken();
}
/// Platform event dispatcher singleton.
///
/// The most basic interface to the host operating system's interface.
///
/// This is the central entry point for platform messages and configuration
/// events from the platform.
///
/// It exposes the core scheduler API, the input event callback, the graphics
/// drawing API, and other such core services.
///
/// It manages the list of the application's [views] and the [screens] attached
/// to the device, as well as the [configuration] of various platform
/// attributes.
///
/// Consider avoiding static references to this singleton through
/// [PlatformDispatcher.instance] and instead prefer using a binding for
/// dependency resolution such as `WidgetsBinding.instance.platformDispatcher`.
/// See [PlatformDispatcher.instance] for more information about why this is
/// preferred.
class PlatformDispatcher {
/// Private constructor, since only dart:ui is supposed to create one of
/// these. Use [instance] to access the singleton.
PlatformDispatcher._() {
_setNeedsReportTimings = _nativeSetNeedsReportTimings;
}
/// The [PlatformDispatcher] singleton.
///
/// Consider avoiding static references to this singleton though
/// [PlatformDispatcher.instance] and instead prefer using a binding for
/// dependency resolution such as `WidgetsBinding.instance.platformDispatcher`.
///
/// Static access of this object means that Flutter has few, if any options to
/// fake or mock the given object in tests. Even in cases where Dart offers
/// special language constructs to forcefully shadow such properties, those
/// mechanisms would only be reasonable for tests and they would not be
/// reasonable for a future of Flutter where we legitimately want to select an
/// appropriate implementation at runtime.
///
/// The only place that `WidgetsBinding.instance.platformDispatcher` is
/// inappropriate is if access to these APIs is required before the binding is
/// initialized by invoking `runApp()` or
/// `WidgetsFlutterBinding.instance.ensureInitialized()`. In that case, it is
/// necessary (though unfortunate) to use the [PlatformDispatcher.instance]
/// object statically.
static PlatformDispatcher get instance => _instance;
static final PlatformDispatcher _instance = PlatformDispatcher._();
/// The current platform configuration.
///
/// If values in this configuration change, [onPlatformConfigurationChanged]
/// will be called.
PlatformConfiguration get configuration => _configuration;
PlatformConfiguration _configuration = const PlatformConfiguration();
/// Called when the platform configuration changes.
///
/// The engine invokes this callback in the same zone in which the callback
/// was set.
VoidCallback? get onPlatformConfigurationChanged => _onPlatformConfigurationChanged;
VoidCallback? _onPlatformConfigurationChanged;
Zone _onPlatformConfigurationChangedZone = Zone.root;
set onPlatformConfigurationChanged(VoidCallback? callback) {
_onPlatformConfigurationChanged = callback;
_onPlatformConfigurationChangedZone = Zone.current;
}
/// The current list of views, including top level platform windows used by
/// the application.
///
/// If any of their configurations change, [onMetricsChanged] will be called.
Iterable<FlutterView> get views => _views.values;
final Map<Object, FlutterView> _views = <Object, FlutterView>{};
// A map of opaque platform view identifiers to view configurations.
final Map<Object, ViewConfiguration> _viewConfigurations = <Object, ViewConfiguration>{};
/// A callback that is invoked whenever the [ViewConfiguration] of any of the
/// [views] changes.
///
/// For example when the device is rotated or when the application is resized
/// (e.g. when showing applications side-by-side on Android),
/// `onMetricsChanged` is called.
///
/// The engine invokes this callback in the same zone in which the callback
/// was set.
///
/// The framework registers with this callback and updates the layout
/// appropriately.
///
/// See also:
///
/// * [WidgetsBindingObserver], for a mechanism at the widgets layer to
/// register for notifications when this is called.
/// * [MediaQuery.of], a simpler mechanism for the same.
VoidCallback? get onMetricsChanged => _onMetricsChanged;
VoidCallback? _onMetricsChanged;
Zone _onMetricsChangedZone = Zone.root;
set onMetricsChanged(VoidCallback? callback) {
_onMetricsChanged = callback;
_onMetricsChangedZone = Zone.current;
}
// Called from the engine, via hooks.dart
//
// Updates the metrics of the window with the given id.
void _updateWindowMetrics(
Object id,
double devicePixelRatio,
double width,
double height,
double viewPaddingTop,
double viewPaddingRight,
double viewPaddingBottom,
double viewPaddingLeft,
double viewInsetTop,
double viewInsetRight,
double viewInsetBottom,
double viewInsetLeft,
double systemGestureInsetTop,
double systemGestureInsetRight,
double systemGestureInsetBottom,
double systemGestureInsetLeft,
double physicalTouchSlop,
List<double> displayFeaturesBounds,
List<int> displayFeaturesType,
List<int> displayFeaturesState,
) {
final ViewConfiguration previousConfiguration =
_viewConfigurations[id] ?? const ViewConfiguration();
if (!_views.containsKey(id)) {
_views[id] = FlutterWindow._(id, this);
}
_viewConfigurations[id] = previousConfiguration.copyWith(
window: _views[id],
devicePixelRatio: devicePixelRatio,
geometry: Rect.fromLTWH(0.0, 0.0, width, height),
viewPadding: WindowPadding._(
top: viewPaddingTop,
right: viewPaddingRight,
bottom: viewPaddingBottom,
left: viewPaddingLeft,
),
viewInsets: WindowPadding._(
top: viewInsetTop,
right: viewInsetRight,
bottom: viewInsetBottom,
left: viewInsetLeft,
),
padding: WindowPadding._(
top: math.max(0.0, viewPaddingTop - viewInsetTop),
right: math.max(0.0, viewPaddingRight - viewInsetRight),
bottom: math.max(0.0, viewPaddingBottom - viewInsetBottom),
left: math.max(0.0, viewPaddingLeft - viewInsetLeft),
),
systemGestureInsets: WindowPadding._(
top: math.max(0.0, systemGestureInsetTop),
right: math.max(0.0, systemGestureInsetRight),
bottom: math.max(0.0, systemGestureInsetBottom),
left: math.max(0.0, systemGestureInsetLeft),
),
// -1 is used as a sentinel for an undefined touch slop
gestureSettings: GestureSettings(
physicalTouchSlop: physicalTouchSlop == _kUnsetGestureSetting ? null : physicalTouchSlop,
),
displayFeatures: _decodeDisplayFeatures(
bounds: displayFeaturesBounds,
type: displayFeaturesType,
state: displayFeaturesState,
devicePixelRatio: devicePixelRatio,
),
);
_invoke(onMetricsChanged, _onMetricsChangedZone);
}
List<DisplayFeature> _decodeDisplayFeatures({
required List<double> bounds,
required List<int> type,
required List<int> state,
required double devicePixelRatio,
}) {
assert(bounds.length / 4 == type.length, 'Bounds are rectangles, requiring 4 measurements each');
assert(type.length == state.length);
final List<DisplayFeature> result = <DisplayFeature>[];
for(int i = 0; i < type.length; i++) {
final int rectOffset = i * 4;
result.add(DisplayFeature(
bounds: Rect.fromLTRB(
bounds[rectOffset] / devicePixelRatio,
bounds[rectOffset + 1] / devicePixelRatio,
bounds[rectOffset + 2] / devicePixelRatio,
bounds[rectOffset + 3] / devicePixelRatio,
),
type: DisplayFeatureType.values[type[i]],
state: state[i] < DisplayFeatureState.values.length
? DisplayFeatureState.values[state[i]]
: DisplayFeatureState.unknown,
));
}
return result;
}
/// A callback invoked when any view begins a frame.
///
/// A callback that is invoked to notify the application that it is an
/// appropriate time to provide a scene using the [SceneBuilder] API and the
/// [FlutterView.render] method.
///
/// When possible, this is driven by the hardware VSync signal of the attached
/// screen with the highest VSync rate. This is only called if
/// [PlatformDispatcher.scheduleFrame] has been called since the last time
/// this callback was invoked.
FrameCallback? get onBeginFrame => _onBeginFrame;
FrameCallback? _onBeginFrame;
Zone _onBeginFrameZone = Zone.root;
set onBeginFrame(FrameCallback? callback) {
_onBeginFrame = callback;
_onBeginFrameZone = Zone.current;
}
// Called from the engine, via hooks.dart
void _beginFrame(int microseconds) {
_invoke1<Duration>(
onBeginFrame,
_onBeginFrameZone,
Duration(microseconds: microseconds),
);
}
/// A callback that is invoked for each frame after [onBeginFrame] has
/// completed and after the microtask queue has been drained.
///
/// This can be used to implement a second phase of frame rendering that
/// happens after any deferred work queued by the [onBeginFrame] phase.
VoidCallback? get onDrawFrame => _onDrawFrame;
VoidCallback? _onDrawFrame;
Zone _onDrawFrameZone = Zone.root;
set onDrawFrame(VoidCallback? callback) {
_onDrawFrame = callback;
_onDrawFrameZone = Zone.current;
}
// Called from the engine, via hooks.dart
void _drawFrame() {
_invoke(onDrawFrame, _onDrawFrameZone);
}
/// A callback that is invoked when pointer data is available.
///
/// The framework invokes this callback in the same zone in which the callback
/// was set.
///
/// See also:
///
/// * [GestureBinding], the Flutter framework class which manages pointer
/// events.
PointerDataPacketCallback? get onPointerDataPacket => _onPointerDataPacket;
PointerDataPacketCallback? _onPointerDataPacket;
Zone _onPointerDataPacketZone = Zone.root;
set onPointerDataPacket(PointerDataPacketCallback? callback) {
_onPointerDataPacket = callback;
_onPointerDataPacketZone = Zone.current;
}
// Called from the engine, via hooks.dart
void _dispatchPointerDataPacket(ByteData packet) {
if (onPointerDataPacket != null) {
_invoke1<PointerDataPacket>(
onPointerDataPacket,
_onPointerDataPacketZone,
_unpackPointerDataPacket(packet),
);
}
}
// If this value changes, update the encoding code in the following files:
//
// * pointer_data.cc
// * pointer.dart
// * AndroidTouchProcessor.java
static const int _kPointerDataFieldCount = 35;
static PointerDataPacket _unpackPointerDataPacket(ByteData packet) {
const int kStride = Int64List.bytesPerElement;
const int kBytesPerPointerData = _kPointerDataFieldCount * kStride;
final int length = packet.lengthInBytes ~/ kBytesPerPointerData;
assert(length * kBytesPerPointerData == packet.lengthInBytes);
final List<PointerData> data = <PointerData>[];
for (int i = 0; i < length; ++i) {
int offset = i * _kPointerDataFieldCount;
data.add(PointerData(
embedderId: packet.getInt64(kStride * offset++, _kFakeHostEndian),
timeStamp: Duration(microseconds: packet.getInt64(kStride * offset++, _kFakeHostEndian)),
change: PointerChange.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)],
kind: PointerDeviceKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)],
signalKind: PointerSignalKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)],
device: packet.getInt64(kStride * offset++, _kFakeHostEndian),
pointerIdentifier: packet.getInt64(kStride * offset++, _kFakeHostEndian),
physicalX: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
physicalY: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
physicalDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
physicalDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
buttons: packet.getInt64(kStride * offset++, _kFakeHostEndian),
obscured: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0,
synthesized: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0,
pressure: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
pressureMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
pressureMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
distance: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
distanceMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
size: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
radiusMajor: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
radiusMinor: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
radiusMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
radiusMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
orientation: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
tilt: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
platformData: packet.getInt64(kStride * offset++, _kFakeHostEndian),
scrollDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
scrollDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
panX: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
panY: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
panDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
panDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
scale: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
rotation: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
));
assert(offset == (i + 1) * _kPointerDataFieldCount);
}
return PointerDataPacket(data: data);
}
static ChannelCallback _keyDataListener(KeyDataCallback onKeyData, Zone zone) =>
(ByteData? packet, PlatformMessageResponseCallback callback) {
_invoke1<KeyData>(
(KeyData keyData) {
final bool handled = onKeyData(keyData);
final Uint8List response = Uint8List(1);
response[0] = handled ? 1 : 0;
callback(response.buffer.asByteData());
},
zone,
_unpackKeyData(packet!),
);
};
/// A callback that is invoked when key data is available.
///
/// The framework invokes this callback in the same zone in which the callback
/// was set.
///
/// The callback should return true if the key event has been handled by the
/// framework and should not be propagated further.
KeyDataCallback? get onKeyData => _onKeyData;
KeyDataCallback? _onKeyData;
set onKeyData(KeyDataCallback? callback) {
_onKeyData = callback;
if (callback != null) {
channelBuffers.setListener(_kFlutterKeyDataChannel, _keyDataListener(callback, Zone.current));
} else {
channelBuffers.clearListener(_kFlutterKeyDataChannel);
}
}
// If this value changes, update the encoding code in the following files:
//
// * key_data.h
// * key.dart (ui)
// * key.dart (web_ui)
// * HardwareKeyboard.java
static const int _kKeyDataFieldCount = 5;
// The packet structure is described in `key_data_packet.h`.
static KeyData _unpackKeyData(ByteData packet) {
const int kStride = Int64List.bytesPerElement;
int offset = 0;
final int charDataSize = packet.getUint64(kStride * offset++, _kFakeHostEndian);
final String? character = charDataSize == 0 ? null : utf8.decoder.convert(
packet.buffer.asUint8List(kStride * (offset + _kKeyDataFieldCount), charDataSize));
final KeyData keyData = KeyData(
timeStamp: Duration(microseconds: packet.getUint64(kStride * offset++, _kFakeHostEndian)),
type: KeyEventType.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)],
physical: packet.getUint64(kStride * offset++, _kFakeHostEndian),
logical: packet.getUint64(kStride * offset++, _kFakeHostEndian),
character: character,
synthesized: packet.getUint64(kStride * offset++, _kFakeHostEndian) != 0,
);
return keyData;
}
/// A callback that is invoked to report the [FrameTiming] of recently
/// rasterized frames.
///
/// It's preferred to use [SchedulerBinding.addTimingsCallback] than to use
/// [onReportTimings] directly because [SchedulerBinding.addTimingsCallback]
/// allows multiple callbacks.
///
/// This can be used to see if the application has missed frames (through
/// [FrameTiming.buildDuration] and [FrameTiming.rasterDuration]), or high
/// latencies (through [FrameTiming.totalSpan]).
///
/// Unlike [Timeline], the timing information here is available in the release
/// mode (additional to the profile and the debug mode). Hence this can be
/// used to monitor the application's performance in the wild.
///
/// {@macro dart.ui.TimingsCallback.list}
///
/// If this is null, no additional work will be done. If this is not null,
/// Flutter spends less than 0.1ms every 1 second to report the timings
/// (measured on iPhone6S). The 0.1ms is about 0.6% of 16ms (frame budget for
/// 60fps), or 0.01% CPU usage per second.
TimingsCallback? get onReportTimings => _onReportTimings;
TimingsCallback? _onReportTimings;
Zone _onReportTimingsZone = Zone.root;
set onReportTimings(TimingsCallback? callback) {
if ((callback == null) != (_onReportTimings == null)) {
_setNeedsReportTimings(callback != null);
}
_onReportTimings = callback;
_onReportTimingsZone = Zone.current;
}
late _SetNeedsReportTimingsFunc _setNeedsReportTimings;
void _nativeSetNeedsReportTimings(bool value) => __nativeSetNeedsReportTimings(value);
@FfiNative<Void Function(Bool)>('PlatformConfigurationNativeApi::SetNeedsReportTimings')
external static void __nativeSetNeedsReportTimings(bool value);
// Called from the engine, via hooks.dart
void _reportTimings(List<int> timings) {
assert(timings.length % FrameTiming._dataLength == 0);
final List<FrameTiming> frameTimings = <FrameTiming>[];
for (int i = 0; i < timings.length; i += FrameTiming._dataLength) {
frameTimings.add(FrameTiming._(timings.sublist(i, i + FrameTiming._dataLength)));
}
_invoke1(onReportTimings, _onReportTimingsZone, frameTimings);
}
/// Sends a message to a platform-specific plugin.
///
/// The `name` parameter determines which plugin receives the message. The
/// `data` parameter contains the message payload and is typically UTF-8
/// encoded JSON but can be arbitrary data. If the plugin replies to the
/// message, `callback` will be called with the response.
///
/// The framework invokes [callback] in the same zone in which this method was
/// called.
void sendPlatformMessage(String name, ByteData? data, PlatformMessageResponseCallback? callback) {
final String? error =
_sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
if (error != null) {
throw Exception(error);
}
}
String? _sendPlatformMessage(String name, PlatformMessageResponseCallback? callback, ByteData? data) =>
__sendPlatformMessage(name, callback, data);
@FfiNative<Handle Function(Handle, Handle, Handle)>('PlatformConfigurationNativeApi::SendPlatformMessage')
external static String? __sendPlatformMessage(String name, PlatformMessageResponseCallback? callback, ByteData? data);
/// Sends a message to a platform-specific plugin via a [SendPort].
///
/// This operates similarly to [sendPlatformMessage] but is used when sending
/// messages from background isolates. The [port] parameter allows Flutter to
/// know which isolate to send the result to. The [name] parameter is the name
/// of the channel communication will happen on. The [data] parameter is the
/// payload of the message. The [identifier] parameter is a unique integer
/// assigned to the message.
void sendPortPlatformMessage(
String name,
ByteData? data,
int identifier,
SendPort port) {
final String? error =
_sendPortPlatformMessage(name, identifier, port.nativePort, data);
if (error != null) {
throw Exception(error);
}
}
String? _sendPortPlatformMessage(String name, int identifier, int port, ByteData? data) =>
__sendPortPlatformMessage(name, identifier, port, data);
@FfiNative<Handle Function(Handle, Handle, Handle, Handle)>('PlatformConfigurationNativeApi::SendPortPlatformMessage')
external static String? __sendPortPlatformMessage(String name, int identifier, int port, ByteData? data);
/// Registers the current isolate with the isolate identified with by the
/// [token]. This is required if platform channels are to be used on a
/// background isolate.
void registerBackgroundIsolate(RootIsolateToken token) {
DartPluginRegistrant.ensureInitialized();
__registerBackgroundIsolate(token._token);
}
@FfiNative<Void Function(Int64)>('PlatformConfigurationNativeApi::RegisterBackgroundIsolate')
external static void __registerBackgroundIsolate(int rootIsolateId);
/// Called whenever this platform dispatcher receives a message from a
/// platform-specific plugin.
///
/// The `name` parameter determines which plugin sent the message. The `data`
/// parameter is the payload and is typically UTF-8 encoded JSON but can be
/// arbitrary data.
///
/// Message handlers must call the function given in the `callback` parameter.
/// If the handler does not need to respond, the handler should pass null to
/// the callback.
///
/// The framework invokes this callback in the same zone in which the callback
/// was set.
// TODO(ianh): Deprecate onPlatformMessage once the framework is moved over
// to using channel buffers exclusively.
PlatformMessageCallback? get onPlatformMessage => _onPlatformMessage;
PlatformMessageCallback? _onPlatformMessage;
Zone _onPlatformMessageZone = Zone.root;
set onPlatformMessage(PlatformMessageCallback? callback) {
_onPlatformMessage = callback;
_onPlatformMessageZone = Zone.current;
}
/// Called by [_dispatchPlatformMessage].
void _respondToPlatformMessage(int responseId, ByteData? data) => __respondToPlatformMessage(responseId, data);
@FfiNative<Void Function(IntPtr, Handle)>('PlatformConfigurationNativeApi::RespondToPlatformMessage')
external static void __respondToPlatformMessage(int responseId, ByteData? data);
/// Wraps the given [callback] in another callback that ensures that the
/// original callback is called in the zone it was registered in.
static PlatformMessageResponseCallback? _zonedPlatformMessageResponseCallback(
PlatformMessageResponseCallback? callback,
) {
if (callback == null) {
return null;
}
// Store the zone in which the callback is being registered.
final Zone registrationZone = Zone.current;
return (ByteData? data) {
registrationZone.runUnaryGuarded(callback, data);
};
}
/// Send a message to the framework using the [ChannelBuffers].
///
/// This method constructs the appropriate callback to respond
/// with the given `responseId`. It should only be called for messages
/// from the platform.
void _dispatchPlatformMessage(String name, ByteData? data, int responseId) {
if (name == ChannelBuffers.kControlChannelName) {
try {
channelBuffers.handleMessage(data!);
} finally {
_respondToPlatformMessage(responseId, null);
}
} else if (onPlatformMessage != null) {
_invoke3<String, ByteData?, PlatformMessageResponseCallback>(
onPlatformMessage,
_onPlatformMessageZone,
name,
data,
(ByteData? responseData) {
_respondToPlatformMessage(responseId, responseData);
},
);
} else {
channelBuffers.push(name, data, (ByteData? responseData) {
_respondToPlatformMessage(responseId, responseData);
});
}
}
/// Set the debug name associated with this platform dispatcher's root
/// isolate.
///
/// Normally debug names are automatically generated from the Dart port, entry
/// point, and source file. For example: `main.dart$main-1234`.
///
/// This can be combined with flutter tools `--isolate-filter` flag to debug
/// specific root isolates. For example: `flutter attach --isolate-filter=[name]`.
/// Note that this does not rename any child isolates of the root.
void setIsolateDebugName(String name) => _setIsolateDebugName(name);
@FfiNative<Void Function(Handle)>('PlatformConfigurationNativeApi::SetIsolateDebugName')
external static void _setIsolateDebugName(String name);
/// Requests the Dart VM to adjusts the GC heuristics based on the requested `performance_mode`.
///
/// This operation is a no-op of web. The request to change a performance may be ignored by the
/// engine or not resolve in a predictable way.
///
/// See [DartPerformanceMode] for more information on individual performance modes.
void requestDartPerformanceMode(DartPerformanceMode mode) {
_requestDartPerformanceMode(mode.index);
}
@FfiNative<Int Function(Int)>('PlatformConfigurationNativeApi::RequestDartPerformanceMode')
external static int _requestDartPerformanceMode(int mode);
/// The embedder can specify data that the isolate can request synchronously
/// on launch. This accessor fetches that data.
///
/// This data is persistent for the duration of the Flutter application and is
/// available even after isolate restarts. Because of this lifecycle, the size
/// of this data must be kept to a minimum.
///
/// For asynchronous communication between the embedder and isolate, a
/// platform channel may be used.
ByteData? getPersistentIsolateData() => _getPersistentIsolateData();
@FfiNative<Handle Function()>('PlatformConfigurationNativeApi::GetPersistentIsolateData')
external static ByteData? _getPersistentIsolateData();
/// Requests that, at the next appropriate opportunity, the [onBeginFrame] and
/// [onDrawFrame] callbacks be invoked.
///
/// See also:
///
/// * [SchedulerBinding], the Flutter framework class which manages the
/// scheduling of frames.
void scheduleFrame() => _scheduleFrame();
@FfiNative<Void Function()>('PlatformConfigurationNativeApi::ScheduleFrame')
external static void _scheduleFrame();
/// Additional accessibility features that may be enabled by the platform.
AccessibilityFeatures get accessibilityFeatures => configuration.accessibilityFeatures;
/// A callback that is invoked when the value of [accessibilityFeatures]
/// changes.
///
/// The framework invokes this callback in the same zone in which the callback
/// was set.
VoidCallback? get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged;
VoidCallback? _onAccessibilityFeaturesChanged;
Zone _onAccessibilityFeaturesChangedZone = Zone.root;
set onAccessibilityFeaturesChanged(VoidCallback? callback) {
_onAccessibilityFeaturesChanged = callback;
_onAccessibilityFeaturesChangedZone = Zone.current;
}
// Called from the engine, via hooks.dart
void _updateAccessibilityFeatures(int values) {
final AccessibilityFeatures newFeatures = AccessibilityFeatures._(values);
final PlatformConfiguration previousConfiguration = configuration;
if (newFeatures == previousConfiguration.accessibilityFeatures) {
return;
}
_configuration = previousConfiguration.copyWith(
accessibilityFeatures: newFeatures,
);
_invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone,);
_invoke(onAccessibilityFeaturesChanged, _onAccessibilityFeaturesChangedZone,);
}
/// Change the retained semantics data about this platform dispatcher.
///
/// If [semanticsEnabled] is true, the user has requested that this function
/// be called whenever the semantic content of this platform dispatcher
/// changes.
///
/// In either case, this function disposes the given update, which means the
/// semantics update cannot be used further.
@Deprecated('''
In a multi-view world, the platform dispatcher can no longer provide apis
to update semantics since each view will host its own semantics tree.
Semantics updates must be passed to an individual [FlutterView]. To update
semantics, use PlatformDispatcher.instance.views to get a [FlutterView] and
call `updateSemantics`.
''')
void updateSemantics(SemanticsUpdate update) => _updateSemantics(update);
@FfiNative<Void Function(Pointer<Void>)>('PlatformConfigurationNativeApi::UpdateSemantics')
external static void _updateSemantics(SemanticsUpdate update);
/// The system-reported default locale of the device.
///
/// This establishes the language and formatting conventions that application
/// should, if possible, use to render their user interface.
///
/// This is the first locale selected by the user and is the user's primary
/// locale (the locale the device UI is displayed in)
///
/// This is equivalent to `locales.first`, except that it will provide an
/// undefined (using the language tag "und") non-null locale if the [locales]
/// list has not been set or is empty.
Locale get locale => locales.isEmpty ? const Locale.fromSubtags() : locales.first;
/// The full system-reported supported locales of the device.
///
/// This establishes the language and formatting conventions that application
/// should, if possible, use to render their user interface.
///
/// The list is ordered in order of priority, with lower-indexed locales being
/// preferred over higher-indexed ones. The first element is the primary
/// [locale].
///
/// The [onLocaleChanged] callback is called whenever this value changes.
///
/// See also:
///
/// * [WidgetsBindingObserver], for a mechanism at the widgets layer to
/// observe when this value changes.
List<Locale> get locales => configuration.locales;
/// Performs the platform-native locale resolution.
///
/// Each platform may return different results.
///
/// If the platform fails to resolve a locale, then this will return null.
///
/// This method returns synchronously and is a direct call to
/// platform specific APIs without invoking method channels.
Locale? computePlatformResolvedLocale(List<Locale> supportedLocales) {
final List<String?> supportedLocalesData = <String?>[];
for (final Locale locale in supportedLocales) {
supportedLocalesData.add(locale.languageCode);
supportedLocalesData.add(locale.countryCode);
supportedLocalesData.add(locale.scriptCode);
}
final List<String> result = _computePlatformResolvedLocale(supportedLocalesData);
if (result.isNotEmpty) {
return Locale.fromSubtags(
languageCode: result[0],
countryCode: result[1] == '' ? null : result[1],
scriptCode: result[2] == '' ? null : result[2]);
}
return null;
}
List<String> _computePlatformResolvedLocale(List<String?> supportedLocalesData) => __computePlatformResolvedLocale(supportedLocalesData);
@FfiNative<Handle Function(Handle)>('PlatformConfigurationNativeApi::ComputePlatformResolvedLocale')
external static List<String> __computePlatformResolvedLocale(List<String?> supportedLocalesData);
/// A callback that is invoked whenever [locale] changes value.
///
/// The framework invokes this callback in the same zone in which the callback
/// was set.
///
/// See also:
///
/// * [WidgetsBindingObserver], for a mechanism at the widgets layer to
/// observe when this callback is invoked.
VoidCallback? get onLocaleChanged => _onLocaleChanged;
VoidCallback? _onLocaleChanged;
Zone _onLocaleChangedZone = Zone.root;
set onLocaleChanged(VoidCallback? callback) {
_onLocaleChanged = callback;
_onLocaleChangedZone = Zone.current;
}
// Called from the engine, via hooks.dart
void _updateLocales(List<String> locales) {
const int stringsPerLocale = 4;
final int numLocales = locales.length ~/ stringsPerLocale;
final PlatformConfiguration previousConfiguration = configuration;
final List<Locale> newLocales = <Locale>[];
bool localesDiffer = numLocales != previousConfiguration.locales.length;
for (int localeIndex = 0; localeIndex < numLocales; localeIndex++) {
final String countryCode = locales[localeIndex * stringsPerLocale + 1];
final String scriptCode = locales[localeIndex * stringsPerLocale + 2];
newLocales.add(Locale.fromSubtags(
languageCode: locales[localeIndex * stringsPerLocale],
countryCode: countryCode.isEmpty ? null : countryCode,
scriptCode: scriptCode.isEmpty ? null : scriptCode,
));
if (!localesDiffer && newLocales[localeIndex] != previousConfiguration.locales[localeIndex]) {
localesDiffer = true;
}
}
if (!localesDiffer) {
return;
}
_configuration = previousConfiguration.copyWith(locales: newLocales);
_invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone);
_invoke(onLocaleChanged, _onLocaleChangedZone);
}
// Called from the engine, via hooks.dart
String _localeClosure() => locale.toString();
/// The lifecycle state immediately after dart isolate initialization.
///
/// This property will not be updated as the lifecycle changes.
///
/// It is used to initialize [SchedulerBinding.lifecycleState] at startup with
/// any buffered lifecycle state events.
String get initialLifecycleState {
_initialLifecycleStateAccessed = true;
return _initialLifecycleState;
}
late String _initialLifecycleState;
/// Tracks if the initial state has been accessed. Once accessed, we will stop
/// updating the [initialLifecycleState], as it is not the preferred way to
/// access the state.
bool _initialLifecycleStateAccessed = false;
// Called from the engine, via hooks.dart
void _updateLifecycleState(String state) {
// We do not update the state if the state has already been used to initialize
// the lifecycleState.
if (!_initialLifecycleStateAccessed) {
_initialLifecycleState = state;
}
}
/// The setting indicating whether time should always be shown in the 24-hour
/// format.
///
/// This option is used by [showTimePicker].
bool get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat;
/// The system-reported text scale.
///
/// This establishes the text scaling factor to use when rendering text,
/// according to the user's platform preferences.
///
/// The [onTextScaleFactorChanged] callback is called whenever this value
/// changes.
///
/// See also:
///
/// * [WidgetsBindingObserver], for a mechanism at the widgets layer to
/// observe when this value changes.
double get textScaleFactor => configuration.textScaleFactor;
/// A callback that is invoked whenever [textScaleFactor] changes value.
///
/// The framework invokes this callback in the same zone in which the callback
/// was set.
///
/// See also:
///
/// * [WidgetsBindingObserver], for a mechanism at the widgets layer to
/// observe when this callback is invoked.
VoidCallback? get onTextScaleFactorChanged => _onTextScaleFactorChanged;
VoidCallback? _onTextScaleFactorChanged;
Zone _onTextScaleFactorChangedZone = Zone.root;
set onTextScaleFactorChanged(VoidCallback? callback) {
_onTextScaleFactorChanged = callback;
_onTextScaleFactorChangedZone = Zone.current;
}
/// Whether the spell check service is supported on the current platform.
///
/// This option is used by [EditableTextState] to define its
/// [SpellCheckConfiguration] when a default spell check service
/// is requested.
bool get nativeSpellCheckServiceDefined => _nativeSpellCheckServiceDefined;
bool _nativeSpellCheckServiceDefined = false;
/// Whether briefly displaying the characters as you type in obscured text
/// fields is enabled in system settings.
///
/// See also:
///
/// * [EditableText.obscureText], which when set to true hides the text in
/// the text field.
bool get brieflyShowPassword => _brieflyShowPassword;
bool _brieflyShowPassword = true;
/// The setting indicating the current brightness mode of the host platform.
/// If the platform has no preference, [platformBrightness] defaults to
/// [Brightness.light].
Brightness get platformBrightness => configuration.platformBrightness;
/// A callback that is invoked whenever [platformBrightness] changes value.
///
/// The framework invokes this callback in the same zone in which the callback
/// was set.
///
/// See also:
///
/// * [WidgetsBindingObserver], for a mechanism at the widgets layer to
/// observe when this callback is invoked.
VoidCallback? get onPlatformBrightnessChanged => _onPlatformBrightnessChanged;
VoidCallback? _onPlatformBrightnessChanged;
Zone _onPlatformBrightnessChangedZone = Zone.root;
set onPlatformBrightnessChanged(VoidCallback? callback) {
_onPlatformBrightnessChanged = callback;
_onPlatformBrightnessChangedZone = Zone.current;
}
/// The setting indicating the current system font of the host platform.
String? get systemFontFamily => configuration.systemFontFamily;
/// A callback that is invoked whenever [systemFontFamily] changes value.
///
/// The framework invokes this callback in the same zone in which the callback
/// was set.
///
/// See also:
///
/// * [WidgetsBindingObserver], for a mechanism at the widgets layer to
/// observe when this callback is invoked.
VoidCallback? get onSystemFontFamilyChanged => _onSystemFontFamilyChanged;