Skip to content

Authenticatie voor de chat applicatie

Inhoud

We gaan gebruikers van de chat eerst laten inloggen, waarvoor ze eerst een account moeten aanmaken. Dit account gaan we lokaal op het apparaat van de gebruiker opslaan. In de productieversie kunnen we de aanmeldgegevens van de gebruikers opslaan in een of andere database. In de tussentijd gebruiken we een library om de gebruikersnaam en het wachtwoord dus lokaal op te slaan.

Lokale opslag

Om gegevens lokaal op te slaan maken we gebruik van de volgende library: Expo secure store

Expo SecureStore is een eenvoudige en veilige manier om gevoelige gegevens in je React Native app op te slaan. Je kunt het zien als een kleine, versleutelde kluis voor belangrijke informatie in je app.

Wat het doet

  • Slaat beveiligde sleutel-waardeparen op het apparaat op
  • Versleutelt de gegevens zodat ze niet gemakkelijk toegankelijk zijn voor onbevoegde gebruikers
  • Perfect voor het opslaan van tokens, wachtwoorden en andere gevoelige informatie
  • Werkt op iOS en Android met dezelfde API

Belangrijkste kenmerken

  • Versleuteling: Gegevens worden in versleuteld formaat opgeslagen
  • Eenvoud: Gemakkelijk te gebruiken met slechts enkele methoden (setItemAsync, getItemAsync, deleteItemAsync)
  • Alleen strings: Slaat alleen stringwaarden op (objecten moeten naar JSON geconverteerd worden)
  • Groottebeperking: Beperkte opslagruimte (ongeveer 2MB in totaal)
  • Async API: Alle bewerkingen geven Promises terug

Wanneer te gebruiken

  • Voor het opslaan van authenticatietokens
  • Voor het bewaren van gebruikersreferenties
  • Voor het veilig bewaren van API-sleutels
  • Voor het opslaan van gevoelige gebruikersinformatie

Take The Wheel

Installeer deze library in jouw project.

React Context

We gaan ervoor moeten zorgen dat de aanmeldgegevens beschikbaar zijn voor alle componenten. Om dit alles te beheren gaan we gebruik maken van de React context.

Take The Wheel

  • Maak een map providers in de root van je project
  • Maak een nieuw bestand met de naam ChatContext.tsx aan in deze map
  • Importeer de volgende dependencies in dit bestand:
    • createContext en useState vanuit React
    • useRouter vanuit expo-router
    • Alert vanuit react-native
    • * als SecureStore vanuit expo-secure-store;

createContext

Vervolgens gaan we de React context aanmaken zodat alle componenten gebruik kunnen maken van de gegevens.

Context initialiseren

ts
export const ChatContext = createContext(null);

De Provider

Alles staat nu klaar om onze Provider aan te maken die de state variabelen gaat voorzien voor de applicatie. Dit doe je als volgt:

Provider aanmaken

ts
export const ChatProvider = (props) => {
    // Initialiseer de router om te kunnen navigeren

    // Maak een state variabele isLoggedIn aan om de login status op te volgen, standaard is deze false

    // Maak een state variabele aan om de gebruikersnaam in op te slaan, standaard is dit een lege string

    // Maak een toggleLogin functie aan om te schakelen tussen de statussen van ingelogd en uitgelogd
};

Take The Wheel

Implementeer in deze provider het volgende:

  • Initialiseer de router om te kunnen navigeren
  • Maak een state variabele isLoggedIn aan om de login status op te volgen, standaard is deze false
  • Maak een state variabele loggedUser aan om de gebruikersnaam in op te slaan, standaard is dit een lege string
  • Maak een asynchrone toggleLogin functie aan om te schakelen tussen de statussen van ingelogd en uitgelogd
    • Controleer de inlogstatus: Gebruik de variabele isLoggedIn om de huidige inlogstatus van de gebruiker te controleren.
    • Uitloggen:
      • Als de gebruiker is ingelogd, stel de waarde van userLoggedIn in op none met behulp van await SecureStore.setItemAsync('userLoggedIn', 'none').
      • Zet de variabele isLoggedIn op false.
      • Maak de variabele loggedUser leeg.
      • Toon een melding met de tekst 'Je bent uitgelogd' met behulp van Alert.alert.
    • Navigeren naar de inlogpagina:
      • Als de gebruiker niet is ingelogd, navigeer dan naar de inlogpagina met behulp van router.push('/login').

Ophalen van de gebruiker

Is de gebruiker ingelogd?

Vervolgens gaan we moeten te weten te komen of er een gebruiker is ingelogd wanneer de App wordt opgestart. Hiervoor gaan we een asynchrone functie getUser aanmaken.

Take The Wheel

Ophalen van inloggegevens:

  • Gebruik await SecureStore.getItemAsync('userLoggedIn') om de inloggegevens van de gebruiker op te halen en sla deze op in een variabele result.

Controleer de opgehaalde gegevens:

  • Als result gelijk is aan 'none', betekent dit dat er geen gebruiker is ingelogd. Log dit naar de console met de tekst 'Er is geen gebruiker ingelogd'.
  • Als result undefined is, betekent dit dat de app voor de eerste keer wordt uitgevoerd. Maak de storage key aan met await SecureStore.setItemAsync('userLoggedIn', 'none') en log dit naar de console met de tekst 'App voor de eerste keer gestart, geen gebruiker aanwezig'.
  • Als result een andere waarde heeft, betekent dit dat een gebruiker is ingelogd. Werk de status bij door setIsLoggedIn(true) en setLoggedUser(result) aan te roepen en log dit naar de console met de tekst 'ingelogde gebruiker' samen met de gebruikersnaam van de gebruiker.

Foutafhandeling:

  • Gebruik een try-catch blok om eventuele fouten tijdens het ophalen van de gegevens af te handelen. Log eventuele fouten naar de console met de tekst 'Error getting user data:' samen met de error.

de Chatgebruiker ophalen

Daarna voegen we een asynchrone functie getChatUser toe die de chatgebruiker ophaalt. Dit is vergelijkbaar met de getUser functie, maar deze functie zal navigeren naar de login pagina als er geen ingelogde gebruiker is.

Take The Wheel

Ophalen van inloggegevens:

  • Gebruik await SecureStore.getItemAsync('userLoggedIn') om de inloggegevens van de gebruiker op te halen en sla deze op in een variabele result.

Controleer de opgehaalde gegevens:

  • Als result gelijk is aan 'none' of undefined, betekent dit dat er geen gebruiker is ingelogd.
    • Log dit naar de console met de tekst 'Geen gebruiker ingelogd')
    • Toon een melding met Alert.alert('Je moet inloggen om te kunnen chatten'), en navigeer naar de inlogpagina.
  • Als result een andere waarde heeft, betekent dit dat een gebruiker is ingelogd. Log dit naar de console met de tekst 'Gebruiker ingelogd' samen met de gebruikersnaam van de gebruiker.

Foutafhandeling:

  • Gebruik een try-catch blok om eventuele fouten tijdens het ophalen van de gegevens af te handelen. Log eventuele fouten naar de console met de tekst 'Error getting chat user:' samen met de error en navigeer naar de loginpagina.

De variabelen ter beschikking stellen

We hoeven nu alleen nog de waarden op te geven die beschikbaar moeten zijn in de context. In de return voegen we het ChatContext.Provider component toe. Alle waarden die we aan de provider toevoegen zullen beschikbaar zijn voor elk component dat de context gebruikt. We voegen daarom toe:

  • toggleLogin,
  • getUser,
  • isLoggedIn,
  • setIsLoggedIn,
  • loggedUser
  • getChatUser

Binnen dit component voegen we de children prop toe. Hiermee kunnen we deze waarden doorgeven aan alle child componenten.

Dit ziet er als volgt uit:

Het ChatContext component

tsx
return (
    <ChatContext.Provider
        value={{
            toggleLogin,
            getUser,
            isLoggedIn,
            setIsLoggedIn,
            loggedUser,
            getChatUser
        }}
    >
        {props.children}
    </ChatContext.Provider>
);
Uitgewerkte code
tsx
import {createContext, useState} from 'react';
import {useRouter} from 'expo-router';
import * as SecureStore from 'expo-secure-store';
import {Alert} from 'react-native';

export const ChatContext = createContext(null);

export const ChatProvider = (props) => {
    // Initialiseer de router om te kunnen navigeren
    const router = useRouter();

    // De staat om de login status op te volgen
    const [isLoggedIn, setIsLoggedIn] = useState(false);

    // De staat pù de gebruikersnaam in op te slaan
    const [loggedUser, setLoggedUser] = useState('');

    //toggle tussen de statussen van ingelogd en uitgelogd
    const toggleLogin = async () => {
        if (isLoggedIn) {
            // Als de gebruiker ingelogd is, loggen we deze uit
            await SecureStore.setItemAsync('userLoggedIn', 'none');
            setIsLoggedIn(false);
            setLoggedUser('');
            Alert.alert('You have been logged out');
        } else {
            // Wanneer geen gebruiker ingelogd is navigeren we naar de login pagina.
            router.push('/login');
        }
    }

    const getUser = async () => {
        try {
            const result = await SecureStore.getItemAsync('userLoggedIn');

            if (result === 'none') {
                console.log('No user logged in');
            } else if (result === undefined) {
                await SecureStore.setItemAsync('userLoggedIn', 'none');
                console.log('First time app launch, no user');
            } else {
                setIsLoggedIn(true);
                setLoggedUser(result);
                await SecureStore.setItemAsync('userLoggedIn', result);
                console.log('Logged in user: ', loggedUser)
            }
        } catch (error) {
            console.error('Error getting user data:', error);
        }
    };

    const getChatUser = async () => {
        try {
            const result = await SecureStore.getItemAsync('userLoggedIn');
            if (result === 'none' || result === undefined) {
                console.log('No user logged in');
                Alert.alert('You need to log in to chat');
                router.push('/login');
            } else {
                console.log(`User signed in: ${result}`);
            }
        } catch (error) {
            console.error('Error getting chat user:', error);
            router.push('/login');
        }
    };

    return (
        <ChatContext.Provider
            value={{
                toggleLogin,
                getUser,
                isLoggedIn,
                setIsLoggedIn,
                loggedUser,
                getChatUser
            }}
        >
            {props.children}
        </ChatContext.Provider>
    );
};

Implementatie in de Header

We gaan de context nu aanroepen en gebruiken in het Header component zodat we gebruik kunnen maken van de waarden uit de context.

Take The Wheel

  1. Importeer:

    • ChatContext
    • useEffect en useContext vanuit React
  2. Zorg dat je aan de toggleLogin, getUser en isLoggedIn data kan door de context te initialiseren met de useContext hook en geef de ChatContext mee als argument:

ts
const {toggleLogin, getUser, isLoggedIn} = useContext(ChatContext);
  1. Voeg vervolgens de useEffect hook toe om ervoor te zorgen dat de getUser functie wordt uitgevoerd wanneer het component wordt weergegeven. Voeg ook de isLoggedIn variabele toe als dependency om ervoor te zorgen dat useEffect wordt uitgevoerd wanneer deze waarde wordt gewijzigd, bijvoorbeeld wanneer een gebruiker uitlogt.

  2. Zorg ervoor dat de kleur van de login icoon wijzigt naar wit als de gebruiker is uitgelogd.

  3. Gebruik de toggleLogin functie wanneer een gebruiker klikt op het icoon.

Uitgewerkte code
tsx
import {StyleSheet, View, Image, Text, Pressable} from 'react-native';
import {useRouter} from 'expo-router';
import {FontAwesome6} from '@expo/vector-icons';
import {ChatContext} from "../providers/ChatContext";
import {useEffect, useContext} from "react";
import logo from '../assets/logo.png';

export default function Header() {
    const router = useRouter();
    const {toggleLogin, getUser, isLoggedIn} = useContext(ChatContext);

    useEffect(() => {
        getUser();
    }, [isLoggedIn])
    let userDisplay = isLoggedIn ? <FontAwesome6 name="user-circle" size={30} color="#00bda5"/> :
        <FontAwesome6 name="user-circle" size={30} color="#FFFFFF"/>;

    return (
        <View style={styles.header}>
            <Pressable
                onPress={() => {
                    router.push('/');
                }}
            >
                <Image
                    source={logo}
                    style={styles.logoStyle}
                />
            </Pressable>
            <Text style={styles.menu}>
                Web Expert
            </Text>
            <Text style={styles.menu}>
                Chat
            </Text>
            <Text
                style={styles.menu}
                onPress={() => {
                    router.push('/register');
                }}
            >
                Registreer
            </Text>
            <Text
                style={styles.menu}
                onPress={toggleLogin}
            >
                {userDisplay}
            </Text>
        </View>
    );
};

const styles = StyleSheet.create({
    header: {
        alignItems: 'center',
        backgroundColor: '#212121',
        height: 120,
        width: '100%',
        flexDirection: 'row',
        justifyContent: 'space-between',
        paddingHorizontal: 5
    },
    logoStyle: {
        height: 50,
        width: 50,
        resizeMode: 'stretch'
    },
    menu: {
        color: '#00bda5',
        fontSize: 16,
        alignSelf: 'center',
        paddingHorizontal: 5
    }
});

De layout template

Het laatste wat we moeten doen om onze context te kunnen gebruiken, is ervoor zorgen dat componenten deel uitmaken van de provider zodat alle variabelen beschikbaar zijn voor alle componenten.

Take The Wheel

  • Importeer de ChatProvider van de ChatContext
  • Gebruik de ChatProvider in de template en zorg dat alle componenten binnen dit component vallen.
Uitgewerkte code
tsx
import {Slot} from 'expo-router';
import {SafeAreaView} from 'react-native-safe-area-context';
import {ChatProvider} from "../providers/ChatContext";
import Header from '../components/Header';
import Footer from '../components/Footer';

export default function Layout() {
    return (
        <ChatProvider>
            <SafeAreaView>
                <Header/>
                <Slot/>
                <Footer/>
            </SafeAreaView>
        </ChatProvider>
    );
};

De Registratie pagina

Voordat gebruikers kunnen inloggen moeten ze zich eerst kunnen registreren. Maak daarom een registratie pagina op basis van de volgende opdracht.

Take The Wheel

  1. Maak een bestand genaamd register.tsx

  2. Implementeer een registratieformulier met de volgende velden:

    • Gebruikersnaam invoerveld
    • Wachtwoord invoerveld
    • Wachtwoordbevestiging invoerveld
  3. Voeg validatie toe voor:

    • Controle op lege gebruikersnaam
    • Controle op leeg wachtwoord
    • Overeenkomst wachtwoordbevestiging
    • Controle op bestaande gebruikersnaam
  4. Implementeer navigatie met Expo Router:

    • Navigeer terug naar de startpagina na succesvolle registratie
    • Voorzie een annuleerknop om terug te keren naar de startpagina
  5. Zorg ervoor dat gebruikers op deze pagina terecht komen wanneer ze in de Header klikken op de Registreer knop.

Voor de stijl kan je het volgende gebruiken als basis:

tsx
const styles = StyleSheet.create({
    container: {
        height: boundedHeight,
        ...Platform.select({
            android: {
                paddingBottom: 360
            },
            ios: {
                paddingBottom: 160
            },
            default: {
                paddingBottom: 20
            }
        }),
    },
    formView: {
        alignItems: 'center'
    },
    formTitle: {
        ...Platform.select({
            android: {
                fontSize: 20,
                paddingVertical: 10
            },
            ios: {
                fontSize: 20,
                paddingVertical: 10
            },
            default: {
                fontSize: 30,
                paddingVertical: 20
            }
        })
    },
    formLabel: {
        ...Platform.select({
            android: {
                fontSize: 16,
                paddingTop: 10
            },
            ios: {
                fontSize: 16,
                paddingTop: 10
            },
            default: {
                fontSize: 24,
                paddingTop: 18
            }
        })
    },
    formInput: {
        width: 250,
        borderWidth: 1,
        padding: 10,
        ...Platform.select({
            android: {
                fontSize: 16,
            },
            ios: {
                fontSize: 16,
            },
            default: {
                fontSize: 24,
                width: 400
            }
        })
    },
    formButtonLabel: {
        ...Platform.select({
            android: {
                fontSize: 16,
                paddingTop: 12
            },
            ios: {
                fontSize: 16,
                paddingTop: 12
            },
            default: {
                fontSize: 24,
                paddingTop: 20
            }
        })
    }
});
Uitgewerkte Code
tsx
import {
    StyleSheet,
    Text,
    View,
    ScrollView,
    TextInput,
    TouchableOpacity,
    Alert,
    Dimensions,
    Platform
} from 'react-native';
import {useState} from 'react';
import {useRouter} from 'expo-router';
import * as SecureStore from 'expo-secure-store';

const windowHeight = Dimensions.get('window').height;

export default function Register() {
    const router = useRouter();
    const [userName, setUserName] = useState('');
    const [password, setPassword] = useState('');
    const [passwordConfirm, setPasswordConfirm] = useState('');

    const cancelRegistration = () => {
        router.replace('/');
    };

    const registerUser = async () => {
        if (userName.trim() === '') {
            Alert.alert('Fout', 'Voer een gebruikersnaam in');
            return;
        }

        if (password === '' || passwordConfirm === '') {
            Alert.alert('Fout', 'Voer een wachtwoord en bevestiging in');
            return;
        }

        if (password !== passwordConfirm) {
            Alert.alert('Fout', 'Wachtwoorden komen niet overeen');
            return;
        }

        const storedUser = await SecureStore.getItemAsync('userLoggedIn');
        if (storedUser && storedUser === userName) {
            Alert.alert('Fout', 'Gebruikersnaam bestaat al');
            return;
        }

        await SecureStore.setItemAsync(userName, password);

        Alert.alert('Succes', 'Account aangemaakt', [
            {
                text: 'OK',
                onPress: () => router.replace('/')
            }
        ]);
    };

    return (
        <View style={styles.container}>
            <ScrollView contentContainerStyle={styles.formView}>
                <Text style={styles.formTitle}>Account Registratie</Text>

                <Text style={styles.formLabel}>Gebruikersnaam</Text>
                <TextInput
                    style={styles.formInput}
                    value={userName}
                    onChangeText={setUserName}
                    placeholder="Voer gebruikersnaam in"
                />

                <Text style={styles.formLabel}>Wachtwoord</Text>
                <TextInput
                    style={styles.formInput}
                    value={password}
                    onChangeText={setPassword}
                    placeholder="Voer wachtwoord in"
                    secureTextEntry
                />

                <Text style={styles.formLabel}>Bevestig Wachtwoord</Text>
                <TextInput
                    style={styles.formInput}
                    value={passwordConfirm}
                    onChangeText={setPasswordConfirm}
                    placeholder="Bevestig wachtwoord"
                    secureTextEntry
                />

                <TouchableOpacity
                    onPress={registerUser}
                >
                    <Text style={styles.formButtonLabel}>ACCOUNT REGISTREREN</Text>
                </TouchableOpacity>

                <TouchableOpacity
                    onPress={cancelRegistration}
                >
                    <Text style={styles.formButtonLabel}>ANNULEREN</Text>
                </TouchableOpacity>
            </ScrollView>
        </View>
    );
}

const styles = StyleSheet.create({
    container: {
        height: windowHeight,
        ...Platform.select({
            android: {
                paddingBottom: 160
            },
            ios: {
                paddingBottom: 160
            },
            default: {
                paddingBottom: 20
            }
        }),
    },
    formView: {
        alignItems: 'center'
    },
    formTitle: {
        ...Platform.select({
            android: {
                fontSize: 20,
                paddingVertical: 10
            },
            ios: {
                fontSize: 20,
                paddingVertical: 10
            },
            default: {
                fontSize: 30,
                paddingVertical: 20
            }
        })
    },
    formLabel: {
        ...Platform.select({
            android: {
                fontSize: 16,
                paddingTop: 10
            },
            ios: {
                fontSize: 16,
                paddingTop: 10
            },
            default: {
                fontSize: 24,
                paddingTop: 18
            }
        })
    },
    formInput: {
        width: 250,
        borderWidth: 1,
        padding: 10,
        ...Platform.select({
            android: {
                fontSize: 16,
            },
            ios: {
                fontSize: 16,
            },
            default: {
                fontSize: 24,
                width: 400
            }
        })
    },
    formButtonLabel: {
        ...Platform.select({
            android: {
                fontSize: 16,
                paddingTop: 12
            },
            ios: {
                fontSize: 16,
                paddingTop: 12
            },
            default: {
                fontSize: 24,
                paddingTop: 20
            }
        })
    }
});

De Login pagina

Een gebruiker kan zich registreren met het registratieformulier. Het is nu ook de bedoeling dat hij zich kan aanmelden. Hiervoor bouwen we een formulier waarmee we kunnen nagaan of de ingevulde gegevens overeenkomen met de gegevens die opgeslagen zijn in de lokale opslag (via SecureStore). Op de login pagina voorzien we volgende functionaliteit:

  • Er kan een gebruikersnaam en wachtwoord ingegeven worden via een formulier.
  • De credentials worden vergeleken met de opgeslagen data.
  • De state van de applicatie wordt geüpdatet wanneer een gebruiker succesvol is ingelogd.
  • Navigatie opties zodat gebruikers het aanmelden kunnen annuleren of ervoor kunnen kiezen om zich aan te melden.

Take The Wheel

  1. Maak een bestand genaamd login.tsx

  2. Implementeer een loginformulier:

    • Gebruik een View element met daarbinnen een ScrollView
    • Voorzie binnen dit ScollView
      • Een Text veld voor de titel van het formulier
      • Een Text veld het label van het invulveld voor de gebruikersnaam
      • Een TextInput veld voor de gebruikersnaam
      • Een Text veld het label van het invulveld voor het wachtwoord
      • Een TextInput veld voor het wachtwoord en zorg ervoor dat het wachtwoord niet zichtbaar blijft, maar vervangen wordt door "bolletjes" (zoals een password inputveld in HTML)
      • Drie TouchableOpacity velden (waarbinnen telkens een Text veld wordt voorzien)
        • Om de gebruiker in te loggen
        • Om het aanmelden te annuleren
        • Om naar de registratiepagina te navigeren
  3. Zorg dat je aan de setIsLoggedIn data van de applicatie kan door de context te initialiseren met de useContext hook en geef de ChatContext mee als argument.

  4. Maak een state variabele aan voor de gebruikersnaam (string) en link dit aan het TextInput veld voor de gebruikersnaam.

  5. Maak een state variabele aan voor het wachtwoord (string) en link dit aan het TextInput veld voor het wachtwoord.

  6. Laad de router in.

  7. Maak een functie cancelLogin waarmee de gebruiker naar de startpagina wordt geleid. Link deze functie aan de knop om het aanmelden te annuleren.

  8. Maak een functie createAccount waarmee de gebruiker naar de registratiepagina wordt geleid. Link deze functie aan de juiste knop.

  9. Maak een functie loginUser waarmee de gebruiker ingelogd wordt en link deze functie aan de juiste knop. Voorzie binnen deze functie het volgende:

    • Wanneer geen gebruikersnaam werd ingevuld, toon je een gepaste Alert
    • Wanneer geen wachtwoord werd ingevuld, toon je een gepaste Alert
    • Wanneer beide gegevens wel zijn ingevuld:
      • Ga je na of er al een gebruiker is ingelogd. Indien dat al het geval is, geef je een gepast Alert en leid je de gebruiker naar de startpagina.
      • Indien er nog geen gebruiker is ingelogd, ga je na of het ingevulde wachtwoord overeenkomt met het opgeslagen wachtwoord voor de gebruiker. Is het wachtwoord fout, voorzie dan een gepaste Alert. Is het wel juist, zorg er dan voor
        • dat je de gebruikersnaam toewijst aan userLoggedIn variabele van de SecureStore
        • dat de loggedIn variabele de waarde true krijgt
        • dat de gebruiker uiteindelijk naar de startpagina wordt geleid
      • Indien er geen gebruiker werd gevonden, voorzie je ook een gepaste Alert.

Voor de stijl kan je de volgende code gebruiken als basis:

tsx
const styles = StyleSheet.create({
    container: {
        height: boundHeight,
        ...Platform.select({
            android: {
                paddingBottom: 160
            },
            ios: {
                paddingBottom: 160
            },
            default: {
                paddingBottom: 20
            }
        }),
    },
    formView: {
        alignItems: 'center'
    },
    formTitle: {
        ...Platform.select({
            android: {
                fontSize: 20,
                paddingVertical: 10
            },
            ios: {
                fontSize: 20,
                paddingVertical: 10
            },
            default: {
                fontSize: 30,
                paddingVertical: 20
            }
        })
    },
    formLabel: {
        ...Platform.select({
            android: {
                fontSize: 16,
                paddingTop: 10
            },
            ios: {
                fontSize: 16,
                paddingTop: 10
            },
            default: {
                fontSize: 24,
                paddingTop: 18
            }
        })
    },
    formInput: {
        width: 250,
        borderWidth: 1,
        padding: 10,
        ...Platform.select({
            android: {
                fontSize: 16,
            },
            ios: {
                fontSize: 16,
            },
            default: {
                fontSize: 24,
                width: 400
            }
        })
    },
    formButtonLabel: {
        ...Platform.select({
            android: {
                fontSize: 16,
                paddingTop: 12
            },
            ios: {
                fontSize: 16,
                paddingTop: 12
            },
            default: {
                fontSize: 24,
                paddingTop: 20
            }
        })
    }
})