1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | import 'dart:math' as math; |
6 | |
7 | import 'package:flutter/foundation.dart'; |
8 | |
9 | import 'basic.dart'; |
10 | import 'debug.dart'; |
11 | import 'framework.dart'; |
12 | import 'media_query.dart'; |
13 | |
14 | /// A widget that insets its child by sufficient padding to avoid intrusions by |
15 | /// the operating system. |
16 | /// |
17 | /// For example, this will indent the child by enough to avoid the status bar at |
18 | /// the top of the screen. |
19 | /// |
20 | /// It will also indent the child by the amount necessary to avoid The Notch on |
21 | /// the iPhone X, or other similar creative physical features of the display. |
22 | /// |
23 | /// When a [minimum] padding is specified, the greater of the minimum padding |
24 | /// or the safe area padding will be applied. |
25 | /// |
26 | /// {@youtube 560 315 https://www.youtube.com/watch?v=lkF0TQJO0bA} |
27 | /// |
28 | /// See also: |
29 | /// |
30 | /// * [SliverSafeArea], for insetting slivers to avoid operating system |
31 | /// intrusions. |
32 | /// * [Padding], for insetting widgets in general. |
33 | /// * [MediaQuery], from which the window padding is obtained. |
34 | /// * [dart:ui.FlutterView.padding], which reports the padding from the operating |
35 | /// system. |
36 | class SafeArea extends StatelessWidget { |
37 | /// Creates a widget that avoids operating system interfaces. |
38 | /// |
39 | /// The [left], [top], [right], [bottom], and [minimum] arguments must not be |
40 | /// null. |
41 | const SafeArea({ |
42 | super.key, |
43 | this.left = true, |
44 | this.top = true, |
45 | this.right = true, |
46 | this.bottom = true, |
47 | this.minimum = EdgeInsets.zero, |
48 | this.maintainBottomViewPadding = false, |
49 | required this.child, |
50 | }); |
51 | |
52 | /// Whether to avoid system intrusions on the left. |
53 | final bool left; |
54 | |
55 | /// Whether to avoid system intrusions at the top of the screen, typically the |
56 | /// system status bar. |
57 | final bool top; |
58 | |
59 | /// Whether to avoid system intrusions on the right. |
60 | final bool right; |
61 | |
62 | /// Whether to avoid system intrusions on the bottom side of the screen. |
63 | final bool bottom; |
64 | |
65 | /// This minimum padding to apply. |
66 | /// |
67 | /// The greater of the minimum insets and the media padding will be applied. |
68 | final EdgeInsets minimum; |
69 | |
70 | /// Specifies whether the [SafeArea] should maintain the bottom |
71 | /// [MediaQueryData.viewPadding] instead of the bottom [MediaQueryData.padding], |
72 | /// defaults to false. |
73 | /// |
74 | /// For example, if there is an onscreen keyboard displayed above the |
75 | /// SafeArea, the padding can be maintained below the obstruction rather than |
76 | /// being consumed. This can be helpful in cases where your layout contains |
77 | /// flexible widgets, which could visibly move when opening a software |
78 | /// keyboard due to the change in the padding value. Setting this to true will |
79 | /// avoid the UI shift. |
80 | final bool maintainBottomViewPadding; |
81 | |
82 | /// The widget below this widget in the tree. |
83 | /// |
84 | /// The padding on the [MediaQuery] for the [child] will be suitably adjusted |
85 | /// to zero out any sides that were avoided by this widget. |
86 | /// |
87 | /// {@macro flutter.widgets.ProxyWidget.child} |
88 | final Widget child; |
89 | |
90 | @override |
91 | Widget build(BuildContext context) { |
92 | assert(debugCheckHasMediaQuery(context)); |
93 | EdgeInsets padding = MediaQuery.paddingOf(context); |
94 | // Bottom padding has been consumed - i.e. by the keyboard |
95 | if (maintainBottomViewPadding) { |
96 | padding = padding.copyWith(bottom: MediaQuery.viewPaddingOf(context).bottom); |
97 | } |
98 | |
99 | return Padding( |
100 | padding: EdgeInsets.only( |
101 | left: math.max(left ? padding.left : 0.0, minimum.left), |
102 | top: math.max(top ? padding.top : 0.0, minimum.top), |
103 | right: math.max(right ? padding.right : 0.0, minimum.right), |
104 | bottom: math.max(bottom ? padding.bottom : 0.0, minimum.bottom), |
105 | ), |
106 | child: MediaQuery.removePadding( |
107 | context: context, |
108 | removeLeft: left, |
109 | removeTop: top, |
110 | removeRight: right, |
111 | removeBottom: bottom, |
112 | child: child, |
113 | ), |
114 | ); |
115 | } |
116 | |
117 | @override |
118 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
119 | super.debugFillProperties(properties); |
120 | properties.add(FlagProperty('left' , value: left, ifTrue: 'avoid left padding' )); |
121 | properties.add(FlagProperty('top' , value: top, ifTrue: 'avoid top padding' )); |
122 | properties.add(FlagProperty('right' , value: right, ifTrue: 'avoid right padding' )); |
123 | properties.add(FlagProperty('bottom' , value: bottom, ifTrue: 'avoid bottom padding' )); |
124 | } |
125 | } |
126 | |
127 | /// A sliver that insets another sliver by sufficient padding to avoid |
128 | /// intrusions by the operating system. |
129 | /// |
130 | /// For example, this will indent the sliver by enough to avoid the status bar |
131 | /// at the top of the screen. |
132 | /// |
133 | /// It will also indent the sliver by the amount necessary to avoid The Notch |
134 | /// on the iPhone X, or other similar creative physical features of the |
135 | /// display. |
136 | /// |
137 | /// When a [minimum] padding is specified, the greater of the minimum padding |
138 | /// or the safe area padding will be applied. |
139 | /// |
140 | /// See also: |
141 | /// |
142 | /// * [SafeArea], for insetting box widgets to avoid operating system intrusions. |
143 | /// * [SliverPadding], for insetting slivers in general. |
144 | /// * [MediaQuery], from which the window padding is obtained. |
145 | /// * [dart:ui.FlutterView.padding], which reports the padding from the operating |
146 | /// system. |
147 | class SliverSafeArea extends StatelessWidget { |
148 | /// Creates a sliver that avoids operating system interfaces. |
149 | const SliverSafeArea({ |
150 | super.key, |
151 | this.left = true, |
152 | this.top = true, |
153 | this.right = true, |
154 | this.bottom = true, |
155 | this.minimum = EdgeInsets.zero, |
156 | required this.sliver, |
157 | }); |
158 | |
159 | /// Whether to avoid system intrusions on the left. |
160 | final bool left; |
161 | |
162 | /// Whether to avoid system intrusions at the top of the screen, typically the |
163 | /// system status bar. |
164 | final bool top; |
165 | |
166 | /// Whether to avoid system intrusions on the right. |
167 | final bool right; |
168 | |
169 | /// Whether to avoid system intrusions on the bottom side of the screen. |
170 | final bool bottom; |
171 | |
172 | /// This minimum padding to apply. |
173 | /// |
174 | /// The greater of the minimum padding and the media padding is be applied. |
175 | final EdgeInsets minimum; |
176 | |
177 | /// The sliver below this sliver in the tree. |
178 | /// |
179 | /// The padding on the [MediaQuery] for the [sliver] will be suitably adjusted |
180 | /// to zero out any sides that were avoided by this sliver. |
181 | final Widget sliver; |
182 | |
183 | @override |
184 | Widget build(BuildContext context) { |
185 | assert(debugCheckHasMediaQuery(context)); |
186 | final EdgeInsets padding = MediaQuery.paddingOf(context); |
187 | return SliverPadding( |
188 | padding: EdgeInsets.only( |
189 | left: math.max(left ? padding.left : 0.0, minimum.left), |
190 | top: math.max(top ? padding.top : 0.0, minimum.top), |
191 | right: math.max(right ? padding.right : 0.0, minimum.right), |
192 | bottom: math.max(bottom ? padding.bottom : 0.0, minimum.bottom), |
193 | ), |
194 | sliver: MediaQuery.removePadding( |
195 | context: context, |
196 | removeLeft: left, |
197 | removeTop: top, |
198 | removeRight: right, |
199 | removeBottom: bottom, |
200 | child: sliver, |
201 | ), |
202 | ); |
203 | } |
204 | |
205 | @override |
206 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
207 | super.debugFillProperties(properties); |
208 | properties.add(FlagProperty('left' , value: left, ifTrue: 'avoid left padding' )); |
209 | properties.add(FlagProperty('top' , value: top, ifTrue: 'avoid top padding' )); |
210 | properties.add(FlagProperty('right' , value: right, ifTrue: 'avoid right padding' )); |
211 | properties.add(FlagProperty('bottom' , value: bottom, ifTrue: 'avoid bottom padding' )); |
212 | } |
213 | } |
214 | |