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