Урок 10

Модальные окна и оверлеи

Модалки — экраны, которые появляются поверх. Могут быть встроенными в навигацию или кастомными.

Модалка в навигации

<Stack.Screen
    name="Modal"
    component={ModalScreen}
    options={{
        presentation: 'modal',
        animation: 'slide_from_bottom',
    }}
/>

presentation:

  • 'card' — обычный стек
  • 'modal' — модалка со свайпом вниз
  • 'transparentModal' — прозрачный фон
  • 'fullScreenModal' — полный экран без свайпа
  • 'formSheet' — как в iOS 15+ (маленькая модалка)
  • 'containedModal' — модалка внутри родителя

Открытие

navigation.navigate('Modal');

Закрытие

navigation.goBack();

Или свайпом вниз (для modal).

React Native Modal

Кастомная модалка поверх всего:

import { Modal } from 'react-native';

<Modal
    visible={isVisible}
    animationType="slide"  // 'slide' | 'fade' | 'none'
    transparent
    onRequestClose={() => setVisible(false)}
    statusBarTranslucent
>
    <View style={styles.overlay}>
        <View style={styles.content}>
            <Text>Модальное окно</Text>
            <Pressable onPress={() => setVisible(false)}>
                <Text>Закрыть</Text>
            </Pressable>
        </View>
    </View>
</Modal>

overlay:

const styles = StyleSheet.create({
    overlay: {
        flex: 1,
        backgroundColor: 'rgba(0,0,0,0.5)',
        justifyContent: 'center',
        alignItems: 'center',
    },
    content: {
        backgroundColor: '#fff',
        borderRadius: 16,
        padding: 24,
        width: '80%',
    },
});

Bottom Sheet

Современный стандарт для модалок снизу:

npx expo install @gorhom/bottom-sheet
import BottomSheet, { BottomSheetView } from '@gorhom/bottom-sheet';
import { useRef, useMemo } from 'react';

function Sheet() {
    const ref = useRef<BottomSheet>(null);
    const snapPoints = useMemo(() => ['25%', '50%', '90%'], []);

    return (
        <BottomSheet ref={ref} index={-1} snapPoints={snapPoints}>
            <BottomSheetView>
                <Text>Содержимое</Text>
            </BottomSheetView>
        </BottomSheet>
    );
}

// открыть
ref.current?.expand();

Alert как модалка

Alert.alert(
    'Удалить?',
    'Это действие нельзя отменить',
    [
        { text: 'Отмена', style: 'cancel' },
        { text: 'Удалить', style: 'destructive', onPress: () => delete() },
    ]
);

ActionSheet

Меню действий снизу:

import { ActionSheetIOS, Platform } from 'react-native';

if (Platform.OS === 'ios') {
    ActionSheetIOS.showActionSheetWithOptions(
        {
            options: ['Отмена', 'Поделиться', 'Копировать', 'Удалить'],
            destructiveButtonIndex: 3,
            cancelButtonIndex: 0,
        },
        (index) => {
            if (index === 1) share();
            if (index === 2) copy();
            if (index === 3) remove();
        }
    );
}

Для Android — @expo/react-native-action-sheet.

Кастомный ActionSheet

Часто делают свой для единообразия:

function ActionSheet({ actions, visible, onClose }: ActionSheetProps) {
    return (
        <Modal visible={visible} transparent animationType="slide" onRequestClose={onClose}>
            <Pressable style={styles.backdrop} onPress={onClose}>
                <View style={styles.sheet}>
                    {actions.map((action) => (
                        <Pressable
                            key={action.label}
                            onPress={() => { action.onPress(); onClose(); }}
                            style={styles.item}
                        >
                            <Text style={action.destructive ? styles.destructive : styles.text}>
                                {action.label}
                            </Text>
                        </Pressable>
                    ))}
                </View>
            </Pressable>
        </Modal>
    );
}

Toast

import Toast from 'react-native-toast-message';

Toast.show({
    type: 'success',
    text1: 'Сохранено',
    position: 'bottom',
});

Если ты уже знаешь React

В вебе — <dialog>, порталы. В React Native — Modal, навигационные модалки, bottom sheet.

Итоги

  • presentation: 'modal' для навигационных модалок
  • Modal для кастомных
  • @gorhom/bottom-sheet для bottom sheet
  • Alert.alert для простых диалогов
  • ActionSheet для меню действий
  • Toast для уведомлений