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