1 /*
<lambda>null2 * Copyright 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package androidx.compose.foundation.layout.samples
18
19 import android.os.Bundle
20 import androidx.activity.ComponentActivity
21 import androidx.activity.compose.setContent
22 import androidx.annotation.Sampled
23 import androidx.compose.foundation.background
24 import androidx.compose.foundation.layout.Box
25 import androidx.compose.foundation.layout.Column
26 import androidx.compose.foundation.layout.ExperimentalLayoutApi
27 import androidx.compose.foundation.layout.MutableWindowInsets
28 import androidx.compose.foundation.layout.PaddingValues
29 import androidx.compose.foundation.layout.WindowInsets
30 import androidx.compose.foundation.layout.asPaddingValues
31 import androidx.compose.foundation.layout.captionBarPadding
32 import androidx.compose.foundation.layout.consumeWindowInsets
33 import androidx.compose.foundation.layout.displayCutoutPadding
34 import androidx.compose.foundation.layout.exclude
35 import androidx.compose.foundation.layout.fillMaxSize
36 import androidx.compose.foundation.layout.fillMaxWidth
37 import androidx.compose.foundation.layout.ime
38 import androidx.compose.foundation.layout.imePadding
39 import androidx.compose.foundation.layout.mandatorySystemGesturesPadding
40 import androidx.compose.foundation.layout.navigationBars
41 import androidx.compose.foundation.layout.navigationBarsPadding
42 import androidx.compose.foundation.layout.onConsumedWindowInsetsChanged
43 import androidx.compose.foundation.layout.padding
44 import androidx.compose.foundation.layout.recalculateWindowInsets
45 import androidx.compose.foundation.layout.safeContent
46 import androidx.compose.foundation.layout.safeContentPadding
47 import androidx.compose.foundation.layout.safeDrawingPadding
48 import androidx.compose.foundation.layout.safeGesturesPadding
49 import androidx.compose.foundation.layout.statusBars
50 import androidx.compose.foundation.layout.statusBarsPadding
51 import androidx.compose.foundation.layout.systemBars
52 import androidx.compose.foundation.layout.systemBarsPadding
53 import androidx.compose.foundation.layout.systemGesturesPadding
54 import androidx.compose.foundation.layout.union
55 import androidx.compose.foundation.layout.waterfallPadding
56 import androidx.compose.foundation.layout.windowInsetsPadding
57 import androidx.compose.foundation.lazy.LazyColumn
58 import androidx.compose.material.Button
59 import androidx.compose.material.Text
60 import androidx.compose.runtime.Composable
61 import androidx.compose.runtime.getValue
62 import androidx.compose.runtime.mutableStateOf
63 import androidx.compose.runtime.remember
64 import androidx.compose.runtime.setValue
65 import androidx.compose.ui.Alignment
66 import androidx.compose.ui.Modifier
67 import androidx.compose.ui.graphics.Color
68 import androidx.compose.ui.platform.LocalDensity
69 import androidx.compose.ui.unit.dp
70 import androidx.core.view.WindowCompat
71
72 @Sampled
73 fun captionBarPaddingSample() {
74 class SampleActivity : ComponentActivity() {
75 override fun onCreate(savedInstanceState: Bundle?) {
76 WindowCompat.setDecorFitsSystemWindows(window, false)
77 super.onCreate(savedInstanceState)
78 setContent {
79 Box(Modifier.captionBarPadding()) {
80 // app content
81 }
82 }
83 }
84 }
85 }
86
87 @Sampled
systemBarsPaddingSamplenull88 fun systemBarsPaddingSample() {
89 class SampleActivity : ComponentActivity() {
90 override fun onCreate(savedInstanceState: Bundle?) {
91 WindowCompat.setDecorFitsSystemWindows(window, false)
92 super.onCreate(savedInstanceState)
93 setContent {
94 Box(Modifier.systemBarsPadding()) {
95 // app content
96 }
97 }
98 }
99 }
100 }
101
102 @Sampled
displayCutoutPaddingSamplenull103 fun displayCutoutPaddingSample() {
104 class SampleActivity : ComponentActivity() {
105 override fun onCreate(savedInstanceState: Bundle?) {
106 WindowCompat.setDecorFitsSystemWindows(window, false)
107 super.onCreate(savedInstanceState)
108 setContent {
109 Box(Modifier.background(Color.Blue).statusBarsPadding()) {
110 Box(Modifier.background(Color.Yellow).displayCutoutPadding()) {
111 // app content
112 }
113 }
114 }
115 }
116 }
117 }
118
119 @Sampled
statusBarsAndNavigationBarsPaddingSamplenull120 fun statusBarsAndNavigationBarsPaddingSample() {
121 class SampleActivity : ComponentActivity() {
122 override fun onCreate(savedInstanceState: Bundle?) {
123 WindowCompat.setDecorFitsSystemWindows(window, false)
124 super.onCreate(savedInstanceState)
125 setContent {
126 Box(Modifier.background(Color.Blue).statusBarsPadding()) {
127 Box(Modifier.background(Color.Green).navigationBarsPadding()) {
128 // app content
129 }
130 }
131 }
132 }
133 }
134 }
135
136 @Sampled
imePaddingSamplenull137 fun imePaddingSample() {
138 class SampleActivity : ComponentActivity() {
139 override fun onCreate(savedInstanceState: Bundle?) {
140 WindowCompat.setDecorFitsSystemWindows(window, false)
141 super.onCreate(savedInstanceState)
142 setContent {
143 Box(Modifier.background(Color.Blue).systemBarsPadding()) {
144 Box(Modifier.imePadding()) {
145 // app content
146 }
147 }
148 }
149 }
150 }
151 }
152
153 @Sampled
waterfallPaddingSamplenull154 fun waterfallPaddingSample() {
155 class SampleActivity : ComponentActivity() {
156 override fun onCreate(savedInstanceState: Bundle?) {
157 WindowCompat.setDecorFitsSystemWindows(window, false)
158 super.onCreate(savedInstanceState)
159 setContent {
160 Box(Modifier.background(Color.Blue).systemBarsPadding()) {
161 // The app content shouldn't spill over the edges. They will be green.
162 Box(Modifier.background(Color.Green).waterfallPadding()) {
163 // app content
164 }
165 }
166 }
167 }
168 }
169 }
170
171 @Sampled
systemGesturesPaddingSamplenull172 fun systemGesturesPaddingSample() {
173 class SampleActivity : ComponentActivity() {
174 override fun onCreate(savedInstanceState: Bundle?) {
175 WindowCompat.setDecorFitsSystemWindows(window, false)
176 super.onCreate(savedInstanceState)
177 setContent {
178 Box(Modifier.background(Color.Blue).systemBarsPadding()) {
179 // The app content won't interfere with the system gestures area.
180 // It will just be white.
181 Box(Modifier.background(Color.White).systemGesturesPadding()) {
182 // app content
183 }
184 }
185 }
186 }
187 }
188 }
189
190 @Sampled
mandatorySystemGesturesPaddingSamplenull191 fun mandatorySystemGesturesPaddingSample() {
192 class SampleActivity : ComponentActivity() {
193 override fun onCreate(savedInstanceState: Bundle?) {
194 WindowCompat.setDecorFitsSystemWindows(window, false)
195 super.onCreate(savedInstanceState)
196 setContent {
197 Box(Modifier.background(Color.Blue).systemBarsPadding()) {
198 // The app content won't interfere with the mandatory system gestures area.
199 // It will just be white.
200 Box(Modifier.background(Color.White).mandatorySystemGesturesPadding()) {
201 // app content
202 }
203 }
204 }
205 }
206 }
207 }
208
209 @Sampled
safeDrawingPaddingSamplenull210 fun safeDrawingPaddingSample() {
211 class SampleActivity : ComponentActivity() {
212 override fun onCreate(savedInstanceState: Bundle?) {
213 WindowCompat.setDecorFitsSystemWindows(window, false)
214 super.onCreate(savedInstanceState)
215 setContent {
216 Box(Modifier.background(Color.Black).systemBarsPadding()) {
217 // The app content won't have anything drawing over it, but all the
218 // background not in the status bars will be white.
219 Box(Modifier.background(Color.White).safeDrawingPadding()) {
220 // app content
221 }
222 }
223 }
224 }
225 }
226 }
227
228 @Sampled
safeGesturesPaddingSamplenull229 fun safeGesturesPaddingSample() {
230 class SampleActivity : ComponentActivity() {
231 override fun onCreate(savedInstanceState: Bundle?) {
232 WindowCompat.setDecorFitsSystemWindows(window, false)
233 super.onCreate(savedInstanceState)
234 setContent {
235 Box(Modifier.background(Color.Black).systemBarsPadding()) {
236 // The app content will only be drawn where there is no possible
237 // gesture confusion. The rest will be plain white
238 Box(Modifier.background(Color.White).safeGesturesPadding()) {
239 // app content
240 }
241 }
242 }
243 }
244 }
245 }
246
247 @Sampled
safeContentPaddingSamplenull248 fun safeContentPaddingSample() {
249 class SampleActivity : ComponentActivity() {
250 override fun onCreate(savedInstanceState: Bundle?) {
251 WindowCompat.setDecorFitsSystemWindows(window, false)
252 super.onCreate(savedInstanceState)
253 setContent {
254 Box(Modifier.background(Color.Black).systemBarsPadding()) {
255 // The app content will only be drawn where there is no possible
256 // gesture confusion and content will not be drawn over.
257 // The rest will be plain white
258 Box(Modifier.background(Color.White).safeContentPadding()) {
259 // app content
260 }
261 }
262 }
263 }
264 }
265 }
266
267 @Sampled
insetsPaddingSamplenull268 fun insetsPaddingSample() {
269 class SampleActivity : ComponentActivity() {
270 override fun onCreate(savedInstanceState: Bundle?) {
271 WindowCompat.setDecorFitsSystemWindows(window, false)
272 super.onCreate(savedInstanceState)
273 setContent {
274 val insets = WindowInsets.systemBars.union(WindowInsets.ime)
275 Box(Modifier.background(Color.White).fillMaxSize().windowInsetsPadding(insets)) {
276 // app content
277 }
278 }
279 }
280 }
281 }
282
283 @Sampled
consumedInsetsPaddingSamplenull284 fun consumedInsetsPaddingSample() {
285 class SampleActivity : ComponentActivity() {
286 override fun onCreate(savedInstanceState: Bundle?) {
287 WindowCompat.setDecorFitsSystemWindows(window, false)
288 super.onCreate(savedInstanceState)
289 setContent {
290 with(LocalDensity.current) {
291 val paddingValues = PaddingValues(horizontal = 20.dp)
292 Box(Modifier.padding(paddingValues).consumeWindowInsets(paddingValues)) {
293 // app content
294 }
295 }
296 }
297 }
298 }
299 }
300
301 @Sampled
insetsIntnull302 fun insetsInt() {
303 class SampleActivity : ComponentActivity() {
304 override fun onCreate(savedInstanceState: Bundle?) {
305 WindowCompat.setDecorFitsSystemWindows(window, false)
306 super.onCreate(savedInstanceState)
307 setContent {
308 // Make sure we are at least 10 pixels away from the top.
309 val insets = WindowInsets.statusBars.union(WindowInsets(top = 10))
310 Box(Modifier.windowInsetsPadding(insets)) {
311 // app content
312 }
313 }
314 }
315 }
316 }
317
318 @Sampled
insetsDpnull319 fun insetsDp() {
320 class SampleActivity : ComponentActivity() {
321 override fun onCreate(savedInstanceState: Bundle?) {
322 WindowCompat.setDecorFitsSystemWindows(window, false)
323 super.onCreate(savedInstanceState)
324 setContent {
325 // Make sure we are at least 10 DP away from the top.
326 val insets = WindowInsets.statusBars.union(WindowInsets(top = 10.dp))
327 Box(Modifier.windowInsetsPadding(insets)) {
328 // app content
329 }
330 }
331 }
332 }
333 }
334
335 @Sampled
paddingValuesSamplenull336 fun paddingValuesSample() {
337 class SampleActivity : ComponentActivity() {
338 override fun onCreate(savedInstanceState: Bundle?) {
339 WindowCompat.setDecorFitsSystemWindows(window, false)
340 super.onCreate(savedInstanceState)
341 setContent {
342 LazyColumn(contentPadding = WindowInsets.navigationBars.asPaddingValues()) {
343 // items
344 }
345 }
346 }
347 }
348 }
349
350 @Sampled
consumedInsetsSamplenull351 fun consumedInsetsSample() {
352 class SampleActivity : ComponentActivity() {
353 override fun onCreate(savedInstanceState: Bundle?) {
354 WindowCompat.setDecorFitsSystemWindows(window, false)
355 super.onCreate(savedInstanceState)
356 setContent {
357 Box(Modifier.padding(WindowInsets.navigationBars.asPaddingValues())) {
358 Box(Modifier.consumeWindowInsets(WindowInsets.navigationBars)) {
359 // app content
360 }
361 }
362 }
363 }
364 }
365 }
366
367 @OptIn(ExperimentalLayoutApi::class)
368 @Sampled
withConsumedInsetsSamplenull369 fun withConsumedInsetsSample() {
370 class SampleActivity : ComponentActivity() {
371 override fun onCreate(savedInstanceState: Bundle?) {
372 WindowCompat.setDecorFitsSystemWindows(window, false)
373 super.onCreate(savedInstanceState)
374 setContent {
375 val remainingInsets = remember { MutableWindowInsets() }
376 val safeContent = WindowInsets.safeContent
377 Box(
378 Modifier.navigationBarsPadding().onConsumedWindowInsetsChanged {
379 consumedWindowInsets ->
380 remainingInsets.insets = safeContent.exclude(consumedWindowInsets)
381 }
382 ) {
383 // padding can be used without recomposition when insets change.
384 val padding = remainingInsets.asPaddingValues()
385 Box(Modifier.padding(padding))
386 }
387 }
388 }
389 }
390 }
391
392 @Sampled
393 @Composable
recalculateWindowInsetsSamplenull394 fun recalculateWindowInsetsSample() {
395 var hasFirstItem by remember { mutableStateOf(true) }
396 var hasLastItem by remember { mutableStateOf(true) }
397 Column(Modifier.fillMaxSize()) {
398 if (hasFirstItem) {
399 Box(Modifier.weight(1f).fillMaxWidth().background(Color.Magenta))
400 }
401 Box(
402 Modifier.fillMaxWidth() // force a fixed size on the content
403 .recalculateWindowInsets()
404 .weight(1f)
405 .background(Color.Yellow)
406 .safeDrawingPadding()
407 ) {
408 Button(
409 onClick = { hasFirstItem = !hasFirstItem },
410 Modifier.align(Alignment.TopCenter)
411 ) {
412 val action = if (hasFirstItem) "Remove" else "Add"
413 Text("$action First Item")
414 }
415 Button(
416 onClick = { hasLastItem = !hasLastItem },
417 Modifier.align(Alignment.BottomCenter)
418 ) {
419 val action = if (hasLastItem) "Remove" else "Add"
420 Text("$action Last Item")
421 }
422 }
423 if (hasLastItem) {
424 Box(Modifier.weight(1f).fillMaxWidth().background(Color.Cyan))
425 }
426 }
427 }
428
429 @Sampled
430 @Composable
consumeWindowInsetsWithPaddingSamplenull431 fun consumeWindowInsetsWithPaddingSample() {
432 // The outer Box uses padding and properly compensates for it by using consumeWindowInsets()
433 Box(
434 Modifier.fillMaxSize()
435 .padding(10.dp)
436 .consumeWindowInsets(WindowInsets(10.dp, 10.dp, 10.dp, 10.dp))
437 ) {
438 Box(Modifier.fillMaxSize().safeContentPadding().background(Color.Blue))
439 }
440 }
441
442 @Sampled
443 @Composable
unconsumedWindowInsetsWithPaddingSamplenull444 fun unconsumedWindowInsetsWithPaddingSample() {
445 // This outer Box is representing a 3rd-party layout that you don't control. It has a
446 // padding, but doesn't properly use consumeWindowInsets()
447 Box(Modifier.padding(10.dp)) {
448 // This is the content that you control. You can make sure that the WindowInsets are correct
449 // so you can pad your content despite the fact that the parent did not
450 // consumeWindowInsets()
451 Box(
452 Modifier.fillMaxSize() // Force a fixed size on the content
453 .recalculateWindowInsets()
454 .safeContentPadding()
455 .background(Color.Blue)
456 )
457 }
458 }
459