1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:async';
6import 'dart:ui';
7
8import 'package:flutter/foundation.dart';
9
10import 'binding.dart';
11import 'system_channels.dart';
12
13export 'dart:ui' show Brightness, Color;
14
15export 'binding.dart' show SystemUiChangeCallback;
16
17/// Specifies a particular device orientation.
18///
19/// To determine which values correspond to which orientations, first position
20/// the device in its default orientation (this is the orientation that the
21/// system first uses for its boot logo, or the orientation in which the
22/// hardware logos or markings are upright, or the orientation in which the
23/// cameras are at the top). If this is a portrait orientation, then this is
24/// [portraitUp]. Otherwise, it's [landscapeLeft]. As you rotate the device by
25/// 90 degrees in a counter-clockwise direction around the axis that pierces the
26/// screen, you step through each value in this enum in the order given.
27///
28/// For a device with a landscape default orientation, the orientation obtained
29/// by rotating the device 90 degrees clockwise from its default orientation is
30/// [portraitUp].
31///
32/// Used by [SystemChrome.setPreferredOrientations].
33enum DeviceOrientation {
34 /// If the device shows its boot logo in portrait, then the boot logo is shown
35 /// in [portraitUp]. Otherwise, the device shows its boot logo in landscape
36 /// and this orientation is obtained by rotating the device 90 degrees
37 /// clockwise from its boot orientation.
38 portraitUp,
39
40 /// The orientation that is 90 degrees clockwise from [portraitUp].
41 ///
42 /// If the device shows its boot logo in landscape, then the boot logo is
43 /// shown in [landscapeLeft].
44 landscapeLeft,
45
46 /// The orientation that is 180 degrees from [portraitUp].
47 portraitDown,
48
49 /// The orientation that is 90 degrees counterclockwise from [portraitUp].
50 landscapeRight,
51}
52
53/// Specifies a description of the application that is pertinent to the
54/// embedder's application switcher (also known as "recent tasks") user
55/// interface.
56///
57/// Used by [SystemChrome.setApplicationSwitcherDescription].
58@immutable
59class ApplicationSwitcherDescription {
60 /// Creates an ApplicationSwitcherDescription.
61 const ApplicationSwitcherDescription({ this.label, this.primaryColor });
62
63 /// A label and description of the current state of the application.
64 final String? label;
65
66 /// The application's primary color.
67 ///
68 /// This may influence the color that the operating system uses to represent
69 /// the application.
70 final int? primaryColor;
71}
72
73/// Specifies a system overlay at a particular location.
74///
75/// Used by [SystemChrome.setEnabledSystemUIMode].
76enum SystemUiOverlay {
77 /// The status bar provided by the embedder on the top of the application
78 /// surface, if any.
79 top,
80
81 /// The status bar provided by the embedder on the bottom of the application
82 /// surface, if any.
83 bottom,
84}
85
86/// Describes different display configurations for both Android and iOS.
87///
88/// These modes mimic Android-specific display setups.
89///
90/// Used by [SystemChrome.setEnabledSystemUIMode].
91enum SystemUiMode {
92 /// Fullscreen display with status and navigation bars presentable by tapping
93 /// anywhere on the display.
94 ///
95 /// Available starting at SDK 16 or Android J. Earlier versions of Android
96 /// will not be affected by this setting.
97 ///
98 /// For applications running on iOS, the status bar and home indicator will be
99 /// hidden for a similar fullscreen experience.
100 ///
101 /// Tapping on the screen displays overlays, this gesture is not received by
102 /// the application.
103 ///
104 /// See also:
105 ///
106 /// * [SystemUiChangeCallback], used to listen and respond to the change in
107 /// system overlays.
108 leanBack,
109
110 /// Fullscreen display with status and navigation bars presentable through a
111 /// swipe gesture at the edges of the display.
112 ///
113 /// Available starting at SDK 19 or Android K. Earlier versions of Android
114 /// will not be affected by this setting.
115 ///
116 /// For applications running on iOS, the status bar and home indicator will be
117 /// hidden for a similar fullscreen experience.
118 ///
119 /// A swipe gesture from the edge of the screen displays overlays. In contrast
120 /// to [SystemUiMode.immersiveSticky], this gesture is not received by the
121 /// application.
122 ///
123 /// See also:
124 ///
125 /// * [SystemUiChangeCallback], used to listen and respond to the change in
126 /// system overlays.
127 immersive,
128
129 /// Fullscreen display with status and navigation bars presentable through a
130 /// swipe gesture at the edges of the display.
131 ///
132 /// Available starting at SDK 19 or Android K. Earlier versions of Android
133 /// will not be affected by this setting.
134 ///
135 /// For applications running on iOS, the status bar and home indicator will be
136 /// hidden for a similar fullscreen experience.
137 ///
138 /// A swipe gesture from the edge of the screen displays overlays. In contrast
139 /// to [SystemUiMode.immersive], this gesture is received by the application.
140 ///
141 /// See also:
142 ///
143 /// * [SystemUiChangeCallback], used to listen and respond to the change in
144 /// system overlays.
145 immersiveSticky,
146
147 /// Fullscreen display with status and navigation elements rendered over the
148 /// application.
149 ///
150 /// Available starting at SDK 29 or Android 10. Earlier versions of Android
151 /// will not be affected by this setting.
152 ///
153 /// For applications running on iOS, the status bar and home indicator will be
154 /// visible.
155 ///
156 /// The system overlays will not disappear or reappear in this mode as they
157 /// are permanently displayed on top of the application.
158 ///
159 /// See also:
160 ///
161 /// * [SystemUiOverlayStyle], can be used to configure transparent status and
162 /// navigation bars with or without a contrast scrim.
163 edgeToEdge,
164
165 /// Declares manually configured [SystemUiOverlay]s.
166 ///
167 /// When using this mode with [SystemChrome.setEnabledSystemUIMode], the
168 /// preferred overlays must be set by the developer.
169 ///
170 /// When [SystemUiOverlay.top] is enabled, the status bar will remain visible
171 /// on all platforms. Omitting this overlay will hide the status bar on iOS &
172 /// Android.
173 ///
174 /// When [SystemUiOverlay.bottom] is enabled, the navigation bar and home
175 /// indicator of Android and iOS applications will remain visible. Omitting this
176 /// overlay will hide them.
177 ///
178 /// Omitting both overlays will result in the same configuration as
179 /// [SystemUiMode.leanBack].
180 manual,
181}
182
183/// Specifies a preference for the style of the system overlays.
184///
185/// Used by [AppBar.systemOverlayStyle] for declaratively setting the style of
186/// the system overlays, and by [SystemChrome.setSystemUIOverlayStyle] for
187/// imperatively setting the style of the systeme overlays.
188@immutable
189class SystemUiOverlayStyle {
190 /// Creates a new [SystemUiOverlayStyle].
191 const SystemUiOverlayStyle({
192 this.systemNavigationBarColor,
193 this.systemNavigationBarDividerColor,
194 this.systemNavigationBarIconBrightness,
195 this.systemNavigationBarContrastEnforced,
196 this.statusBarColor,
197 this.statusBarBrightness,
198 this.statusBarIconBrightness,
199 this.systemStatusBarContrastEnforced,
200 });
201
202 /// The color of the system bottom navigation bar.
203 ///
204 /// Only honored in Android versions O and greater.
205 final Color? systemNavigationBarColor;
206
207 /// The color of the divider between the system's bottom navigation bar and the app's content.
208 ///
209 /// Only honored in Android versions P and greater.
210 final Color? systemNavigationBarDividerColor;
211
212 /// The brightness of the system navigation bar icons.
213 ///
214 /// Only honored in Android versions O and greater.
215 /// When set to [Brightness.light], the system navigation bar icons are light.
216 /// When set to [Brightness.dark], the system navigation bar icons are dark.
217 final Brightness? systemNavigationBarIconBrightness;
218
219 /// Overrides the contrast enforcement when setting a transparent navigation
220 /// bar.
221 ///
222 /// When setting a transparent navigation bar in SDK 29+, or Android 10 and up,
223 /// a translucent body scrim may be applied behind the button navigation bar
224 /// to ensure contrast with buttons and the background of the application.
225 ///
226 /// SDK 28-, or Android P and lower, will not apply this body scrim.
227 ///
228 /// Setting this to false overrides the default body scrim.
229 ///
230 /// See also:
231 ///
232 /// * [SystemUiOverlayStyle.systemNavigationBarColor], which is overridden
233 /// when transparent to enforce this contrast policy.
234 final bool? systemNavigationBarContrastEnforced;
235
236 /// The color of top status bar.
237 ///
238 /// Only honored in Android version M and greater.
239 final Color? statusBarColor;
240
241 /// The brightness of top status bar.
242 ///
243 /// Only honored in iOS.
244 final Brightness? statusBarBrightness;
245
246 /// The brightness of the top status bar icons.
247 ///
248 /// Only honored in Android version M and greater.
249 final Brightness? statusBarIconBrightness;
250
251 /// Overrides the contrast enforcement when setting a transparent status
252 /// bar.
253 ///
254 /// When setting a transparent status bar in SDK 29+, or Android 10 and up,
255 /// a translucent body scrim may be applied to ensure contrast with icons and
256 /// the background of the application.
257 ///
258 /// SDK 28-, or Android P and lower, will not apply this body scrim.
259 ///
260 /// Setting this to false overrides the default body scrim.
261 ///
262 /// See also:
263 ///
264 /// * [SystemUiOverlayStyle.statusBarColor], which is overridden
265 /// when transparent to enforce this contrast policy.
266 final bool? systemStatusBarContrastEnforced;
267
268 /// System overlays should be drawn with a light color. Intended for
269 /// applications with a dark background.
270 static const SystemUiOverlayStyle light = SystemUiOverlayStyle(
271 systemNavigationBarColor: Color(0xFF000000),
272 systemNavigationBarIconBrightness: Brightness.light,
273 statusBarIconBrightness: Brightness.light,
274 statusBarBrightness: Brightness.dark,
275 );
276
277 /// System overlays should be drawn with a dark color. Intended for
278 /// applications with a light background.
279 static const SystemUiOverlayStyle dark = SystemUiOverlayStyle(
280 systemNavigationBarColor: Color(0xFF000000),
281 systemNavigationBarIconBrightness: Brightness.light,
282 statusBarIconBrightness: Brightness.dark,
283 statusBarBrightness: Brightness.light,
284 );
285
286 /// Convert this event to a map for serialization.
287 Map<String, dynamic> _toMap() {
288 return <String, dynamic>{
289 'systemNavigationBarColor': systemNavigationBarColor?.value,
290 'systemNavigationBarDividerColor': systemNavigationBarDividerColor?.value,
291 'systemStatusBarContrastEnforced': systemStatusBarContrastEnforced,
292 'statusBarColor': statusBarColor?.value,
293 'statusBarBrightness': statusBarBrightness?.toString(),
294 'statusBarIconBrightness': statusBarIconBrightness?.toString(),
295 'systemNavigationBarIconBrightness': systemNavigationBarIconBrightness?.toString(),
296 'systemNavigationBarContrastEnforced': systemNavigationBarContrastEnforced,
297 };
298 }
299
300 @override
301 String toString() => '${objectRuntimeType(this, 'SystemUiOverlayStyle')}(${_toMap()})';
302
303 /// Creates a copy of this theme with the given fields replaced with new values.
304 SystemUiOverlayStyle copyWith({
305 Color? systemNavigationBarColor,
306 Color? systemNavigationBarDividerColor,
307 bool? systemNavigationBarContrastEnforced,
308 Color? statusBarColor,
309 Brightness? statusBarBrightness,
310 Brightness? statusBarIconBrightness,
311 bool? systemStatusBarContrastEnforced,
312 Brightness? systemNavigationBarIconBrightness,
313 }) {
314 return SystemUiOverlayStyle(
315 systemNavigationBarColor: systemNavigationBarColor ?? this.systemNavigationBarColor,
316 systemNavigationBarDividerColor: systemNavigationBarDividerColor ?? this.systemNavigationBarDividerColor,
317 systemNavigationBarContrastEnforced: systemNavigationBarContrastEnforced ?? this.systemNavigationBarContrastEnforced,
318 statusBarColor: statusBarColor ?? this.statusBarColor,
319 statusBarIconBrightness: statusBarIconBrightness ?? this.statusBarIconBrightness,
320 statusBarBrightness: statusBarBrightness ?? this.statusBarBrightness,
321 systemStatusBarContrastEnforced: systemStatusBarContrastEnforced ?? this.systemStatusBarContrastEnforced,
322 systemNavigationBarIconBrightness: systemNavigationBarIconBrightness ?? this.systemNavigationBarIconBrightness,
323 );
324 }
325
326 @override
327 int get hashCode => Object.hash(
328 systemNavigationBarColor,
329 systemNavigationBarDividerColor,
330 systemNavigationBarContrastEnforced,
331 statusBarColor,
332 statusBarBrightness,
333 statusBarIconBrightness,
334 systemStatusBarContrastEnforced,
335 systemNavigationBarIconBrightness,
336 );
337
338 @override
339 bool operator ==(Object other) {
340 if (other.runtimeType != runtimeType) {
341 return false;
342 }
343 return other is SystemUiOverlayStyle
344 && other.systemNavigationBarColor == systemNavigationBarColor
345 && other.systemNavigationBarDividerColor == systemNavigationBarDividerColor
346 && other.systemNavigationBarContrastEnforced == systemNavigationBarContrastEnforced
347 && other.statusBarColor == statusBarColor
348 && other.statusBarIconBrightness == statusBarIconBrightness
349 && other.statusBarBrightness == statusBarBrightness
350 && other.systemStatusBarContrastEnforced == systemStatusBarContrastEnforced
351 && other.systemNavigationBarIconBrightness == systemNavigationBarIconBrightness;
352 }
353}
354
355List<String> _stringify(List<dynamic> list) => <String>[
356 for (final dynamic item in list) item.toString(),
357];
358
359/// Controls specific aspects of the operating system's graphical interface and
360/// how it interacts with the application.
361abstract final class SystemChrome {
362 /// Specifies the set of orientations the application interface can
363 /// be displayed in.
364 ///
365 /// The `orientation` argument is a list of [DeviceOrientation] enum values.
366 /// The empty list causes the application to defer to the operating system
367 /// default.
368 ///
369 /// ## Limitations
370 ///
371 /// ### Android
372 ///
373 /// Android screens may choose to [letterbox](https://developer.android.com/guide/practices/enhanced-letterboxing)
374 /// applications that lock orientation, particularly on larger screens. When
375 /// letterboxing occurs on Android, the [MediaQueryData.size] reports the
376 /// letterboxed size, not the full screen size. Applications that make
377 /// decisions about whether to lock orientation based on the screen size
378 /// must use the `display` property of the current [FlutterView].
379 ///
380 /// ```dart
381 /// // A widget that locks the screen to portrait if it is less than 600
382 /// // logical pixels wide.
383 /// class MyApp extends StatefulWidget {
384 /// const MyApp({ super.key });
385 ///
386 /// @override
387 /// State<MyApp> createState() => _MyAppState();
388 /// }
389 ///
390 /// class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
391 /// ui.FlutterView? _view;
392 /// static const double kOrientationLockBreakpoint = 600;
393 ///
394 /// @override
395 /// void initState() {
396 /// super.initState();
397 /// WidgetsBinding.instance.addObserver(this);
398 /// }
399 ///
400 /// @override
401 /// void didChangeDependencies() {
402 /// super.didChangeDependencies();
403 /// _view = View.maybeOf(context);
404 /// }
405 ///
406 /// @override
407 /// void dispose() {
408 /// WidgetsBinding.instance.removeObserver(this);
409 /// _view = null;
410 /// super.dispose();
411 /// }
412 ///
413 /// @override
414 /// void didChangeMetrics() {
415 /// final ui.Display? display = _view?.display;
416 /// if (display == null) {
417 /// return;
418 /// }
419 /// if (display.size.width / display.devicePixelRatio < kOrientationLockBreakpoint) {
420 /// SystemChrome.setPreferredOrientations(<DeviceOrientation>[
421 /// DeviceOrientation.portraitUp,
422 /// ]);
423 /// } else {
424 /// SystemChrome.setPreferredOrientations(<DeviceOrientation>[]);
425 /// }
426 /// }
427 ///
428 /// @override
429 /// Widget build(BuildContext context) {
430 /// return const MaterialApp(
431 /// home: Placeholder(),
432 /// );
433 /// }
434 /// }
435 /// ```
436 ///
437 /// ### iOS
438 ///
439 /// This setting will only be respected on iPad if multitasking is disabled.
440 ///
441 /// You can decide to opt out of multitasking on iPad, then
442 /// setPreferredOrientations will work but your app will not
443 /// support Slide Over and Split View multitasking anymore.
444 ///
445 /// Should you decide to opt out of multitasking you can do this by
446 /// setting "Requires full screen" to true in the Xcode Deployment Info.
447 static Future<void> setPreferredOrientations(List<DeviceOrientation> orientations) async {
448 await SystemChannels.platform.invokeMethod<void>(
449 'SystemChrome.setPreferredOrientations',
450 _stringify(orientations),
451 );
452 }
453
454 /// Specifies the description of the current state of the application as it
455 /// pertains to the application switcher (also known as "recent tasks").
456 ///
457 /// Any part of the description that is unsupported on the current platform
458 /// will be ignored.
459 static Future<void> setApplicationSwitcherDescription(ApplicationSwitcherDescription description) async {
460 await SystemChannels.platform.invokeMethod<void>(
461 'SystemChrome.setApplicationSwitcherDescription',
462 <String, dynamic>{
463 'label': description.label,
464 'primaryColor': description.primaryColor,
465 },
466 );
467 }
468
469 /// Specifies the [SystemUiMode] to have visible when the application
470 /// is running.
471 ///
472 /// The `overlays` argument is a list of [SystemUiOverlay] enum values
473 /// denoting the overlays to show when configured with [SystemUiMode.manual].
474 ///
475 /// If a particular mode is unsupported on the platform, enabling or
476 /// disabling that mode will be ignored.
477 ///
478 /// The settings here can be overridden by the platform when System UI becomes
479 /// necessary for functionality.
480 ///
481 /// For example, on Android, when the keyboard becomes visible, it will enable the
482 /// navigation bar and status bar system UI overlays. When the keyboard is closed,
483 /// Android will not restore the previous UI visibility settings, and the UI
484 /// visibility cannot be changed until 1 second after the keyboard is closed to
485 /// prevent malware locking users from navigation buttons.
486 ///
487 /// To regain "fullscreen" after text entry, the UI overlays can be set again
488 /// after a delay of at least 1 second through [restoreSystemUIOverlays] or
489 /// calling this again. Otherwise, the original UI overlay settings will be
490 /// automatically restored only when the application loses and regains focus.
491 ///
492 /// Alternatively, a [SystemUiChangeCallback] can be provided to respond to
493 /// changes in the System UI. This will be called, for example, when in
494 /// [SystemUiMode.leanBack] and the user taps the screen to bring up the
495 /// system overlays. The callback provides a boolean to represent if the
496 /// application is currently in a fullscreen mode or not, so that the
497 /// application can respond to these changes. When `systemOverlaysAreVisible`
498 /// is true, the application is not fullscreen. See
499 /// [SystemChrome.setSystemUIChangeCallback] to respond to these changes in a
500 /// fullscreen application.
501 static Future<void> setEnabledSystemUIMode(SystemUiMode mode, { List<SystemUiOverlay>? overlays }) async {
502 if (mode != SystemUiMode.manual) {
503 await SystemChannels.platform.invokeMethod<void>(
504 'SystemChrome.setEnabledSystemUIMode',
505 mode.toString(),
506 );
507 } else {
508 assert(mode == SystemUiMode.manual && overlays != null);
509 await SystemChannels.platform.invokeMethod<void>(
510 'SystemChrome.setEnabledSystemUIOverlays',
511 _stringify(overlays!),
512 );
513 }
514 }
515
516 /// Sets the callback method for responding to changes in the system UI.
517 ///
518 /// This is relevant when using [SystemUiMode.leanBack]
519 /// and [SystemUiMode.immersive] and [SystemUiMode.immersiveSticky] on Android
520 /// platforms, where the [SystemUiOverlay]s can appear and disappear based on
521 /// user interaction.
522 ///
523 /// This will be called, for example, when in [SystemUiMode.leanBack] and the
524 /// user taps the screen to bring up the system overlays. The callback
525 /// provides a boolean to represent if the application is currently in a
526 /// fullscreen mode or not, so that the application can respond to these
527 /// changes. When `systemOverlaysAreVisible` is true, the application is not
528 /// fullscreen.
529 ///
530 /// When using [SystemUiMode.edgeToEdge], system overlays are always visible
531 /// and do not change. When manually configuring [SystemUiOverlay]s with
532 /// [SystemUiMode.manual], this callback will only be triggered when all
533 /// overlays have been disabled. This results in the same behavior as
534 /// [SystemUiMode.leanBack].
535 ///
536 static Future<void> setSystemUIChangeCallback(SystemUiChangeCallback? callback) async {
537 ServicesBinding.instance.setSystemUiChangeCallback(callback);
538 // Skip setting up the listener if there is no callback.
539 if (callback != null) {
540 await SystemChannels.platform.invokeMethod<void>(
541 'SystemChrome.setSystemUIChangeListener',
542 );
543 }
544 }
545
546 /// Restores the system overlays to the last settings provided via
547 /// [setEnabledSystemUIMode]. May be used when the platform force enables/disables
548 /// UI elements.
549 ///
550 /// For example, when the Android keyboard disables hidden status and navigation bars,
551 /// this can be called to re-disable the bars when the keyboard is closed.
552 ///
553 /// On Android, the system UI cannot be changed until 1 second after the previous
554 /// change. This is to prevent malware from permanently hiding navigation buttons.
555 static Future<void> restoreSystemUIOverlays() async {
556 await SystemChannels.platform.invokeMethod<void>(
557 'SystemChrome.restoreSystemUIOverlays',
558 );
559 }
560
561 /// Specifies the style to use for the system overlays (e.g. the status bar on
562 /// Android or iOS, the system navigation bar on Android) that are visible (if any).
563 ///
564 /// This method will schedule the embedder update to be run in a microtask.
565 /// Any subsequent calls to this method during the current event loop will
566 /// overwrite the pending value, such that only the last specified value takes
567 /// effect.
568 ///
569 /// Call this API in code whose lifecycle matches that of the desired
570 /// system UI styles. For instance, to change the system UI style on a new
571 /// page, consider calling when pushing/popping a new [PageRoute].
572 ///
573 /// The [AppBar] widget automatically sets the system overlay style based on
574 /// its [AppBar.systemOverlayStyle], so configure that instead of calling this
575 /// method directly. Likewise, do the same for [CupertinoNavigationBar] via
576 /// [CupertinoNavigationBar.backgroundColor].
577 ///
578 /// If a particular style is not supported on the platform, selecting it will
579 /// have no effect.
580 ///
581 /// {@tool sample}
582 /// The following example uses an `AppBar` to set the system status bar color and
583 /// the system navigation bar color.
584 ///
585 /// ** See code in examples/api/lib/services/system_chrome/system_chrome.set_system_u_i_overlay_style.0.dart **
586 /// {@end-tool}
587 ///
588 /// For more complex control of the system overlay styles, consider using
589 /// an [AnnotatedRegion] widget instead of calling [setSystemUIOverlayStyle]
590 /// directly. This widget places a value directly into the layer tree where
591 /// it can be hit-tested by the framework. On every frame, the framework will
592 /// hit-test and select the annotated region it finds under the status and
593 /// navigation bar and synthesize them into a single style. This can be used
594 /// to configure the system styles when an app bar is not used. When an app
595 /// bar is used, apps should not enclose the app bar in an annotated region
596 /// because one is automatically created. If an app bar is used and the app
597 /// bar is enclosed in an annotated region, the app bar overlay style supersedes
598 /// the status bar properties defined in the enclosing annotated region overlay
599 /// style and the enclosing annotated region overlay style supersedes the app bar
600 /// overlay style navigation bar properties.
601 ///
602 /// {@tool sample}
603 /// The following example uses an `AnnotatedRegion<SystemUiOverlayStyle>` to set
604 /// the system status bar color and the system navigation bar color.
605 ///
606 /// ** See code in examples/api/lib/services/system_chrome/system_chrome.set_system_u_i_overlay_style.1.dart **
607 /// {@end-tool}
608 ///
609 /// See also:
610 ///
611 /// * [AppBar.systemOverlayStyle], a convenient property for declaratively setting
612 /// the style of the system overlays.
613 /// * [AnnotatedRegion], the widget used to place a `SystemUiOverlayStyle` into
614 /// the layer tree.
615 static void setSystemUIOverlayStyle(SystemUiOverlayStyle style) {
616 if (_pendingStyle != null) {
617 // The microtask has already been queued; just update the pending value.
618 _pendingStyle = style;
619 return;
620 }
621 if (style == _latestStyle) {
622 // Trivial success: no microtask has been queued and the given style is
623 // already in effect, so no need to queue a microtask.
624 return;
625 }
626 _pendingStyle = style;
627 scheduleMicrotask(() {
628 assert(_pendingStyle != null);
629 if (_pendingStyle != _latestStyle) {
630 SystemChannels.platform.invokeMethod<void>(
631 'SystemChrome.setSystemUIOverlayStyle',
632 _pendingStyle!._toMap(),
633 );
634 _latestStyle = _pendingStyle;
635 }
636 _pendingStyle = null;
637 });
638 }
639
640 static SystemUiOverlayStyle? _pendingStyle;
641
642 /// The last style that was set using [SystemChrome.setSystemUIOverlayStyle].
643 @visibleForTesting
644 static SystemUiOverlayStyle? get latestStyle => _latestStyle;
645 static SystemUiOverlayStyle? _latestStyle;
646}
647