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/// @docImport 'package:flutter/services.dart';
8///
9/// @docImport 'app.dart';
10/// @docImport 'display_feature_sub_screen.dart';
11/// @docImport 'overlay.dart';
12/// @docImport 'safe_area.dart';
13/// @docImport 'system_context_menu.dart';
14/// @docImport 'view.dart';
15library;
16
17import 'dart:math' as math;
18import 'dart:ui' as ui;
19
20import 'package:flutter/foundation.dart';
21import 'package:flutter/gestures.dart';
22
23import 'basic.dart';
24import 'binding.dart';
25import 'debug.dart';
26import 'framework.dart';
27import 'inherited_model.dart';
28
29// Examples can assume:
30// late BuildContext context;
31
32/// Whether in portrait or landscape.
33enum Orientation {
34 /// Taller than wide.
35 portrait,
36
37 /// Wider than tall.
38 landscape,
39}
40
41/// Specifies a part of MediaQueryData to depend on.
42///
43/// [MediaQuery] contains a large number of related properties. Widgets frequently
44/// depend on only a few of these attributes. For example, a widget that needs to
45/// rebuild when the [MediaQueryData.textScaler] changes does not need to be
46/// notified when the [MediaQueryData.size] changes. Specifying an aspect avoids
47/// unnecessary rebuilds.
48enum _MediaQueryAspect {
49 /// Specifies the aspect corresponding to [MediaQueryData.size].
50 size,
51
52 /// Specifies the aspect corresponding to the width of [MediaQueryData.size].
53 width,
54
55 /// Specifies the aspect corresponding to the height of [MediaQueryData.size].
56 height,
57
58 /// Specifies the aspect corresponding to [MediaQueryData.orientation].
59 orientation,
60
61 /// Specifies the aspect corresponding to [MediaQueryData.devicePixelRatio].
62 devicePixelRatio,
63
64 /// Specifies the aspect corresponding to [MediaQueryData.textScaleFactor].
65 textScaleFactor,
66
67 /// Specifies the aspect corresponding to [MediaQueryData.textScaler].
68 textScaler,
69
70 /// Specifies the aspect corresponding to [MediaQueryData.platformBrightness].
71 platformBrightness,
72
73 /// Specifies the aspect corresponding to [MediaQueryData.padding].
74 padding,
75
76 /// Specifies the aspect corresponding to [MediaQueryData.viewInsets].
77 viewInsets,
78
79 /// Specifies the aspect corresponding to [MediaQueryData.systemGestureInsets].
80 systemGestureInsets,
81
82 /// Specifies the aspect corresponding to [MediaQueryData.viewPadding].
83 viewPadding,
84
85 /// Specifies the aspect corresponding to [MediaQueryData.alwaysUse24HourFormat].
86 alwaysUse24HourFormat,
87
88 /// Specifies the aspect corresponding to [MediaQueryData.accessibleNavigation].
89 accessibleNavigation,
90
91 /// Specifies the aspect corresponding to [MediaQueryData.invertColors].
92 invertColors,
93
94 /// Specifies the aspect corresponding to [MediaQueryData.highContrast].
95 highContrast,
96
97 /// Specifies the aspect corresponding to [MediaQueryData.onOffSwitchLabels].
98 onOffSwitchLabels,
99
100 /// Specifies the aspect corresponding to [MediaQueryData.disableAnimations].
101 disableAnimations,
102
103 /// Specifies the aspect corresponding to [MediaQueryData.boldText].
104 boldText,
105
106 /// Specifies the aspect corresponding to [MediaQueryData.supportsAnnounce].
107 supportsAnnounce,
108
109 /// Specifies the aspect corresponding to [MediaQueryData.navigationMode].
110 navigationMode,
111
112 /// Specifies the aspect corresponding to [MediaQueryData.gestureSettings].
113 gestureSettings,
114
115 /// Specifies the aspect corresponding to [MediaQueryData.displayFeatures].
116 displayFeatures,
117
118 /// Specifies the aspect corresponding to [MediaQueryData.supportsShowingSystemContextMenu].
119 supportsShowingSystemContextMenu,
120}
121
122/// Information about a piece of media (e.g., a window).
123///
124/// For example, the [MediaQueryData.size] property contains the width and
125/// height of the current window.
126///
127/// To obtain individual attributes in a [MediaQueryData], prefer to use the
128/// attribute-specific functions of [MediaQuery] over obtaining the entire
129/// [MediaQueryData] and accessing its members.
130/// {@macro flutter.widgets.media_query.MediaQuery.useSpecific}
131///
132/// To obtain the entire current [MediaQueryData] for a given [BuildContext],
133/// use the [MediaQuery.of] function. This can be useful if you are going to use
134/// [copyWith] to replace the [MediaQueryData] with one with an updated
135/// property.
136///
137/// ## Insets and Padding
138///
139/// ![A diagram of padding, viewInsets, and viewPadding in correlation with each
140/// other](https://flutter.github.io/assets-for-api-docs/assets/widgets/media_query.png)
141///
142/// This diagram illustrates how [padding] relates to [viewPadding] and
143/// [viewInsets], shown here in its simplest configuration, as the difference
144/// between the two. In cases when the viewInsets exceed the viewPadding, like
145/// when a software keyboard is shown below, padding goes to zero rather than a
146/// negative value. Therefore, padding is calculated by taking
147/// `max(0.0, viewPadding - viewInsets)`.
148///
149/// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/widgets/window_padding.mp4}
150///
151/// In this diagram, the black areas represent system UI that the app cannot
152/// draw over. The red area represents view padding that the application may not
153/// be able to detect gestures in and may not want to draw in. The grey area
154/// represents the system keyboard, which can cover over the bottom view padding
155/// when visible.
156///
157/// MediaQueryData includes three [EdgeInsets] values:
158/// [padding], [viewPadding], and [viewInsets]. These values reflect the
159/// configuration of the device and are used and optionally consumed by widgets
160/// that position content within these insets. The padding value defines areas
161/// that might not be completely visible, like the display "notch" on the iPhone
162/// X. The viewInsets value defines areas that aren't visible at all, typically
163/// because they're obscured by the device's keyboard. Similar to viewInsets,
164/// viewPadding does not differentiate padding in areas that may be obscured.
165/// For example, by using the viewPadding property, padding would defer to the
166/// iPhone "safe area" regardless of whether a keyboard is showing.
167///
168/// {@youtube 560 315 https://www.youtube.com/watch?v=ceCo8U0XHqw}
169///
170/// The viewInsets and viewPadding are independent values, they're
171/// measured from the edges of the MediaQuery widget's bounds. Together they
172/// inform the [padding] property. The bounds of the top level MediaQuery
173/// created by [WidgetsApp] are the same as the window that contains the app.
174///
175/// Widgets whose layouts consume space defined by [viewInsets], [viewPadding],
176/// or [padding] should enclose their children in secondary MediaQuery
177/// widgets that reduce those properties by the same amount.
178/// The [removePadding], [removeViewPadding], and [removeViewInsets] methods are
179/// useful for this.
180///
181/// See also:
182///
183/// * [Scaffold], [SafeArea], [CupertinoTabScaffold], and
184/// [CupertinoPageScaffold], all of which are informed by [padding],
185/// [viewPadding], and [viewInsets].
186@immutable
187class MediaQueryData {
188 /// Creates data for a media query with explicit values.
189 ///
190 /// In a typical application, calling this constructor directly is rarely
191 /// needed. Consider using [MediaQueryData.fromView] to create data based on a
192 /// [dart:ui.FlutterView], or [MediaQueryData.copyWith] to create a new copy
193 /// of [MediaQueryData] with updated properties from a base [MediaQueryData].
194 const MediaQueryData({
195 this.size = Size.zero,
196 this.devicePixelRatio = 1.0,
197 @Deprecated(
198 'Use textScaler instead. '
199 'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. '
200 'This feature was deprecated after v3.12.0-2.0.pre.',
201 )
202 double textScaleFactor = 1.0,
203 TextScaler textScaler = _kUnspecifiedTextScaler,
204 this.platformBrightness = Brightness.light,
205 this.padding = EdgeInsets.zero,
206 this.viewInsets = EdgeInsets.zero,
207 this.systemGestureInsets = EdgeInsets.zero,
208 this.viewPadding = EdgeInsets.zero,
209 this.alwaysUse24HourFormat = false,
210 this.accessibleNavigation = false,
211 this.invertColors = false,
212 this.highContrast = false,
213 this.onOffSwitchLabels = false,
214 this.disableAnimations = false,
215 this.boldText = false,
216 this.supportsAnnounce = false,
217 this.navigationMode = NavigationMode.traditional,
218 this.gestureSettings = const DeviceGestureSettings(touchSlop: kTouchSlop),
219 this.displayFeatures = const <ui.DisplayFeature>[],
220 this.supportsShowingSystemContextMenu = false,
221 }) : _textScaleFactor = textScaleFactor,
222 _textScaler = textScaler,
223 assert(
224 identical(textScaler, _kUnspecifiedTextScaler) || textScaleFactor == 1.0,
225 'textScaleFactor is deprecated and cannot be specified when textScaler is specified.',
226 );
227
228 /// Deprecated. Use [MediaQueryData.fromView] instead.
229 ///
230 /// This constructor was operating on a single window assumption. In
231 /// preparation for Flutter's upcoming multi-window support, it has been
232 /// deprecated.
233 @Deprecated(
234 'Use MediaQueryData.fromView instead. '
235 'This constructor was deprecated in preparation for the upcoming multi-window support. '
236 'This feature was deprecated after v3.7.0-32.0.pre.',
237 )
238 factory MediaQueryData.fromWindow(ui.FlutterView window) = MediaQueryData.fromView;
239
240 /// Creates data for a [MediaQuery] based on the given `view`.
241 ///
242 /// If provided, the `platformData` is used to fill in the platform-specific
243 /// aspects of the newly created [MediaQueryData]. If `platformData` is null,
244 /// the `view`'s [PlatformDispatcher] is consulted to construct the
245 /// platform-specific data.
246 ///
247 /// Data which is exposed directly on the [FlutterView] is considered
248 /// view-specific. Data which is only exposed via the
249 /// [FlutterView.platformDispatcher] property is considered platform-specific.
250 ///
251 /// Callers of this method should ensure that they also register for
252 /// notifications so that the [MediaQueryData] can be updated when any data
253 /// used to construct it changes. Notifications to consider are:
254 ///
255 /// * [WidgetsBindingObserver.didChangeMetrics] or
256 /// [dart:ui.PlatformDispatcher.onMetricsChanged],
257 /// * [WidgetsBindingObserver.didChangeAccessibilityFeatures] or
258 /// [dart:ui.PlatformDispatcher.onAccessibilityFeaturesChanged],
259 /// * [WidgetsBindingObserver.didChangeTextScaleFactor] or
260 /// [dart:ui.PlatformDispatcher.onTextScaleFactorChanged],
261 /// * [WidgetsBindingObserver.didChangePlatformBrightness] or
262 /// [dart:ui.PlatformDispatcher.onPlatformBrightnessChanged].
263 ///
264 /// The last three notifications are only relevant if no `platformData` is
265 /// provided. If `platformData` is provided, callers should ensure to call
266 /// this method again when it changes to keep the constructed [MediaQueryData]
267 /// updated.
268 ///
269 /// In general, [MediaQuery.of], and its associated "...Of" methods, are the
270 /// appropriate way to obtain [MediaQueryData] from a widget. This `fromView`
271 /// constructor is primarily for use in the implementation of the framework
272 /// itself.
273 ///
274 /// See also:
275 ///
276 /// * [MediaQuery.fromView], which constructs [MediaQueryData] from a provided
277 /// [FlutterView], makes it available to descendant widgets, and sets up
278 /// the appropriate notification listeners to keep the data updated.
279 MediaQueryData.fromView(ui.FlutterView view, {MediaQueryData? platformData})
280 : size = view.physicalSize / view.devicePixelRatio,
281 devicePixelRatio = view.devicePixelRatio,
282 _textScaleFactor = 1.0, // _textScaler is the source of truth.
283 _textScaler = _textScalerFromView(view, platformData),
284 platformBrightness =
285 platformData?.platformBrightness ?? view.platformDispatcher.platformBrightness,
286 padding = EdgeInsets.fromViewPadding(view.padding, view.devicePixelRatio),
287 viewPadding = EdgeInsets.fromViewPadding(view.viewPadding, view.devicePixelRatio),
288 viewInsets = EdgeInsets.fromViewPadding(view.viewInsets, view.devicePixelRatio),
289 systemGestureInsets = EdgeInsets.fromViewPadding(
290 view.systemGestureInsets,
291 view.devicePixelRatio,
292 ),
293 accessibleNavigation =
294 platformData?.accessibleNavigation ??
295 view.platformDispatcher.accessibilityFeatures.accessibleNavigation,
296 invertColors =
297 platformData?.invertColors ?? view.platformDispatcher.accessibilityFeatures.invertColors,
298 disableAnimations =
299 platformData?.disableAnimations ??
300 view.platformDispatcher.accessibilityFeatures.disableAnimations,
301 boldText = platformData?.boldText ?? view.platformDispatcher.accessibilityFeatures.boldText,
302 supportsAnnounce =
303 platformData?.supportsAnnounce ??
304 view.platformDispatcher.accessibilityFeatures.supportsAnnounce,
305 highContrast =
306 platformData?.highContrast ?? view.platformDispatcher.accessibilityFeatures.highContrast,
307 onOffSwitchLabels =
308 platformData?.onOffSwitchLabels ??
309 view.platformDispatcher.accessibilityFeatures.onOffSwitchLabels,
310 alwaysUse24HourFormat =
311 platformData?.alwaysUse24HourFormat ?? view.platformDispatcher.alwaysUse24HourFormat,
312 navigationMode = platformData?.navigationMode ?? NavigationMode.traditional,
313 gestureSettings = DeviceGestureSettings.fromView(view),
314 displayFeatures = view.displayFeatures,
315 supportsShowingSystemContextMenu =
316 platformData?.supportsShowingSystemContextMenu ??
317 view.platformDispatcher.supportsShowingSystemContextMenu;
318
319 static TextScaler _textScalerFromView(ui.FlutterView view, MediaQueryData? platformData) {
320 return platformData?.textScaler ?? SystemTextScaler._(view.platformDispatcher);
321 }
322
323 /// The size of the media in logical pixels (e.g, the size of the screen).
324 ///
325 /// Logical pixels are roughly the same visual size across devices. Physical
326 /// pixels are the size of the actual hardware pixels on the device. The
327 /// number of physical pixels per logical pixel is described by the
328 /// [devicePixelRatio].
329 ///
330 /// Prefer using [MediaQuery.sizeOf] over [MediaQuery.of]`.size` to get the
331 /// size, since the former will only notify of changes in [size], while the
332 /// latter will notify for all [MediaQueryData] changes.
333 ///
334 /// For widgets drawn in an [Overlay], do not assume that the size of the
335 /// [Overlay] is the size of the [MediaQuery]'s size. Nested overlays can have
336 /// different sizes.
337 ///
338 /// ## Troubleshooting
339 ///
340 /// It is considered bad practice to cache and later use the size returned by
341 /// `MediaQuery.sizeOf(context)`. It will make the application non-responsive
342 /// and might lead to unexpected behaviors.
343 ///
344 /// For instance, during startup, especially in release mode, the first
345 /// returned size might be [Size.zero]. The size will be updated when the
346 /// native platform reports the actual resolution. Using [MediaQuery.sizeOf]
347 /// will ensure that when the size changes, any widgets depending on the size
348 /// are automatically rebuilt.
349 ///
350 /// See the article on [Creating responsive and adaptive
351 /// apps](https://docs.flutter.dev/ui/adaptive-responsive)
352 /// for an introduction.
353 ///
354 /// See also:
355 ///
356 /// * [FlutterView.physicalSize], which returns the size of the view in physical pixels.
357 /// * [FlutterView.display], which returns reports display information like size, and refresh rate.
358 /// * [MediaQuery.sizeOf], a method to find and depend on the size defined for
359 /// a [BuildContext].
360 final Size size;
361
362 /// The number of device pixels for each logical pixel. This number might not
363 /// be a power of two. Indeed, it might not even be an integer. For example,
364 /// the Nexus 6 has a device pixel ratio of 3.5.
365 final double devicePixelRatio;
366
367 /// Deprecated. Will be removed in a future version of Flutter. Use
368 /// [textScaler] instead.
369 ///
370 /// The number of font pixels for each logical pixel.
371 ///
372 /// For example, if the text scale factor is 1.5, text will be 50% larger than
373 /// the specified font size.
374 ///
375 /// See also:
376 ///
377 /// * [MediaQuery.textScaleFactorOf], a method to find and depend on the
378 /// textScaleFactor defined for a [BuildContext].
379 @Deprecated(
380 'Use textScaler instead. '
381 'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. '
382 'This feature was deprecated after v3.12.0-2.0.pre.',
383 )
384 double get textScaleFactor => textScaler.textScaleFactor;
385 // TODO(LongCatIsLooong): remove this after textScaleFactor is removed. To
386 // maintain backward compatibility and also keep the const constructor this
387 // has to be kept as a private field.
388 // https://github.com/flutter/flutter/issues/128825
389 final double _textScaleFactor;
390
391 /// The font scaling strategy to use for laying out textual contents.
392 ///
393 /// If this [MediaQueryData] is created by the [MediaQueryData.fromView]
394 /// constructor, this property reflects the platform's preferred text scaling
395 /// strategy, and may change as the user changes the scaling factor in the
396 /// operating system's accessibility settings.
397 ///
398 /// See also:
399 ///
400 /// * [MediaQuery.textScalerOf], a method to find and depend on the
401 /// [textScaler] defined for a [BuildContext].
402 /// * [TextPainter], a class that lays out and paints text.
403 TextScaler get textScaler {
404 // The constructor was called with an explicitly specified textScaler value,
405 // we assume the caller is migrated and ignore _textScaleFactor.
406 if (!identical(_kUnspecifiedTextScaler, _textScaler)) {
407 return _textScaler;
408 }
409 return _textScaleFactor == 1.0
410 // textScaleFactor and textScaler from the constructor are consistent.
411 ? TextScaler.noScaling
412 // The constructor was called with an explicitly specified textScaleFactor,
413 // we assume the caller is unmigrated and ignore _textScaler.
414 : TextScaler.linear(_textScaleFactor);
415 }
416
417 final TextScaler _textScaler;
418
419 /// The current brightness mode of the host platform.
420 ///
421 /// For example, starting in Android Pie, battery saver mode asks all apps to
422 /// render in a "dark mode".
423 ///
424 /// Not all platforms necessarily support a concept of brightness mode. Those
425 /// platforms will report [Brightness.light] in this property.
426 ///
427 /// See also:
428 ///
429 /// * [MediaQuery.platformBrightnessOf], a method to find and depend on the
430 /// platformBrightness defined for a [BuildContext].
431 final Brightness platformBrightness;
432
433 /// The parts of the display that are completely obscured by system UI,
434 /// typically by the device's keyboard.
435 ///
436 /// When a mobile device's keyboard is visible `viewInsets.bottom`
437 /// corresponds to the top of the keyboard.
438 ///
439 /// This value is independent of the [padding] and [viewPadding]. viewPadding
440 /// is measured from the edges of the [MediaQuery] widget's bounds. Padding is
441 /// calculated based on the viewPadding and viewInsets. The bounds of the top
442 /// level MediaQuery created by [WidgetsApp] are the same as the window
443 /// (often the mobile device screen) that contains the app.
444 ///
445 /// {@youtube 560 315 https://www.youtube.com/watch?v=ceCo8U0XHqw}
446 ///
447 /// See also:
448 ///
449 /// * [FlutterView], which provides some additional detail about this property
450 /// and how it relates to [padding] and [viewPadding].
451 final EdgeInsets viewInsets;
452
453 /// The parts of the display that are partially obscured by system UI,
454 /// typically by the hardware display "notches" or the system status bar.
455 ///
456 /// If you consumed this padding (e.g. by building a widget that envelops or
457 /// accounts for this padding in its layout in such a way that children are
458 /// no longer exposed to this padding), you should remove this padding
459 /// for subsequent descendants in the widget tree by inserting a new
460 /// [MediaQuery] widget using the [MediaQuery.removePadding] factory.
461 ///
462 /// Padding is derived from the values of [viewInsets] and [viewPadding].
463 ///
464 /// {@youtube 560 315 https://www.youtube.com/watch?v=ceCo8U0XHqw}
465 ///
466 /// See also:
467 ///
468 /// * [FlutterView], which provides some additional detail about this
469 /// property and how it relates to [viewInsets] and [viewPadding].
470 /// * [SafeArea], a widget that consumes this padding with a [Padding] widget
471 /// and automatically removes it from the [MediaQuery] for its child.
472 final EdgeInsets padding;
473
474 /// The parts of the display that are partially obscured by system UI,
475 /// typically by the hardware display "notches" or the system status bar.
476 ///
477 /// This value remains the same regardless of whether the system is reporting
478 /// other obstructions in the same physical area of the screen. For example, a
479 /// software keyboard on the bottom of the screen that may cover and consume
480 /// the same area that requires bottom padding will not affect this value.
481 ///
482 /// This value is independent of the [padding] and [viewInsets]: their values
483 /// are measured from the edges of the [MediaQuery] widget's bounds. The
484 /// bounds of the top level MediaQuery created by [WidgetsApp] are the
485 /// same as the window that contains the app. On mobile devices, this will
486 /// typically be the full screen.
487 ///
488 /// {@youtube 560 315 https://www.youtube.com/watch?v=ceCo8U0XHqw}
489 ///
490 /// See also:
491 ///
492 /// * [FlutterView], which provides some additional detail about this
493 /// property and how it relates to [padding] and [viewInsets].
494 final EdgeInsets viewPadding;
495
496 /// The areas along the edges of the display where the system consumes
497 /// certain input events and blocks delivery of those events to the app.
498 ///
499 /// Starting with Android Q, simple swipe gestures that start within the
500 /// [systemGestureInsets] areas are used by the system for page navigation
501 /// and may not be delivered to the app. Taps and swipe gestures that begin
502 /// with a long-press are delivered to the app, but simple press-drag-release
503 /// swipe gestures which begin within the area defined by [systemGestureInsets]
504 /// may not be.
505 ///
506 /// Apps should avoid locating gesture detectors within the system gesture
507 /// insets area. Apps should feel free to put visual elements within
508 /// this area.
509 ///
510 /// This property is currently only expected to be set to a non-default value
511 /// on Android starting with version Q.
512 ///
513 /// {@tool dartpad}
514 /// For apps that might be deployed on Android Q devices with full gesture
515 /// navigation enabled, use [systemGestureInsets] with [Padding]
516 /// to avoid having the left and right edges of the [Slider] from appearing
517 /// within the area reserved for system gesture navigation.
518 ///
519 /// By default, [Slider]s expand to fill the available width. So, we pad the
520 /// left and right sides.
521 ///
522 /// ** See code in examples/api/lib/widgets/media_query/media_query_data.system_gesture_insets.0.dart **
523 /// {@end-tool}
524 final EdgeInsets systemGestureInsets;
525
526 /// Whether to use 24-hour format when formatting time.
527 ///
528 /// The behavior of this flag is different across platforms:
529 ///
530 /// - On Android this flag is reported directly from the user settings called
531 /// "Use 24-hour format". It applies to any locale used by the application,
532 /// whether it is the system-wide locale, or the custom locale set by the
533 /// application.
534 /// - On iOS this flag is set to true when the user setting called "24-Hour
535 /// Time" is set or the system-wide locale's default uses 24-hour
536 /// formatting.
537 final bool alwaysUse24HourFormat;
538
539 /// Whether the user is using an accessibility service like TalkBack or
540 /// VoiceOver to interact with the application.
541 ///
542 /// When this setting is true, features such as timeouts should be disabled or
543 /// have minimum durations increased.
544 ///
545 /// See also:
546 ///
547 /// * [dart:ui.PlatformDispatcher.accessibilityFeatures], where the setting originates.
548 final bool accessibleNavigation;
549
550 /// Whether the device is inverting the colors of the platform.
551 ///
552 /// This flag is currently only updated on iOS devices.
553 ///
554 /// See also:
555 ///
556 /// * [dart:ui.PlatformDispatcher.accessibilityFeatures], where the setting
557 /// originates.
558 final bool invertColors;
559
560 /// Whether the user requested a high contrast between foreground and background
561 /// content on iOS, via Settings -> Accessibility -> Increase Contrast.
562 ///
563 /// This flag is currently only updated on iOS devices that are running iOS 13
564 /// or above.
565 final bool highContrast;
566
567 /// Whether the user requested to show on/off labels inside switches on iOS,
568 /// via Settings -> Accessibility -> Display & Text Size -> On/Off Labels.
569 ///
570 /// See also:
571 ///
572 /// * [dart:ui.PlatformDispatcher.accessibilityFeatures], where the setting
573 /// originates.
574 final bool onOffSwitchLabels;
575
576 /// Whether the platform is requesting that animations be disabled or reduced
577 /// as much as possible.
578 ///
579 /// See also:
580 ///
581 /// * [dart:ui.PlatformDispatcher.accessibilityFeatures], where the setting
582 /// originates.
583 final bool disableAnimations;
584
585 /// Whether the platform is requesting that text be drawn with a bold font
586 /// weight.
587 ///
588 /// See also:
589 ///
590 /// * [dart:ui.PlatformDispatcher.accessibilityFeatures], where the setting
591 /// originates.
592 final bool boldText;
593
594 /// Whether accessibility announcements (like [SemanticsService.announce])
595 /// are supported on the current platform.
596 ///
597 /// Returns `false` on platforms where announcements are deprecated or
598 /// unsupported by the underlying platform.
599 ///
600 /// Returns `true` on platforms where such announcements are
601 /// generally supported without discouragement. (iOS, web etc)
602 ///
603 /// See also:
604 ///
605 /// * [dart:ui.PlatformDispatcher.accessibilityFeatures], where the setting
606 /// originates.
607 final bool supportsAnnounce;
608
609 /// Describes the navigation mode requested by the platform.
610 ///
611 /// Some user interfaces are better navigated using a directional pad (DPAD)
612 /// or arrow keys, and for those interfaces, some widgets need to handle these
613 /// directional events differently. In order to know when to do that, these
614 /// widgets will look for the navigation mode in effect for their context.
615 ///
616 /// For instance, in a television interface, [NavigationMode.directional]
617 /// should be set, so that directional navigation is used to navigate away
618 /// from a text field using the DPAD. In contrast, on a regular desktop
619 /// application with the [navigationMode] set to [NavigationMode.traditional],
620 /// the arrow keys are used to move the cursor instead of navigating away.
621 ///
622 /// The [NavigationMode] values indicate the type of navigation to be used in
623 /// a widget subtree for those widgets sensitive to it.
624 final NavigationMode navigationMode;
625
626 /// The gesture settings for the view this media query is derived from.
627 ///
628 /// This contains platform specific configuration for gesture behavior,
629 /// such as touch slop. These settings should be favored for configuring
630 /// gesture behavior over the framework constants.
631 final DeviceGestureSettings gestureSettings;
632
633 /// {@macro dart.ui.ViewConfiguration.displayFeatures}
634 ///
635 /// See also:
636 ///
637 /// * [dart:ui.DisplayFeatureType], which lists the different types of
638 /// display features and explains the differences between them.
639 /// * [dart:ui.DisplayFeatureState], which lists the possible states for
640 /// folding features ([dart:ui.DisplayFeatureType.fold] and
641 /// [dart:ui.DisplayFeatureType.hinge]).
642 final List<ui.DisplayFeature> displayFeatures;
643
644 /// Whether showing the system context menu is supported.
645 ///
646 /// For example, on iOS 16.0 and above, the system text selection context menu
647 /// may be shown instead of the Flutter-drawn context menu in order to avoid
648 /// the iOS clipboard access notification when the "Paste" button is pressed.
649 ///
650 /// See also:
651 ///
652 /// * [SystemContextMenuController] and [SystemContextMenu], which may be
653 /// used to show the system context menu when this flag indicates it's
654 /// supported.
655 final bool supportsShowingSystemContextMenu;
656
657 /// The orientation of the media (e.g., whether the device is in landscape or
658 /// portrait mode).
659 Orientation get orientation {
660 return size.width > size.height ? Orientation.landscape : Orientation.portrait;
661 }
662
663 /// Creates a copy of this media query data but with the given fields replaced
664 /// with the new values.
665 ///
666 /// The `textScaler` parameter and `textScaleFactor` parameter must not be
667 /// both specified.
668 MediaQueryData copyWith({
669 Size? size,
670 double? devicePixelRatio,
671 @Deprecated(
672 'Use textScaler instead. '
673 'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. '
674 'This feature was deprecated after v3.12.0-2.0.pre.',
675 )
676 double? textScaleFactor,
677 TextScaler? textScaler,
678 Brightness? platformBrightness,
679 EdgeInsets? padding,
680 EdgeInsets? viewPadding,
681 EdgeInsets? viewInsets,
682 EdgeInsets? systemGestureInsets,
683 bool? alwaysUse24HourFormat,
684 bool? highContrast,
685 bool? onOffSwitchLabels,
686 bool? disableAnimations,
687 bool? invertColors,
688 bool? accessibleNavigation,
689 bool? boldText,
690 bool? supportsAnnounce,
691 NavigationMode? navigationMode,
692 DeviceGestureSettings? gestureSettings,
693 List<ui.DisplayFeature>? displayFeatures,
694 bool? supportsShowingSystemContextMenu,
695 }) {
696 assert(textScaleFactor == null || textScaler == null);
697 if (textScaleFactor != null) {
698 textScaler ??= TextScaler.linear(textScaleFactor);
699 }
700 return MediaQueryData(
701 size: size ?? this.size,
702 devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio,
703 textScaler: textScaler ?? this.textScaler,
704 platformBrightness: platformBrightness ?? this.platformBrightness,
705 padding: padding ?? this.padding,
706 viewPadding: viewPadding ?? this.viewPadding,
707 viewInsets: viewInsets ?? this.viewInsets,
708 systemGestureInsets: systemGestureInsets ?? this.systemGestureInsets,
709 alwaysUse24HourFormat: alwaysUse24HourFormat ?? this.alwaysUse24HourFormat,
710 invertColors: invertColors ?? this.invertColors,
711 highContrast: highContrast ?? this.highContrast,
712 onOffSwitchLabels: onOffSwitchLabels ?? this.onOffSwitchLabels,
713 disableAnimations: disableAnimations ?? this.disableAnimations,
714 accessibleNavigation: accessibleNavigation ?? this.accessibleNavigation,
715 boldText: boldText ?? this.boldText,
716 supportsAnnounce: supportsAnnounce ?? this.supportsAnnounce,
717 navigationMode: navigationMode ?? this.navigationMode,
718 gestureSettings: gestureSettings ?? this.gestureSettings,
719 displayFeatures: displayFeatures ?? this.displayFeatures,
720 supportsShowingSystemContextMenu:
721 supportsShowingSystemContextMenu ?? this.supportsShowingSystemContextMenu,
722 );
723 }
724
725 /// Creates a copy of this media query data but with the given [padding]s
726 /// replaced with zero.
727 ///
728 /// If all four of the `removeLeft`, `removeTop`, `removeRight`, and
729 /// `removeBottom` arguments are false (the default), then this
730 /// [MediaQueryData] is returned unmodified.
731 ///
732 /// See also:
733 ///
734 /// * [MediaQuery.removePadding], which uses this method to remove [padding]
735 /// from the ambient [MediaQuery].
736 /// * [SafeArea], which both removes the padding from the [MediaQuery] and
737 /// adds a [Padding] widget.
738 /// * [removeViewInsets], the same thing but for [viewInsets].
739 /// * [removeViewPadding], the same thing but for [viewPadding].
740 MediaQueryData removePadding({
741 bool removeLeft = false,
742 bool removeTop = false,
743 bool removeRight = false,
744 bool removeBottom = false,
745 }) {
746 if (!(removeLeft || removeTop || removeRight || removeBottom)) {
747 return this;
748 }
749 return copyWith(
750 padding: padding.copyWith(
751 left: removeLeft ? 0.0 : null,
752 top: removeTop ? 0.0 : null,
753 right: removeRight ? 0.0 : null,
754 bottom: removeBottom ? 0.0 : null,
755 ),
756 viewPadding: viewPadding.copyWith(
757 left: removeLeft ? math.max(0.0, viewPadding.left - padding.left) : null,
758 top: removeTop ? math.max(0.0, viewPadding.top - padding.top) : null,
759 right: removeRight ? math.max(0.0, viewPadding.right - padding.right) : null,
760 bottom: removeBottom ? math.max(0.0, viewPadding.bottom - padding.bottom) : null,
761 ),
762 );
763 }
764
765 /// Creates a copy of this media query data but with the given [viewInsets]
766 /// replaced with zero.
767 ///
768 /// If all four of the `removeLeft`, `removeTop`, `removeRight`, and
769 /// `removeBottom` arguments are false (the default), then this
770 /// [MediaQueryData] is returned unmodified.
771 ///
772 /// See also:
773 ///
774 /// * [MediaQuery.removeViewInsets], which uses this method to remove
775 /// [viewInsets] from the ambient [MediaQuery].
776 /// * [removePadding], the same thing but for [padding].
777 /// * [removeViewPadding], the same thing but for [viewPadding].
778 MediaQueryData removeViewInsets({
779 bool removeLeft = false,
780 bool removeTop = false,
781 bool removeRight = false,
782 bool removeBottom = false,
783 }) {
784 if (!(removeLeft || removeTop || removeRight || removeBottom)) {
785 return this;
786 }
787 return copyWith(
788 viewPadding: viewPadding.copyWith(
789 left: removeLeft ? math.max(0.0, viewPadding.left - viewInsets.left) : null,
790 top: removeTop ? math.max(0.0, viewPadding.top - viewInsets.top) : null,
791 right: removeRight ? math.max(0.0, viewPadding.right - viewInsets.right) : null,
792 bottom: removeBottom ? math.max(0.0, viewPadding.bottom - viewInsets.bottom) : null,
793 ),
794 viewInsets: viewInsets.copyWith(
795 left: removeLeft ? 0.0 : null,
796 top: removeTop ? 0.0 : null,
797 right: removeRight ? 0.0 : null,
798 bottom: removeBottom ? 0.0 : null,
799 ),
800 );
801 }
802
803 /// Creates a copy of this media query data but with the given [viewPadding]
804 /// replaced with zero.
805 ///
806 /// If all four of the `removeLeft`, `removeTop`, `removeRight`, and
807 /// `removeBottom` arguments are false (the default), then this
808 /// [MediaQueryData] is returned unmodified.
809 ///
810 /// See also:
811 ///
812 /// * [MediaQuery.removeViewPadding], which uses this method to remove
813 /// [viewPadding] from the ambient [MediaQuery].
814 /// * [removePadding], the same thing but for [padding].
815 /// * [removeViewInsets], the same thing but for [viewInsets].
816 MediaQueryData removeViewPadding({
817 bool removeLeft = false,
818 bool removeTop = false,
819 bool removeRight = false,
820 bool removeBottom = false,
821 }) {
822 if (!(removeLeft || removeTop || removeRight || removeBottom)) {
823 return this;
824 }
825 return copyWith(
826 padding: padding.copyWith(
827 left: removeLeft ? 0.0 : null,
828 top: removeTop ? 0.0 : null,
829 right: removeRight ? 0.0 : null,
830 bottom: removeBottom ? 0.0 : null,
831 ),
832 viewPadding: viewPadding.copyWith(
833 left: removeLeft ? 0.0 : null,
834 top: removeTop ? 0.0 : null,
835 right: removeRight ? 0.0 : null,
836 bottom: removeBottom ? 0.0 : null,
837 ),
838 );
839 }
840
841 /// Creates a copy of this media query data by removing [displayFeatures] that
842 /// are completely outside the given sub-screen and adjusting the [padding],
843 /// [viewInsets] and [viewPadding] to be zero on the sides that are not
844 /// included in the sub-screen.
845 ///
846 /// Returns unmodified [MediaQueryData] if the sub-screen coincides with the
847 /// available screen space.
848 ///
849 /// Asserts in debug mode, if the given sub-screen is outside the available
850 /// screen space.
851 ///
852 /// See also:
853 ///
854 /// * [DisplayFeatureSubScreen], which removes the display features that
855 /// split the screen, from the [MediaQuery] and adds a [Padding] widget to
856 /// position the child to match the selected sub-screen.
857 MediaQueryData removeDisplayFeatures(Rect subScreen) {
858 assert(
859 subScreen.left >= 0.0 &&
860 subScreen.top >= 0.0 &&
861 subScreen.right <= size.width &&
862 subScreen.bottom <= size.height,
863 "'subScreen' argument cannot be outside the bounds of the screen",
864 );
865 if (subScreen.size == size && subScreen.topLeft == Offset.zero) {
866 return this;
867 }
868 final double rightInset = size.width - subScreen.right;
869 final double bottomInset = size.height - subScreen.bottom;
870 return copyWith(
871 padding: EdgeInsets.only(
872 left: math.max(0.0, padding.left - subScreen.left),
873 top: math.max(0.0, padding.top - subScreen.top),
874 right: math.max(0.0, padding.right - rightInset),
875 bottom: math.max(0.0, padding.bottom - bottomInset),
876 ),
877 viewPadding: EdgeInsets.only(
878 left: math.max(0.0, viewPadding.left - subScreen.left),
879 top: math.max(0.0, viewPadding.top - subScreen.top),
880 right: math.max(0.0, viewPadding.right - rightInset),
881 bottom: math.max(0.0, viewPadding.bottom - bottomInset),
882 ),
883 viewInsets: EdgeInsets.only(
884 left: math.max(0.0, viewInsets.left - subScreen.left),
885 top: math.max(0.0, viewInsets.top - subScreen.top),
886 right: math.max(0.0, viewInsets.right - rightInset),
887 bottom: math.max(0.0, viewInsets.bottom - bottomInset),
888 ),
889 displayFeatures: displayFeatures
890 .where((ui.DisplayFeature displayFeature) => subScreen.overlaps(displayFeature.bounds))
891 .toList(),
892 );
893 }
894
895 @override
896 bool operator ==(Object other) {
897 if (other.runtimeType != runtimeType) {
898 return false;
899 }
900 return other is MediaQueryData &&
901 other.size == size &&
902 other.devicePixelRatio == devicePixelRatio &&
903 other.textScaleFactor == textScaleFactor &&
904 other.platformBrightness == platformBrightness &&
905 other.padding == padding &&
906 other.viewPadding == viewPadding &&
907 other.viewInsets == viewInsets &&
908 other.systemGestureInsets == systemGestureInsets &&
909 other.alwaysUse24HourFormat == alwaysUse24HourFormat &&
910 other.highContrast == highContrast &&
911 other.onOffSwitchLabels == onOffSwitchLabels &&
912 other.disableAnimations == disableAnimations &&
913 other.invertColors == invertColors &&
914 other.accessibleNavigation == accessibleNavigation &&
915 other.boldText == boldText &&
916 other.supportsAnnounce == supportsAnnounce &&
917 other.navigationMode == navigationMode &&
918 other.gestureSettings == gestureSettings &&
919 listEquals(other.displayFeatures, displayFeatures) &&
920 other.supportsShowingSystemContextMenu == supportsShowingSystemContextMenu;
921 }
922
923 @override
924 int get hashCode => Object.hash(
925 size,
926 devicePixelRatio,
927 textScaleFactor,
928 platformBrightness,
929 padding,
930 viewPadding,
931 viewInsets,
932 alwaysUse24HourFormat,
933 highContrast,
934 onOffSwitchLabels,
935 disableAnimations,
936 invertColors,
937 accessibleNavigation,
938 boldText,
939 navigationMode,
940 gestureSettings,
941 Object.hashAll(displayFeatures),
942 supportsShowingSystemContextMenu,
943 );
944
945 @override
946 String toString() {
947 final List<String> properties = <String>[
948 'size: $size',
949 'devicePixelRatio: ${devicePixelRatio.toStringAsFixed(1)}',
950 'textScaler: $textScaler',
951 'platformBrightness: $platformBrightness',
952 'padding: $padding',
953 'viewPadding: $viewPadding',
954 'viewInsets: $viewInsets',
955 'systemGestureInsets: $systemGestureInsets',
956 'alwaysUse24HourFormat: $alwaysUse24HourFormat',
957 'accessibleNavigation: $accessibleNavigation',
958 'highContrast: $highContrast',
959 'onOffSwitchLabels: $onOffSwitchLabels',
960 'disableAnimations: $disableAnimations',
961 'invertColors: $invertColors',
962 'boldText: $boldText',
963 'navigationMode: ${navigationMode.name}',
964 'gestureSettings: $gestureSettings',
965 'displayFeatures: $displayFeatures',
966 'supportsShowingSystemContextMenu: $supportsShowingSystemContextMenu',
967 ];
968 return '${objectRuntimeType(this, 'MediaQueryData')}(${properties.join(', ')})';
969 }
970}
971
972/// Establishes a subtree in which media queries resolve to the given data.
973///
974/// For example, to learn the size of the current view (e.g.,
975/// the [FlutterView] containing your app), you can use [MediaQuery.sizeOf]:
976/// `MediaQuery.sizeOf(context)`.
977///
978/// Querying the current media using specific methods (for example,
979/// [MediaQuery.sizeOf] or [MediaQuery.paddingOf]) will cause your widget to
980/// rebuild automatically whenever that specific property changes.
981///
982/// {@template flutter.widgets.media_query.MediaQuery.useSpecific}
983/// Querying using [MediaQuery.of] will cause your widget to rebuild
984/// automatically whenever _any_ field of the [MediaQueryData] changes (e.g., if
985/// the user rotates their device). Therefore, unless you are concerned with the
986/// entire [MediaQueryData] object changing, prefer using the specific methods
987/// (for example: [MediaQuery.sizeOf] and [MediaQuery.paddingOf]), as it will
988/// rebuild more efficiently.
989///
990/// If no [MediaQuery] is in scope then [MediaQuery.of] and the "...Of" methods
991/// similar to [MediaQuery.sizeOf] will throw an exception. Alternatively, the
992/// "maybe-" variant methods (such as [MediaQuery.maybeOf] and
993/// [MediaQuery.maybeSizeOf]) can be used, which return null, instead of
994/// throwing, when no [MediaQuery] is in scope.
995/// {@endtemplate}
996///
997/// {@youtube 560 315 https://www.youtube.com/watch?v=A3WrA4zAaPw}
998///
999/// See also:
1000///
1001/// * [WidgetsApp] and [MaterialApp], which introduce a [MediaQuery] and keep
1002/// it up to date with the current screen metrics as they change.
1003/// * [MediaQueryData], the data structure that represents the metrics.
1004class MediaQuery extends InheritedModel<_MediaQueryAspect> {
1005 /// Creates a widget that provides [MediaQueryData] to its descendants.
1006 const MediaQuery({super.key, required this.data, required super.child});
1007
1008 /// Creates a new [MediaQuery] that inherits from the ambient [MediaQuery]
1009 /// from the given context, but removes the specified padding.
1010 ///
1011 /// This should be inserted into the widget tree when the [MediaQuery] padding
1012 /// is consumed by a widget in such a way that the padding is no longer
1013 /// exposed to the widget's descendants or siblings.
1014 ///
1015 /// The [context] argument must have a [MediaQuery] in scope.
1016 ///
1017 /// If all four of the `removeLeft`, `removeTop`, `removeRight`, and
1018 /// `removeBottom` arguments are false (the default), then the returned
1019 /// [MediaQuery] reuses the ambient [MediaQueryData] unmodified, which is not
1020 /// particularly useful.
1021 ///
1022 /// See also:
1023 ///
1024 /// * [SafeArea], which both removes the padding from the [MediaQuery] and
1025 /// adds a [Padding] widget.
1026 /// * [MediaQueryData.padding], the affected property of the
1027 /// [MediaQueryData].
1028 /// * [MediaQuery.removeViewInsets], the same thing but for [MediaQueryData.viewInsets].
1029 /// * [MediaQuery.removeViewPadding], the same thing but for
1030 /// [MediaQueryData.viewPadding].
1031 MediaQuery.removePadding({
1032 super.key,
1033 required BuildContext context,
1034 bool removeLeft = false,
1035 bool removeTop = false,
1036 bool removeRight = false,
1037 bool removeBottom = false,
1038 required super.child,
1039 }) : data = MediaQuery.of(context).removePadding(
1040 removeLeft: removeLeft,
1041 removeTop: removeTop,
1042 removeRight: removeRight,
1043 removeBottom: removeBottom,
1044 );
1045
1046 /// Creates a new [MediaQuery] that inherits from the ambient [MediaQuery]
1047 /// from the given context, but removes the specified view insets.
1048 ///
1049 /// This should be inserted into the widget tree when the [MediaQuery] view
1050 /// insets are consumed by a widget in such a way that the view insets are no
1051 /// longer exposed to the widget's descendants or siblings.
1052 ///
1053 /// The [context] argument must have a [MediaQuery] in scope.
1054 ///
1055 /// If all four of the `removeLeft`, `removeTop`, `removeRight`, and
1056 /// `removeBottom` arguments are false (the default), then the returned
1057 /// [MediaQuery] reuses the ambient [MediaQueryData] unmodified, which is not
1058 /// particularly useful.
1059 ///
1060 /// See also:
1061 ///
1062 /// * [MediaQueryData.viewInsets], the affected property of the
1063 /// [MediaQueryData].
1064 /// * [MediaQuery.removePadding], the same thing but for [MediaQueryData.padding].
1065 /// * [MediaQuery.removeViewPadding], the same thing but for
1066 /// [MediaQueryData.viewPadding].
1067 MediaQuery.removeViewInsets({
1068 super.key,
1069 required BuildContext context,
1070 bool removeLeft = false,
1071 bool removeTop = false,
1072 bool removeRight = false,
1073 bool removeBottom = false,
1074 required super.child,
1075 }) : data = MediaQuery.of(context).removeViewInsets(
1076 removeLeft: removeLeft,
1077 removeTop: removeTop,
1078 removeRight: removeRight,
1079 removeBottom: removeBottom,
1080 );
1081
1082 /// Creates a new [MediaQuery] that inherits from the ambient [MediaQuery]
1083 /// from the given context, but removes the specified view padding.
1084 ///
1085 /// This should be inserted into the widget tree when the [MediaQuery] view
1086 /// padding is consumed by a widget in such a way that the view padding is no
1087 /// longer exposed to the widget's descendants or siblings.
1088 ///
1089 /// The [context] argument must have a [MediaQuery] in scope.
1090 ///
1091 /// If all four of the `removeLeft`, `removeTop`, `removeRight`, and
1092 /// `removeBottom` arguments are false (the default), then the returned
1093 /// [MediaQuery] reuses the ambient [MediaQueryData] unmodified, which is not
1094 /// particularly useful.
1095 ///
1096 /// See also:
1097 ///
1098 /// * [MediaQueryData.viewPadding], the affected property of the
1099 /// [MediaQueryData].
1100 /// * [MediaQuery.removePadding], the same thing but for [MediaQueryData.padding].
1101 /// * [MediaQuery.removeViewInsets], the same thing but for [MediaQueryData.viewInsets].
1102 MediaQuery.removeViewPadding({
1103 super.key,
1104 required BuildContext context,
1105 bool removeLeft = false,
1106 bool removeTop = false,
1107 bool removeRight = false,
1108 bool removeBottom = false,
1109 required super.child,
1110 }) : data = MediaQuery.of(context).removeViewPadding(
1111 removeLeft: removeLeft,
1112 removeTop: removeTop,
1113 removeRight: removeRight,
1114 removeBottom: removeBottom,
1115 );
1116
1117 /// Deprecated. Use [MediaQuery.fromView] instead.
1118 ///
1119 /// This constructor was operating on a single window assumption. In
1120 /// preparation for Flutter's upcoming multi-window support, it has been
1121 /// deprecated.
1122 ///
1123 /// Replaced by [MediaQuery.fromView], which requires specifying the
1124 /// [FlutterView] the [MediaQuery] is constructed for. The [FlutterView] can,
1125 /// for example, be obtained from the context via [View.of] or from
1126 /// [PlatformDispatcher.views].
1127 @Deprecated(
1128 'Use MediaQuery.fromView instead. '
1129 'This constructor was deprecated in preparation for the upcoming multi-window support. '
1130 'This feature was deprecated after v3.7.0-32.0.pre.',
1131 )
1132 static Widget fromWindow({Key? key, required Widget child}) {
1133 return _MediaQueryFromView(
1134 key: key,
1135 view: WidgetsBinding.instance.window,
1136 ignoreParentData: true,
1137 child: child,
1138 );
1139 }
1140
1141 /// Wraps the [child] in a [MediaQuery] which is built using data from the
1142 /// provided [view].
1143 ///
1144 /// The [MediaQuery] is constructed using the platform-specific data of the
1145 /// surrounding [MediaQuery] and the view-specific data of the provided
1146 /// [view]. If no surrounding [MediaQuery] exists, the platform-specific data
1147 /// is generated from the [PlatformDispatcher] associated with the provided
1148 /// [view]. Any information that's exposed via the [PlatformDispatcher] is
1149 /// considered platform-specific. Data exposed directly on the [FlutterView]
1150 /// (excluding its [FlutterView.platformDispatcher] property) is considered
1151 /// view-specific.
1152 ///
1153 /// The injected [MediaQuery] automatically updates when any of the data used
1154 /// to construct it changes.
1155 static Widget fromView({Key? key, required FlutterView view, required Widget child}) {
1156 return _MediaQueryFromView(key: key, view: view, child: child);
1157 }
1158
1159 /// Wraps the `child` in a [MediaQuery] with its [MediaQueryData.textScaler]
1160 /// set to [TextScaler.noScaling].
1161 ///
1162 /// The returned widget must be inserted in a widget tree below an existing
1163 /// [MediaQuery] widget.
1164 ///
1165 /// This can be used to prevent, for example, icon fonts from scaling as the
1166 /// user adjusts the platform's text scaling value.
1167 static Widget withNoTextScaling({Key? key, required Widget child}) {
1168 return Builder(
1169 key: key,
1170 builder: (BuildContext context) {
1171 assert(debugCheckHasMediaQuery(context));
1172 return MediaQuery(
1173 data: MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling),
1174 child: child,
1175 );
1176 },
1177 );
1178 }
1179
1180 /// Wraps the `child` in a [MediaQuery] and applies [TextScaler.clamp] on the
1181 /// current [MediaQueryData.textScaler].
1182 ///
1183 /// The returned widget must be inserted in a widget tree below an existing
1184 /// [MediaQuery] widget.
1185 ///
1186 /// This is a convenience function to restrict the range of the scaled text
1187 /// size to `[minScaleFactor * fontSize, maxScaleFactor * fontSize]` (to
1188 /// prevent excessive text scaling that would break the UI, for example). When
1189 /// `minScaleFactor` equals `maxScaleFactor`, the scaler becomes
1190 /// `TextScaler.linear(minScaleFactor)`.
1191 static Widget withClampedTextScaling({
1192 Key? key,
1193 double minScaleFactor = 0.0,
1194 double maxScaleFactor = double.infinity,
1195 required Widget child,
1196 }) {
1197 assert(maxScaleFactor >= minScaleFactor);
1198 assert(!maxScaleFactor.isNaN);
1199 assert(minScaleFactor.isFinite);
1200 assert(minScaleFactor >= 0);
1201
1202 return Builder(
1203 builder: (BuildContext context) {
1204 assert(debugCheckHasMediaQuery(context));
1205 final MediaQueryData data = MediaQuery.of(context);
1206 return MediaQuery(
1207 data: data.copyWith(
1208 textScaler: data.textScaler.clamp(
1209 minScaleFactor: minScaleFactor,
1210 maxScaleFactor: maxScaleFactor,
1211 ),
1212 ),
1213 child: child,
1214 );
1215 },
1216 );
1217 }
1218
1219 /// Contains information about the current media.
1220 ///
1221 /// For example, the [MediaQueryData.size] property contains the width and
1222 /// height of the current window.
1223 final MediaQueryData data;
1224
1225 /// The data from the closest instance of this class that encloses the given
1226 /// context.
1227 ///
1228 /// You can use this function to query the entire set of data held in the
1229 /// current [MediaQueryData] object. When any of that information changes,
1230 /// your widget will be scheduled to be rebuilt, keeping your widget
1231 /// up-to-date.
1232 ///
1233 /// Since it is typical that the widget only requires a subset of properties
1234 /// of the [MediaQueryData] object, prefer using the more specific methods
1235 /// (for example: [MediaQuery.sizeOf] and [MediaQuery.paddingOf]), as those
1236 /// methods will not cause a widget to rebuild when unrelated properties are
1237 /// updated.
1238 ///
1239 /// Typical usage is as follows:
1240 ///
1241 /// ```dart
1242 /// MediaQueryData media = MediaQuery.of(context);
1243 /// ```
1244 ///
1245 /// If there is no [MediaQuery] in scope, this method will throw a [TypeError]
1246 /// exception in release builds, and throw a descriptive [FlutterError] in
1247 /// debug builds.
1248 ///
1249 /// See also:
1250 ///
1251 /// * [maybeOf], which doesn't throw or assert if it doesn't find a
1252 /// [MediaQuery] ancestor. It returns null instead.
1253 /// * [sizeOf] and other specific methods for retrieving and depending on
1254 /// changes of a specific value.
1255 static MediaQueryData of(BuildContext context) {
1256 return _of(context);
1257 }
1258
1259 static MediaQueryData _of(BuildContext context, [_MediaQueryAspect? aspect]) {
1260 assert(debugCheckHasMediaQuery(context));
1261 return InheritedModel.inheritFrom<MediaQuery>(context, aspect: aspect)!.data;
1262 }
1263
1264 /// The data from the closest instance of this class that encloses the given
1265 /// context, if any.
1266 ///
1267 /// Use this function if you want to allow situations where no [MediaQuery] is
1268 /// in scope. Prefer using [MediaQuery.of] in situations where a media query
1269 /// is always expected to exist.
1270 ///
1271 /// If there is no [MediaQuery] in scope, then this function will return null.
1272 ///
1273 /// You can use this function to query the entire set of data held in the
1274 /// current [MediaQueryData] object. When any of that information changes,
1275 /// your widget will be scheduled to be rebuilt, keeping your widget
1276 /// up-to-date.
1277 ///
1278 /// Since it is typical that the widget only requires a subset of properties
1279 /// of the [MediaQueryData] object, prefer using the more specific methods
1280 /// (for example: [MediaQuery.maybeSizeOf] and [MediaQuery.maybePaddingOf]),
1281 /// as those methods will not cause a widget to rebuild when unrelated
1282 /// properties are updated.
1283 ///
1284 /// Typical usage is as follows:
1285 ///
1286 /// ```dart
1287 /// MediaQueryData? mediaQuery = MediaQuery.maybeOf(context);
1288 /// if (mediaQuery == null) {
1289 /// // Do something else instead.
1290 /// }
1291 /// ```
1292 ///
1293 /// See also:
1294 ///
1295 /// * [of], which will throw if it doesn't find a [MediaQuery] ancestor,
1296 /// instead of returning null.
1297 /// * [maybeSizeOf] and other specific methods for retrieving and depending on
1298 /// changes of a specific value.
1299 static MediaQueryData? maybeOf(BuildContext context) {
1300 return _maybeOf(context);
1301 }
1302
1303 static MediaQueryData? _maybeOf(BuildContext context, [_MediaQueryAspect? aspect]) {
1304 return InheritedModel.inheritFrom<MediaQuery>(context, aspect: aspect)?.data;
1305 }
1306
1307 /// Returns [MediaQueryData.size] from the nearest [MediaQuery] ancestor or
1308 /// throws an exception, if no such ancestor exists.
1309 ///
1310 /// Use of this method will cause the given [context] to rebuild any time that
1311 /// the [MediaQueryData.size] property of the ancestor [MediaQuery] changes.
1312 ///
1313 /// {@template flutter.widgets.media_query.MediaQuery.dontUseOf}
1314 /// Prefer using this function over getting the attribute directly from the
1315 /// [MediaQueryData] returned from [of], because using this function will only
1316 /// rebuild the `context` when this specific attribute changes, not when _any_
1317 /// attribute changes.
1318 /// {@endtemplate}
1319 static Size sizeOf(BuildContext context) => _of(context, _MediaQueryAspect.size).size;
1320
1321 /// Returns [MediaQueryData.size] from the nearest [MediaQuery] ancestor or
1322 /// null, if no such ancestor exists.
1323 ///
1324 /// Use of this method will cause the given [context] to rebuild any time that
1325 /// the [MediaQueryData.size] property of the ancestor [MediaQuery] changes.
1326 ///
1327 /// {@template flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1328 /// Prefer using this function over getting the attribute directly from the
1329 /// [MediaQueryData] returned from [maybeOf], because using this function will
1330 /// only rebuild the `context` when this specific attribute changes, not when
1331 /// _any_ attribute changes.
1332 /// {@endtemplate}
1333 static Size? maybeSizeOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.size)?.size;
1334
1335 /// Returns width of [MediaQueryData.size] from the nearest [MediaQuery]
1336 /// ancestor or throws an exception, if no such ancestor exists.
1337 ///
1338 /// Use of this method will cause the given [context] to rebuild any time that
1339 /// the width of [MediaQueryData.size] property of the ancestor [MediaQuery]
1340 /// changes.
1341 ///
1342 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1343 static double widthOf(BuildContext context) => _of(context, _MediaQueryAspect.width).size.width;
1344
1345 /// Returns width of [MediaQueryData.size] from the nearest [MediaQuery]
1346 /// ancestor or null, if no such ancestor exists.
1347 ///
1348 /// Use of this method will cause the given [context] to rebuild any time that
1349 /// the width of [MediaQueryData.size] property of the ancestor [MediaQuery]
1350 /// changes.
1351 ///
1352 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1353 static double? maybeWidthOf(BuildContext context) =>
1354 _maybeOf(context, _MediaQueryAspect.width)?.size.width;
1355
1356 /// Returns height of [MediaQueryData.size] from the nearest [MediaQuery]
1357 /// ancestor or throws an exception, if no such ancestor exists.
1358 ///
1359 /// Use of this method will cause the given [context] to rebuild any time that
1360 /// the height of [MediaQueryData.size] property of the ancestor [MediaQuery]
1361 /// changes.
1362 ///
1363 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1364 static double heightOf(BuildContext context) =>
1365 _of(context, _MediaQueryAspect.height).size.height;
1366
1367 /// Returns height of [MediaQueryData.size] from the nearest [MediaQuery]
1368 /// ancestor or null, if no such ancestor exists.
1369 ///
1370 /// Use of this method will cause the given [context] to rebuild any time that
1371 /// the height of [MediaQueryData.size] property of the ancestor [MediaQuery]
1372 /// changes.
1373 ///
1374 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1375 static double? maybeHeightOf(BuildContext context) =>
1376 _maybeOf(context, _MediaQueryAspect.height)?.size.height;
1377
1378 /// Returns [MediaQueryData.orientation] for the nearest [MediaQuery] ancestor or
1379 /// throws an exception, if no such ancestor exists.
1380 ///
1381 /// Use of this method will cause the given [context] to rebuild any time that
1382 /// the [MediaQueryData.orientation] property of the ancestor [MediaQuery] changes.
1383 ///
1384 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1385 static Orientation orientationOf(BuildContext context) =>
1386 _of(context, _MediaQueryAspect.orientation).orientation;
1387
1388 /// Returns [MediaQueryData.orientation] for the nearest [MediaQuery] ancestor or
1389 /// null, if no such ancestor exists.
1390 ///
1391 /// Use of this method will cause the given [context] to rebuild any time that
1392 /// the [MediaQueryData.orientation] property of the ancestor [MediaQuery] changes.
1393 ///
1394 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1395 static Orientation? maybeOrientationOf(BuildContext context) =>
1396 _maybeOf(context, _MediaQueryAspect.orientation)?.orientation;
1397
1398 /// Returns [MediaQueryData.devicePixelRatio] for the nearest [MediaQuery] ancestor or
1399 /// throws an exception, if no such ancestor exists.
1400 ///
1401 /// Use of this method will cause the given [context] to rebuild any time that
1402 /// the [MediaQueryData.devicePixelRatio] property of the ancestor [MediaQuery] changes.
1403 ///
1404 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1405 static double devicePixelRatioOf(BuildContext context) =>
1406 _of(context, _MediaQueryAspect.devicePixelRatio).devicePixelRatio;
1407
1408 /// Returns [MediaQueryData.devicePixelRatio] for the nearest [MediaQuery] ancestor or
1409 /// null, if no such ancestor exists.
1410 ///
1411 /// Use of this method will cause the given [context] to rebuild any time that
1412 /// the [MediaQueryData.devicePixelRatio] property of the ancestor [MediaQuery] changes.
1413 ///
1414 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1415 static double? maybeDevicePixelRatioOf(BuildContext context) =>
1416 _maybeOf(context, _MediaQueryAspect.devicePixelRatio)?.devicePixelRatio;
1417
1418 /// Deprecated. Will be removed in a future version of Flutter. Use
1419 /// [maybeTextScalerOf] instead.
1420 ///
1421 /// Returns [MediaQueryData.textScaleFactor] for the nearest [MediaQuery] ancestor or
1422 /// 1.0, if no such ancestor exists.
1423 ///
1424 /// Use of this method will cause the given [context] to rebuild any time that
1425 /// the [MediaQueryData.textScaleFactor] property of the ancestor [MediaQuery] changes.
1426 ///
1427 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1428 @Deprecated(
1429 'Use textScalerOf instead. '
1430 'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. '
1431 'This feature was deprecated after v3.12.0-2.0.pre.',
1432 )
1433 static double textScaleFactorOf(BuildContext context) => maybeTextScaleFactorOf(context) ?? 1.0;
1434
1435 /// Deprecated. Will be removed in a future version of Flutter. Use
1436 /// [maybeTextScalerOf] instead.
1437 ///
1438 /// Returns [MediaQueryData.textScaleFactor] for the nearest [MediaQuery] ancestor or
1439 /// null, if no such ancestor exists.
1440 ///
1441 /// Use of this method will cause the given [context] to rebuild any time that
1442 /// the [MediaQueryData.textScaleFactor] property of the ancestor [MediaQuery]
1443 /// changes.
1444 ///
1445 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1446 @Deprecated(
1447 'Use maybeTextScalerOf instead. '
1448 'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. '
1449 'This feature was deprecated after v3.12.0-2.0.pre.',
1450 )
1451 static double? maybeTextScaleFactorOf(BuildContext context) =>
1452 _maybeOf(context, _MediaQueryAspect.textScaleFactor)?.textScaleFactor;
1453
1454 /// Returns the [MediaQueryData.textScaler] for the nearest [MediaQuery]
1455 /// ancestor or [TextScaler.noScaling] if no such ancestor exists.
1456 ///
1457 /// Use of this method will cause the given [context] to rebuild any time that
1458 /// the [MediaQueryData.textScaler] property of the ancestor [MediaQuery]
1459 /// changes.
1460 ///
1461 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1462 static TextScaler textScalerOf(BuildContext context) =>
1463 maybeTextScalerOf(context) ?? TextScaler.noScaling;
1464
1465 /// Returns the [MediaQueryData.textScaler] for the nearest [MediaQuery]
1466 /// ancestor or null if no such ancestor exists.
1467 ///
1468 /// Use of this method will cause the given [context] to rebuild any time that
1469 /// the [MediaQueryData.textScaler] property of the ancestor [MediaQuery]
1470 /// changes.
1471 ///
1472 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1473 static TextScaler? maybeTextScalerOf(BuildContext context) =>
1474 _maybeOf(context, _MediaQueryAspect.textScaler)?.textScaler;
1475
1476 /// Returns [MediaQueryData.platformBrightness] for the nearest [MediaQuery]
1477 /// ancestor or [Brightness.light], if no such ancestor exists.
1478 ///
1479 /// Use of this method will cause the given [context] to rebuild any time that
1480 /// the [MediaQueryData.platformBrightness] property of the ancestor
1481 /// [MediaQuery] changes.
1482 ///
1483 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1484 static Brightness platformBrightnessOf(BuildContext context) =>
1485 maybePlatformBrightnessOf(context) ?? Brightness.light;
1486
1487 /// Returns [MediaQueryData.platformBrightness] for the nearest [MediaQuery]
1488 /// ancestor or null, if no such ancestor exists.
1489 ///
1490 /// Use of this method will cause the given [context] to rebuild any time that
1491 /// the [MediaQueryData.platformBrightness] property of the ancestor
1492 /// [MediaQuery] changes.
1493 ///
1494 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1495 static Brightness? maybePlatformBrightnessOf(BuildContext context) =>
1496 _maybeOf(context, _MediaQueryAspect.platformBrightness)?.platformBrightness;
1497
1498 /// Returns [MediaQueryData.padding] for the nearest [MediaQuery] ancestor or
1499 /// throws an exception, if no such ancestor exists.
1500 ///
1501 /// Use of this method will cause the given [context] to rebuild any time that
1502 /// the [MediaQueryData.padding] property of the ancestor [MediaQuery]
1503 /// changes.
1504 ///
1505 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1506 static EdgeInsets paddingOf(BuildContext context) =>
1507 _of(context, _MediaQueryAspect.padding).padding;
1508
1509 /// Returns [MediaQueryData.padding] for the nearest [MediaQuery] ancestor
1510 /// or null, if no such ancestor exists.
1511 ///
1512 /// Use of this method will cause the given [context] to rebuild any time that
1513 /// the [MediaQueryData.padding] property of the ancestor [MediaQuery]
1514 /// changes.
1515 ///
1516 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1517 static EdgeInsets? maybePaddingOf(BuildContext context) =>
1518 _maybeOf(context, _MediaQueryAspect.padding)?.padding;
1519
1520 /// Returns [MediaQueryData.viewInsets] for the nearest [MediaQuery] ancestor
1521 /// or throws an exception, if no such ancestor exists.
1522 ///
1523 /// Use of this method will cause the given [context] to rebuild any time that
1524 /// the [MediaQueryData.viewInsets] property of the ancestor [MediaQuery]
1525 /// changes.
1526 ///
1527 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1528 static EdgeInsets viewInsetsOf(BuildContext context) =>
1529 _of(context, _MediaQueryAspect.viewInsets).viewInsets;
1530
1531 /// Returns [MediaQueryData.viewInsets] for the nearest [MediaQuery] ancestor
1532 /// or null, if no such ancestor exists.
1533 ///
1534 /// Use of this method will cause the given [context] to rebuild any time that
1535 /// the [MediaQueryData.viewInsets] property of the ancestor [MediaQuery]
1536 /// changes.
1537 ///
1538 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1539 static EdgeInsets? maybeViewInsetsOf(BuildContext context) =>
1540 _maybeOf(context, _MediaQueryAspect.viewInsets)?.viewInsets;
1541
1542 /// Returns [MediaQueryData.systemGestureInsets] for the nearest [MediaQuery]
1543 /// ancestor or throws an exception, if no such ancestor exists.
1544 ///
1545 /// Use of this method will cause the given [context] to rebuild any time that
1546 /// the [MediaQueryData.systemGestureInsets] property of the ancestor
1547 /// [MediaQuery] changes.
1548 ///
1549 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1550 static EdgeInsets systemGestureInsetsOf(BuildContext context) =>
1551 _of(context, _MediaQueryAspect.systemGestureInsets).systemGestureInsets;
1552
1553 /// Returns [MediaQueryData.systemGestureInsets] for the nearest [MediaQuery]
1554 /// ancestor or null, if no such ancestor exists.
1555 ///
1556 /// Use of this method will cause the given [context] to rebuild any time that
1557 /// the [MediaQueryData.systemGestureInsets] property of the ancestor
1558 /// [MediaQuery] changes.
1559 ///
1560 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1561 static EdgeInsets? maybeSystemGestureInsetsOf(BuildContext context) =>
1562 _maybeOf(context, _MediaQueryAspect.systemGestureInsets)?.systemGestureInsets;
1563
1564 /// Returns [MediaQueryData.viewPadding] for the nearest [MediaQuery] ancestor
1565 /// or throws an exception, if no such ancestor exists.
1566 ///
1567 /// Use of this method will cause the given [context] to rebuild any time that
1568 /// the [MediaQueryData.viewPadding] property of the ancestor [MediaQuery]
1569 /// changes.
1570 ///
1571 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1572 static EdgeInsets viewPaddingOf(BuildContext context) =>
1573 _of(context, _MediaQueryAspect.viewPadding).viewPadding;
1574
1575 /// Returns [MediaQueryData.viewPadding] for the nearest [MediaQuery] ancestor
1576 /// or null, if no such ancestor exists.
1577 ///
1578 /// Use of this method will cause the given [context] to rebuild any time that
1579 /// the [MediaQueryData.viewPadding] property of the ancestor [MediaQuery]
1580 /// changes.
1581 ///
1582 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1583 static EdgeInsets? maybeViewPaddingOf(BuildContext context) =>
1584 _maybeOf(context, _MediaQueryAspect.viewPadding)?.viewPadding;
1585
1586 /// Returns [MediaQueryData.alwaysUse24HourFormat] for the nearest
1587 /// [MediaQuery] ancestor or throws an exception, if no such ancestor exists.
1588 ///
1589 /// Use of this method will cause the given [context] to rebuild any time that
1590 /// the [MediaQueryData.alwaysUse24HourFormat] property of the ancestor
1591 /// [MediaQuery] changes.
1592 ///
1593 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1594 static bool alwaysUse24HourFormatOf(BuildContext context) =>
1595 _of(context, _MediaQueryAspect.alwaysUse24HourFormat).alwaysUse24HourFormat;
1596
1597 /// Returns [MediaQueryData.alwaysUse24HourFormat] for the nearest
1598 /// [MediaQuery] ancestor or null, if no such ancestor exists.
1599 ///
1600 /// Use of this method will cause the given [context] to rebuild any time that
1601 /// the [MediaQueryData.alwaysUse24HourFormat] property of the ancestor
1602 /// [MediaQuery] changes.
1603 ///
1604 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1605 static bool? maybeAlwaysUse24HourFormatOf(BuildContext context) =>
1606 _maybeOf(context, _MediaQueryAspect.alwaysUse24HourFormat)?.alwaysUse24HourFormat;
1607
1608 /// Returns [MediaQueryData.accessibleNavigation] for the nearest [MediaQuery]
1609 /// ancestor or throws an exception, if no such ancestor exists.
1610 ///
1611 /// Use of this method will cause the given [context] to rebuild any time that
1612 /// the [MediaQueryData.accessibleNavigation] property of the ancestor
1613 /// [MediaQuery] changes.
1614 ///
1615 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1616 static bool accessibleNavigationOf(BuildContext context) =>
1617 _of(context, _MediaQueryAspect.accessibleNavigation).accessibleNavigation;
1618
1619 /// Returns [MediaQueryData.accessibleNavigation] for the nearest [MediaQuery]
1620 /// ancestor or null, if no such ancestor exists.
1621 ///
1622 /// Use of this method will cause the given [context] to rebuild any time that
1623 /// the [MediaQueryData.accessibleNavigation] property of the ancestor
1624 /// [MediaQuery] changes.
1625 ///
1626 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1627 static bool? maybeAccessibleNavigationOf(BuildContext context) =>
1628 _maybeOf(context, _MediaQueryAspect.accessibleNavigation)?.accessibleNavigation;
1629
1630 /// Returns [MediaQueryData.invertColors] for the nearest [MediaQuery]
1631 /// ancestor or throws an exception, if no such ancestor exists.
1632 ///
1633 /// Use of this method will cause the given [context] to rebuild any time that
1634 /// the [MediaQueryData.invertColors] property of the ancestor [MediaQuery]
1635 /// changes.
1636 ///
1637 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1638 static bool invertColorsOf(BuildContext context) =>
1639 _of(context, _MediaQueryAspect.invertColors).invertColors;
1640
1641 /// Returns [MediaQueryData.invertColors] for the nearest [MediaQuery]
1642 /// ancestor or null, if no such ancestor exists.
1643 ///
1644 /// Use of this method will cause the given [context] to rebuild any time that
1645 /// the [MediaQueryData.invertColors] property of the ancestor [MediaQuery]
1646 /// changes.
1647 ///
1648 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1649 static bool? maybeInvertColorsOf(BuildContext context) =>
1650 _maybeOf(context, _MediaQueryAspect.invertColors)?.invertColors;
1651
1652 /// Returns [MediaQueryData.highContrast] for the nearest [MediaQuery]
1653 /// ancestor or false, if no such ancestor exists.
1654 ///
1655 /// Use of this method will cause the given [context] to rebuild any time that
1656 /// the [MediaQueryData.highContrast] property of the ancestor [MediaQuery]
1657 /// changes.
1658 ///
1659 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1660 static bool highContrastOf(BuildContext context) => maybeHighContrastOf(context) ?? false;
1661
1662 /// Returns [MediaQueryData.highContrast] for the nearest [MediaQuery]
1663 /// ancestor or null, if no such ancestor exists.
1664 ///
1665 /// Use of this method will cause the given [context] to rebuild any time that
1666 /// the [MediaQueryData.highContrast] property of the ancestor [MediaQuery]
1667 /// changes.
1668 ///
1669 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1670 static bool? maybeHighContrastOf(BuildContext context) =>
1671 _maybeOf(context, _MediaQueryAspect.highContrast)?.highContrast;
1672
1673 /// Returns [MediaQueryData.onOffSwitchLabels] for the nearest [MediaQuery]
1674 /// ancestor or false, if no such ancestor exists.
1675 ///
1676 /// Use of this method will cause the given [context] to rebuild any time that
1677 /// the [MediaQueryData.onOffSwitchLabels] property of the ancestor
1678 /// [MediaQuery] changes.
1679 ///
1680 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1681 static bool onOffSwitchLabelsOf(BuildContext context) =>
1682 maybeOnOffSwitchLabelsOf(context) ?? false;
1683
1684 /// Returns [MediaQueryData.onOffSwitchLabels] for the nearest [MediaQuery]
1685 /// ancestor or null, if no such ancestor exists.
1686 ///
1687 /// Use of this method will cause the given [context] to rebuild any time that
1688 /// the [MediaQueryData.onOffSwitchLabels] property of the ancestor
1689 /// [MediaQuery] changes.
1690 ///
1691 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1692 static bool? maybeOnOffSwitchLabelsOf(BuildContext context) =>
1693 _maybeOf(context, _MediaQueryAspect.onOffSwitchLabels)?.onOffSwitchLabels;
1694
1695 /// Returns [MediaQueryData.disableAnimations] for the nearest [MediaQuery]
1696 /// ancestor or false, if no such ancestor exists.
1697 ///
1698 /// Use of this method will cause the given [context] to rebuild any time that
1699 /// the [MediaQueryData.disableAnimations] property of the ancestor
1700 /// [MediaQuery] changes.
1701 ///
1702 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1703 static bool disableAnimationsOf(BuildContext context) =>
1704 _of(context, _MediaQueryAspect.disableAnimations).disableAnimations;
1705
1706 /// Returns [MediaQueryData.disableAnimations] for the nearest [MediaQuery]
1707 /// ancestor or null, if no such ancestor exists.
1708 ///
1709 /// Use of this method will cause the given [context] to rebuild any time that
1710 /// the [MediaQueryData.disableAnimations] property of the ancestor
1711 /// [MediaQuery] changes.
1712 ///
1713 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1714 static bool? maybeDisableAnimationsOf(BuildContext context) =>
1715 _maybeOf(context, _MediaQueryAspect.disableAnimations)?.disableAnimations;
1716
1717 /// Returns the [MediaQueryData.boldText] accessibility setting for the
1718 /// nearest [MediaQuery] ancestor or false, if no such ancestor exists.
1719 ///
1720 /// Use of this method will cause the given [context] to rebuild any time that
1721 /// the [MediaQueryData.boldText] property of the ancestor [MediaQuery]
1722 /// changes.
1723 ///
1724 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1725 static bool boldTextOf(BuildContext context) => maybeBoldTextOf(context) ?? false;
1726
1727 /// Returns the [MediaQueryData.boldText] accessibility setting for the
1728 /// nearest [MediaQuery] ancestor or null, if no such ancestor exists.
1729 ///
1730 /// Use of this method will cause the given [context] to rebuild any time that
1731 /// the [MediaQueryData.boldText] property of the ancestor [MediaQuery]
1732 /// changes.
1733 ///
1734 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1735 static bool? maybeBoldTextOf(BuildContext context) =>
1736 _maybeOf(context, _MediaQueryAspect.boldText)?.boldText;
1737
1738 /// Returns the [MediaQueryData.supportsAnnounce] accessibility setting for the
1739 /// nearest [MediaQuery] ancestor or false, if no such ancestor exists.
1740 ///
1741 /// Use of this method will cause the given [context] to rebuild any time that
1742 /// the [MediaQueryData.supportsAnnounce] property of the ancestor [MediaQuery]
1743 /// changes. This is especially important for supportsAnnounce because supportsAnnounce has a
1744 /// low frequency change rate. The performance difference between rebuilding
1745 /// for all media query data changes and only rebuilding for supportsAnnounce is a
1746 /// dramatic difference.
1747 ///
1748 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1749 static bool supportsAnnounceOf(BuildContext context) => maybeSupportsAnnounceOf(context) ?? false;
1750
1751 /// Returns the [MediaQueryData.supportsAnnounce] accessibility setting for the
1752 /// nearest [MediaQuery] ancestor or null, if no such ancestor exists.
1753 ///
1754 /// Use of this method will cause the given [context] to rebuild any time that
1755 /// the [MediaQueryData.supportsAnnounce] property of the ancestor [MediaQuery]
1756 /// changes.
1757 ///
1758 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1759 static bool? maybeSupportsAnnounceOf(BuildContext context) =>
1760 _maybeOf(context, _MediaQueryAspect.supportsAnnounce)?.supportsAnnounce;
1761
1762 /// Returns [MediaQueryData.navigationMode] for the nearest [MediaQuery]
1763 /// ancestor or throws an exception, if no such ancestor exists.
1764 ///
1765 /// Use of this method will cause the given [context] to rebuild any time that
1766 /// the [MediaQueryData.navigationMode] property of the ancestor [MediaQuery]
1767 /// changes.
1768 ///
1769 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1770 static NavigationMode navigationModeOf(BuildContext context) =>
1771 _of(context, _MediaQueryAspect.navigationMode).navigationMode;
1772
1773 /// Returns [MediaQueryData.navigationMode] for the nearest [MediaQuery]
1774 /// ancestor or null, if no such ancestor exists.
1775 ///
1776 /// Use of this method will cause the given [context] to rebuild any time that
1777 /// the [MediaQueryData.navigationMode] property of the ancestor [MediaQuery]
1778 /// changes.
1779 ///
1780 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1781 static NavigationMode? maybeNavigationModeOf(BuildContext context) =>
1782 _maybeOf(context, _MediaQueryAspect.navigationMode)?.navigationMode;
1783
1784 /// Returns [MediaQueryData.gestureSettings] for the nearest [MediaQuery]
1785 /// ancestor or throws an exception, if no such ancestor exists.
1786 ///
1787 /// Use of this method will cause the given [context] to rebuild any time that
1788 /// the [MediaQueryData.gestureSettings] property of the ancestor [MediaQuery]
1789 /// changes.
1790 ///
1791 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1792 static DeviceGestureSettings gestureSettingsOf(BuildContext context) =>
1793 _of(context, _MediaQueryAspect.gestureSettings).gestureSettings;
1794
1795 /// Returns [MediaQueryData.gestureSettings] for the nearest [MediaQuery]
1796 /// ancestor or null, if no such ancestor exists.
1797 ///
1798 /// Use of this method will cause the given [context] to rebuild any time that
1799 /// the [MediaQueryData.gestureSettings] property of the ancestor [MediaQuery]
1800 /// changes.
1801 ///
1802 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1803 static DeviceGestureSettings? maybeGestureSettingsOf(BuildContext context) =>
1804 _maybeOf(context, _MediaQueryAspect.gestureSettings)?.gestureSettings;
1805
1806 /// Returns [MediaQueryData.displayFeatures] for the nearest [MediaQuery]
1807 /// ancestor or throws an exception, if no such ancestor exists.
1808 ///
1809 /// Use of this method will cause the given [context] to rebuild any time that
1810 /// the [MediaQueryData.displayFeatures] property of the ancestor [MediaQuery]
1811 /// changes.
1812 ///
1813 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1814 static List<ui.DisplayFeature> displayFeaturesOf(BuildContext context) =>
1815 _of(context, _MediaQueryAspect.displayFeatures).displayFeatures;
1816
1817 /// Returns [MediaQueryData.displayFeatures] for the nearest [MediaQuery]
1818 /// ancestor or null, if no such ancestor exists.
1819 ///
1820 /// Use of this method will cause the given [context] to rebuild any time that
1821 /// the [MediaQueryData.displayFeatures] property of the ancestor [MediaQuery]
1822 /// changes.
1823 ///
1824 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1825 static List<ui.DisplayFeature>? maybeDisplayFeaturesOf(BuildContext context) =>
1826 _maybeOf(context, _MediaQueryAspect.displayFeatures)?.displayFeatures;
1827
1828 /// Returns [MediaQueryData.supportsShowingSystemContextMenu] for the nearest
1829 /// [MediaQuery] ancestor or throws an exception, if no such ancestor exists.
1830 ///
1831 /// Use of this method will cause the given [context] to rebuild any time that
1832 /// the [MediaQueryData.supportsShowingSystemContextMenu] property of the
1833 /// ancestor [MediaQuery] changes.
1834 ///
1835 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
1836 static bool supportsShowingSystemContextMenu(BuildContext context) => _of(
1837 context,
1838 _MediaQueryAspect.supportsShowingSystemContextMenu,
1839 ).supportsShowingSystemContextMenu;
1840
1841 /// Returns [MediaQueryData.supportsShowingSystemContextMenu] for the nearest
1842 /// [MediaQuery] ancestor or null, if no such ancestor exists.
1843 ///
1844 /// Use of this method will cause the given [context] to rebuild any time that
1845 /// the [MediaQueryData.supportsShowingSystemContextMenu] property of the
1846 /// ancestor [MediaQuery] changes.
1847 ///
1848 /// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
1849 static bool? maybeSupportsShowingSystemContextMenu(BuildContext context) => _maybeOf(
1850 context,
1851 _MediaQueryAspect.supportsShowingSystemContextMenu,
1852 )?.supportsShowingSystemContextMenu;
1853
1854 @override
1855 bool updateShouldNotify(MediaQuery oldWidget) => data != oldWidget.data;
1856
1857 @override
1858 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1859 super.debugFillProperties(properties);
1860 properties.add(DiagnosticsProperty<MediaQueryData>('data', data, showName: false));
1861 }
1862
1863 @override
1864 bool updateShouldNotifyDependent(MediaQuery oldWidget, Set<Object> dependencies) {
1865 return dependencies.any(
1866 (Object dependency) =>
1867 dependency is _MediaQueryAspect &&
1868 switch (dependency) {
1869 _MediaQueryAspect.size => data.size != oldWidget.data.size,
1870 _MediaQueryAspect.width => data.size.width != oldWidget.data.size.width,
1871 _MediaQueryAspect.height => data.size.height != oldWidget.data.size.height,
1872 _MediaQueryAspect.orientation => data.orientation != oldWidget.data.orientation,
1873 _MediaQueryAspect.devicePixelRatio =>
1874 data.devicePixelRatio != oldWidget.data.devicePixelRatio,
1875 _MediaQueryAspect.textScaleFactor =>
1876 data.textScaleFactor != oldWidget.data.textScaleFactor,
1877 _MediaQueryAspect.textScaler => data.textScaler != oldWidget.data.textScaler,
1878 _MediaQueryAspect.platformBrightness =>
1879 data.platformBrightness != oldWidget.data.platformBrightness,
1880 _MediaQueryAspect.padding => data.padding != oldWidget.data.padding,
1881 _MediaQueryAspect.viewInsets => data.viewInsets != oldWidget.data.viewInsets,
1882 _MediaQueryAspect.viewPadding => data.viewPadding != oldWidget.data.viewPadding,
1883 _MediaQueryAspect.invertColors => data.invertColors != oldWidget.data.invertColors,
1884 _MediaQueryAspect.highContrast => data.highContrast != oldWidget.data.highContrast,
1885 _MediaQueryAspect.onOffSwitchLabels =>
1886 data.onOffSwitchLabels != oldWidget.data.onOffSwitchLabels,
1887 _MediaQueryAspect.disableAnimations =>
1888 data.disableAnimations != oldWidget.data.disableAnimations,
1889 _MediaQueryAspect.boldText => data.boldText != oldWidget.data.boldText,
1890 _MediaQueryAspect.supportsAnnounce =>
1891 data.supportsAnnounce != oldWidget.data.supportsAnnounce,
1892 _MediaQueryAspect.navigationMode =>
1893 data.navigationMode != oldWidget.data.navigationMode,
1894 _MediaQueryAspect.gestureSettings =>
1895 data.gestureSettings != oldWidget.data.gestureSettings,
1896 _MediaQueryAspect.displayFeatures =>
1897 data.displayFeatures != oldWidget.data.displayFeatures,
1898 _MediaQueryAspect.systemGestureInsets =>
1899 data.systemGestureInsets != oldWidget.data.systemGestureInsets,
1900 _MediaQueryAspect.accessibleNavigation =>
1901 data.accessibleNavigation != oldWidget.data.accessibleNavigation,
1902 _MediaQueryAspect.alwaysUse24HourFormat =>
1903 data.alwaysUse24HourFormat != oldWidget.data.alwaysUse24HourFormat,
1904 _MediaQueryAspect.supportsShowingSystemContextMenu =>
1905 data.supportsShowingSystemContextMenu !=
1906 oldWidget.data.supportsShowingSystemContextMenu,
1907 },
1908 );
1909 }
1910}
1911
1912/// Describes the navigation mode to be set by a [MediaQuery] widget.
1913///
1914/// The different modes indicate the type of navigation to be used in a widget
1915/// subtree for those widgets sensitive to it.
1916///
1917/// Use `MediaQuery.navigationModeOf(context)` to determine the navigation mode
1918/// in effect for the given context. Use a [MediaQuery] widget to set the
1919/// navigation mode for its descendant widgets.
1920enum NavigationMode {
1921 /// This indicates a traditional keyboard-and-mouse navigation modality.
1922 ///
1923 /// This navigation mode is where the arrow keys can be used for secondary
1924 /// modification operations, like moving sliders or cursors, and disabled
1925 /// controls will lose focus and not be traversable.
1926 traditional,
1927
1928 /// This indicates a directional-based navigation mode.
1929 ///
1930 /// This navigation mode indicates that arrow keys should be reserved for
1931 /// navigation operations, and secondary modifications operations, like moving
1932 /// sliders or cursors, will use alternative bindings or be disabled.
1933 ///
1934 /// Some behaviors are also affected by this mode. For instance, disabled
1935 /// controls will retain focus when disabled, and will be able to receive
1936 /// focus (although they remain disabled) when traversed.
1937 directional,
1938}
1939
1940class _MediaQueryFromView extends StatefulWidget {
1941 const _MediaQueryFromView({
1942 super.key,
1943 required this.view,
1944 this.ignoreParentData = false,
1945 required this.child,
1946 });
1947
1948 final FlutterView view;
1949 final bool ignoreParentData;
1950 final Widget child;
1951
1952 @override
1953 State<_MediaQueryFromView> createState() => _MediaQueryFromViewState();
1954}
1955
1956class _MediaQueryFromViewState extends State<_MediaQueryFromView> with WidgetsBindingObserver {
1957 MediaQueryData? _parentData;
1958 MediaQueryData? _data;
1959
1960 @override
1961 void initState() {
1962 super.initState();
1963 WidgetsBinding.instance.addObserver(this);
1964 }
1965
1966 @override
1967 void didChangeDependencies() {
1968 super.didChangeDependencies();
1969 _updateParentData();
1970 _updateData();
1971 assert(_data != null);
1972 }
1973
1974 @override
1975 void didUpdateWidget(_MediaQueryFromView oldWidget) {
1976 super.didUpdateWidget(oldWidget);
1977 if (widget.ignoreParentData != oldWidget.ignoreParentData) {
1978 _updateParentData();
1979 }
1980 if (_data == null || oldWidget.view != widget.view) {
1981 _updateData();
1982 }
1983 assert(_data != null);
1984 }
1985
1986 void _updateParentData() {
1987 _parentData = widget.ignoreParentData ? null : MediaQuery.maybeOf(context);
1988 _data = null; // _updateData must be called again after changing parent data.
1989 }
1990
1991 void _updateData() {
1992 final MediaQueryData newData = MediaQueryData.fromView(widget.view, platformData: _parentData);
1993 if (newData != _data) {
1994 setState(() {
1995 _data = newData;
1996 });
1997 }
1998 }
1999
2000 @override
2001 void didChangeAccessibilityFeatures() {
2002 // If we have a parent, it dictates our accessibility features. If we don't
2003 // have a parent, we get our accessibility features straight from the
2004 // PlatformDispatcher and need to update our data in response to the
2005 // PlatformDispatcher changing its accessibility features setting.
2006 if (_parentData == null) {
2007 _updateData();
2008 }
2009 }
2010
2011 @override
2012 void didChangeMetrics() {
2013 _updateData();
2014 }
2015
2016 @override
2017 void didChangeTextScaleFactor() {
2018 // If we have a parent, it dictates our text scale factor. If we don't have
2019 // a parent, we get our text scale factor from the PlatformDispatcher and
2020 // need to update our data in response to the PlatformDispatcher changing
2021 // its text scale factor setting.
2022 if (_parentData == null) {
2023 _updateData();
2024 }
2025 }
2026
2027 @override
2028 void didChangePlatformBrightness() {
2029 // If we have a parent, it dictates our platform brightness. If we don't
2030 // have a parent, we get our platform brightness from the PlatformDispatcher
2031 // and need to update our data in response to the PlatformDispatcher
2032 // changing its platform brightness setting.
2033 if (_parentData == null) {
2034 _updateData();
2035 }
2036 }
2037
2038 @override
2039 void dispose() {
2040 WidgetsBinding.instance.removeObserver(this);
2041 super.dispose();
2042 }
2043
2044 @override
2045 Widget build(BuildContext context) {
2046 MediaQueryData effectiveData = _data!;
2047 // If we get our platformBrightness from the PlatformDispatcher (i.e. we have no parentData) replace it
2048 // with the debugBrightnessOverride in non-release mode.
2049 if (!kReleaseMode &&
2050 _parentData == null &&
2051 effectiveData.platformBrightness != debugBrightnessOverride) {
2052 effectiveData = effectiveData.copyWith(platformBrightness: debugBrightnessOverride);
2053 }
2054 return MediaQuery(data: effectiveData, child: widget.child);
2055 }
2056}
2057
2058const TextScaler _kUnspecifiedTextScaler = _UnspecifiedTextScaler();
2059
2060// TODO(LongCatIsLooong): Remove once `MediaQueryData.textScaleFactor` is
2061// removed: https://github.com/flutter/flutter/issues/128825.
2062class _UnspecifiedTextScaler implements TextScaler {
2063 const _UnspecifiedTextScaler();
2064
2065 @override
2066 Never clamp({double minScaleFactor = 0, double maxScaleFactor = double.infinity}) =>
2067 throw UnimplementedError();
2068 @override
2069 Never scale(double fontSize) => throw UnimplementedError();
2070 @override
2071 Never get textScaleFactor => throw UnimplementedError();
2072}
2073
2074/// A [TextScaler] that reflects the user's font scale preferences from the
2075/// platform's accessibility settings.
2076final class SystemTextScaler extends TextScaler {
2077 SystemTextScaler._(this._platformDispatcher)
2078 : textScaleFactor = _platformDispatcher.textScaleFactor;
2079
2080 final ui.PlatformDispatcher _platformDispatcher;
2081 @override
2082 double scale(double fontSize) => _platformDispatcher.scaleFontSize(fontSize);
2083
2084 /// A value that represents the current user preference for the scaling factor
2085 /// for fonts.
2086 ///
2087 /// This numeric value is typically used to compare [SystemTextScaler]s. Two
2088 /// [SystemTextScaler] instances with the same [textScaleFactor] are considered
2089 /// equal as their [scale] methods produce the same output when given the same
2090 /// input font size. However, [textScaleFactor] should not be used in arithmetic
2091 /// operations.
2092 // TODO(LongCatIsLooong): consider changing the type to Comparable
2093 // once `MediaQueryData.textScaleFactor` is removed:
2094 // https://github.com/flutter/flutter/issues/128825.
2095 @override
2096 final double textScaleFactor;
2097
2098 @override
2099 bool operator ==(Object other) {
2100 if (identical(this, other)) {
2101 return true;
2102 }
2103 return switch (other) {
2104 // The system's text scale factor is used for the equality check because the
2105 // `scale` function's output monotonically increases with the text scale factor.
2106 SystemTextScaler(:final double textScaleFactor) => this.textScaleFactor == textScaleFactor,
2107 // When textScaleFactor is 1.0, the two TextScalers are extensionally
2108 // equivalent.
2109 TextScaler.noScaling => textScaleFactor == 1.0,
2110 _ => false,
2111 };
2112 }
2113
2114 @override
2115 int get hashCode => textScaleFactor.hashCode;
2116
2117 @override
2118 String toString() =>
2119 'SystemTextScaler (${textScaleFactor == 1.0 ? "no scaling" : "${textScaleFactor}x"})';
2120}
2121