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 | |
5 | import 'dart:async'; |
6 | import 'dart:ui'; |
7 | |
8 | import 'package:flutter/foundation.dart'; |
9 | |
10 | import 'binding.dart'; |
11 | import 'system_channels.dart'; |
12 | |
13 | export 'dart:ui' show Brightness, Color; |
14 | |
15 | export '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]. |
33 | enum 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 |
59 | class 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]. |
76 | enum 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]. |
91 | enum 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 |
189 | class 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 | |
355 | List<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. |
361 | abstract 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 | |