Sheet — to komponent UI, który pojawia się w dolnej części ekranu nad główną treścią i udostępnia dodatkowe akcje lub informacje. Bottom Sheet to najczęściej spotykany typ, zdefiniowany w wytycznych Material Design 3 (Google, 2024). W przeciwieństwie do okien dialogowych, Sheet nie blokuje całkowicie interakcji z tłem i obsługuje stany collapsed i expanded. Zrozumienie typów i zachowania Sheet jest niezbędne do budowania wygodnej nawigacji w aplikacjach mobilnych.
Najważniejsze
Sheet — to powierzchniowy komponent UI, który wysuwa się z dołu ekranu i wyświetla dodatkową treść lub akcje, nie przenosząc użytkownika z bieżącego ekranu. W przeciwieństwie do przejść pełnoekranowych, Sheet zachowuje kontekst: użytkownik widzi ekran tła i rozumie, skąd pojawił się Sheet.
Architektura Sheet opiera się na kontenerze o zmiennej wysokości. W minimalnym stanie (collapsed) widoczna jest tylko część treści — zazwyczaj nagłówek lub mały panel. Użytkownik może przeciągnąć Sheet w górę, aby go rozwinąć (expanded), lub przesunąć w dół, aby go zamknąć. Takie zachowanie przypomina fizyczną kartkę papieru wysuwaną spod stosu — stąd nazwa.
Google w wytycznych Material Design 3 (2024) wyróżnia dwa kluczowe scenariusze użycia Sheet: wyświetlanie dodatkowych akcji, które nie zmieściły się na głównym pasku narzędzi, oraz pokazywanie formularzy lub szczegółowych informacji bez przechodzenia do osobnego ekranu. Na iOS Apple dodało systemową obsługę Sheet od iOS 15 przez UISheetPresentationController, co potwierdza uniwersalność tego wzorca.
Material Design definiuje trzy typy Bottom Sheet, każdy z własnym zachowaniem i obszarem zastosowania. Wybór odpowiedniego typu bezpośrednio wpływa na doświadczenie użytkownika i zgodność z wytycznymi platformy.
Standardowy Bottom Sheet — komponent niemodalny: użytkownik może wchodzić w interakcję z ekranem tła bez zamykania Sheet. Na przykład, przeglądając listę produktów w oknie tła, użytkownik widzi filtry w Bottom Sheet i może je zmieniać bez zamykania panelu. Sheet dostosowuje swoją wysokość do treści — od 30% do 90% ekranu.
Standardowy Bottom Sheet nie blokuje tła i pozwala na interakcję z główną treścią. To kluczowa różnica w porównaniu z modalnym Sheet, który wymaga obowiązkowego działania, aby kontynuować pracę. Używaj standardowego Sheet, gdy dodatkowa informacja nie jest krytyczna dla głównego scenariusza.
Modalny Bottom Sheet blokuje interakcję z ekranem tła, dopóki użytkownik nie wykona akcji wewnątrz Sheet. Taki Sheet jest zawsze wyświetlany w stanie expanded i wymaga jawnego zamknięcia — przez przycisk, akcję lub przesunięcie w dół. Material Design zaleca modalny Sheet dla formularzy logowania, potwierdzeń i krytycznych ustawień.
Na Androidzie modalny Bottom Sheet jest implementowany przez BottomSheetDialogFragment, który automatycznie blokuje tło. Na iOS analogiczne zachowanie zapewnia modalna wersja UISheetPresentationController z parametrem .mediumDetent. Modalnego Sheet należy używać tylko wtedy, gdy kontekst wymaga uwagi użytkownika.
Rozszerzony Bottom Sheet łączy właściwości standardowego i modalnego: zaczyna się jako niemodalny w stanie collapsed, ale przy przeciągnięciu w górę staje się modalny. W praktyce oznacza to, że użytkownik może najpierw rzucić okiem na zawartość, a następnie podjąć decyzję — rozwinąć Sheet w celu szczegółowego zapoznania się lub zamknąć.
Ten wzorzec jest szczególnie popularny w mapach i nawigacjach, gdzie dolny panel pokazuje krótką informację o punkcie trasy, a przy przesunięciu w górę rozwija się ze szczegółami. Google Maps używa właśnie takiego rozszerzonego Sheet do wyświetlania informacji o miejscach i trasach.
| Typ Sheet | Blokada tła | Stan początkowy | Przykład |
|---|---|---|---|
| Standardowy | Nie | Collapsed | Filtry w katalogu |
| Modalny | Tak | Expanded | Formularz logowania |
| Rozszerzony | Częściowo | Collapsed | Karta miejsca |
Android SDK udostępnia kilka API do implementacji Bottom Sheet, począwszy od klasycznego podejścia View, a skończywszy na nowoczesnym Jetpack Compose. Material Components Library dla Androida zawiera gotowe implementacje BottomSheetDialogFragment i BottomSheetBehavior.
BottomSheetBehavior — to klasa z Material Components, która dołącza się do dowolnego View i zarządza jego pozycją na ekranie. Deweloper ustawia trzy stany: STATE_COLLAPSED (zwinięty), STATE_EXPANDED (rozwinięty) i STATE_HIDDEN (ukryty). Behavior automatycznie obsługuje gesty przeciągania i animację.
val sheet = findViewById<View>(R.id.bottom_sheet)
val behavior = BottomSheetBehavior.from(sheet)
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
behavior.peekHeight = 200
Jetpack Compose udostępnia bardziej deklaratywne API przez komponent ModalBottomSheet z Material 3. W przeciwieństwie do podejścia View, w Compose Bottom Sheet jest funkcją composable, która przyjmuje stan i treść. Sheet automatycznie animuje pojawianie się, ukrywanie i zmianę wysokości przy przeciąganiu.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FilterSheet(onDismiss: () -> Unit) {
ModalBottomSheet(onDismissRequest = onDismiss) {
Text("Filtry")
Button(onClick = onDismiss) { Text("Zastosuj") }
}
}
BottomSheetDialogFragment — to wyspecjalizowany DialogFragment, który wyświetla się jako Bottom Sheet. Automatycznie obsługuje modalność, cykl życia i zmiany konfiguracji. Deweloper musi tylko nadpisać onCreateView i zwrócić układ Sheet. Według dokumentacji Google (Android Developers, 2024), BottomSheetDialogFragment to zalecany sposób dla modalnych Bottom Sheet w klasycznym Androidzie.
Apple wprowadziła systemową obsługę Sheet od iOS 15 przez UISheetPresentationController. Wcześniej deweloperzy implementowali Bottom Sheet ręcznie przez custom transition lub biblioteki zewnętrzne. Natywny Sheet od Apple obsługuje detents — predefiniowane poziomy rozwinięcia.
UISheetPresentationController — to wbudowany kontroler prezentacji, który wyświetla View Controller jako Sheet. Deweloper ustawia tablicę detents — wartości wysokości, na których Sheet może się zatrzymać. iOS 16 dodał custom detents z dowolną wysokością. Apple zaleca używanie medium (.medium) i large (.large) detents jako podstawowych.
let viewController = MySheetViewController()
if let sheet = viewController.sheetPresentationController {
sheet.detents = [.medium, .large]
sheet.prefersGrabberVisible = true
sheet.preferredCornerRadius = 16
}
present(viewController, animated: true)
SwiftUI udostępnia modyfikator .sheet, który wiąże wyświetlanie Sheet ze stanem. Gdy opublikowana zmienna (published) staje się true, SwiftUI automatycznie pokazuje Sheet. SwiftUI obsługuje również detents przez modyfikator .presentationDetents, dodany w iOS 16.
struct ContentView: View {
@State private var showSheet = false
var body: some View {
Button("Pokaż arkusz") { showSheet = true }
.sheet(isPresented: $showSheet) {
FilterView()
.presentationDetents([.medium, .large])
}
}
}
Frameworki cross-platformowe również obsługują Bottom Sheet, dostosowując zachowanie do platformy docelowej. Flutter i React Native udostępniają wbudowane i niestandardowe implementacje z różnym stopniem elastyczności.
Flutter SDK zawiera dwie metody wyświetlania Bottom Sheet: showBottomSheet (standardowy) i showModalBottomSheet (modalny). Material Design we Flutter kieruje się tymi samymi zasadami co na Androidzie — Sheet pojawia się na dole, obsługuje przeciąganie i konfigurowalną wysokość. Dla rozszerzonego zachowania z detektami używany jest DraggableScrollableSheet.
Scaffold.of(context).showBottomSheet((context) {
return Container(
padding: EdgeInsets.all(16),
child: Column(
children: [
Text("Zawartość arkusza"),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: Text("Zamknij")
)
]
)
);
})
React Native nie ma wbudowanego komponentu Bottom Sheet w rdzeniu, dlatego deweloperzy używają bibliotek zewnętrznych. Najbardziej popularna — @gorhom/bottom-sheet (ponad 7 tysięcy gwiazdek na GitHubie, 2024). Udostępnia ona gesture-driven Bottom Sheet z obsługą snap points, animacji i przypiętych nagłówków. Biblioteka jest napisana w Reanimated 2 i Gesture Handler dla wydajności na poziomie 60 FPS.
Prawidłowe użycie Bottom Sheet wymaga przestrzegania wytycznych platformy i zrozumienia scenariuszy, w których Sheet jest odpowiedni. Omówmy kluczowe zalecenia i typowe błędy przy projektowaniu Sheet.
Bottom Sheet jest optymalny dla działań, które są dodatkowe w stosunku do głównej treści na ekranie. Filtry, sortowanie, wybór opcji, krótka informacja o elemencie — typowe scenariusze. Material Design zaleca używanie Bottom Sheet dla narzędzi, które nie powinny zajmować całego ekranu, ale wymagają więcej miejsca niż Action Sheet lub Popup Menu.
Nie używaj Bottom Sheet dla krytycznych ostrzeżeń lub błędów — do tego służą okna dialogowe. Unikaj Sheet z dużą liczbą pól wprowadzania, które wymagają przewijania: użytkownik ma trudności z dostępem do klawiatury i traci kontekst. Jeśli Sheet zawiera więcej niż siedem elementów akcji, rozważ osobny ekran zamiast Sheet.
Typowym błędem jest ustawienie peekHeight (wysokość w stanie zwiniętym) na ponad 40% ekranu. Użytkownik nie rozumie, czy widzi Sheet w całości, czy można go rozwinąć. Material Design zaleca peekHeight w zakresie 15–30% wysokości ekranu, aby pozostawić zauważalną część treści ukrytą i motywować do przeciągania.
Na iOS użytkownicy oczekują, że Sheet można zamknąć, przesuwając go w dół z dowolnej pozycji. Jeśli wyłączysz ten gest (przez UISheetPresentationController.prefersEdgeAttachedInCompactHeight), użytkownik będzie sfrustrowany, próbując zamknąć Sheet w znajomy sposób. Apple Human Interface Guidelines (2024) podkreślają, że przesunięcie w celu zamknięcia to podstawowe oczekiwanie użytkownika iOS.
Często zadawane pytania
Bottom Sheet nie blokuje całkowicie tła i obsługuje przeciąganie, a okno dialogowe jest modalne i wymaga obowiązkowego działania. Bottom Sheet lepiej nadaje się do dodatkowych opcji, okno dialogowe — do krytycznych potwierdzeń.
Tak, ale wymaga ustawienia adjustResize lub adjustPan w manifeście Androida. Na iOS klawiatura automatycznie podnosi Sheet. Flutter i React Native wymagają ręcznej obsługi przez MediaQuery do prawidłowego pozycjonowania.
Zaleca się nie więcej niż 5–7 elementów w standardowym Sheet. Jeśli treści jest więcej — użyj przewijania lub podziału na kategorie. Material Design radzi nie przekraczać 90% wysokości ekranu w stanie expanded.
Dla modalnego Sheet ustaw setCancelable(true) na Androidzie lub isModalInPresentation = false na iOS. W Jetpack Compose onDismissRequest automatycznie obsługuje tapnięcie w tło. W SwiftUI przesunięcie w dół to standardowe zachowanie.
Tak, wszystkie natywne implementacje obsługują VoiceOver i TalkBack. Upewnij się, że FocusManager prawidłowo przenosi fokus do Sheet przy jego otwieraniu i zwraca go z powrotem przy zamykaniu. Niestandardowe implementacje wymagają ręcznej konfiguracji accessibility.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również