React Native: Screens und Navigation hinzufügen



Was bisher geschah?

Es wird eine Navigation und zusätzliche Bildschirmseiten mit den Einstellungen, der Hauptseite, Info und einem leeren Dummy-Screen erstellt. Die “Welcome to React“-Seite bleibt als Home-Screen bestehen. In dem Header wird links eine Menü- und rechts eine Einstellungen-Schaltfläche eingefügt, die jeweils eine Seitenleiste mit Auswahl-Optionen einblenden. In der Mitte soll der Titel der aktiven Seite angezeigt werden.

Screen Properties Objekt-Typ

Die src/types/default.ts-Datei wird mit  dem  DefaultScreenProps-Typ erweitert. Der Typ erbt von DefaultProps und enthält die Navigations-Parameter für die einzelnen Seiten.

src/types/default.ts

import React from 'react';
import { NavigationParams, NavigationScreenProp, NavigationState } from 'react-navigation';

export interface DefaultProps {
  children?: React.ReactNode;
}

export interface DefaultScreenProps extends DefaultProps{
  navigation: NavigationScreenProp<NavigationState, NavigationParams>;
}

Screen-Header erstellen

Der Screen-Header enthält den Titel der aktiven Seite und die Menü- und Einstellungen-Schaltflächen.

Einen neuen Ordner src/components mit der Datei Header.tsx erstellen.

src/components/Header.tsx

import React from 'react';
import {
  Body, Button, Header as NativeHeader, Icon, Left, Right, Text, Title,
} from 'native-base';
import { StyleSheet, Alert } from 'react-native';
import { DefaultScreenProps } from '../types/default';
import { DrawerActions } from 'react-navigation-drawer';

interface HeaderProps extends DefaultScreenProps{
  title: string;
}

const Header = (props: HeaderProps): React.FunctionComponentElement<HeaderProps> => {

  const onClickMenuButton = (): void => {
    props.navigation.navigate({routeName: 'Menu', action: DrawerActions.toggleDrawer()});
  };

  const onClickMoreButton = (): void => {
    props.navigation.navigate({routeName: 'More', action: DrawerActions.toggleDrawer()});
  };

  return (
    <NativeHeader>
      <Left style={styles.iconWrapper}>
        <Button transparent onPress={onClickMenuButton}>
          <Icon name="menu" />
        </Button>
      </Left>
      <Body>
        <Title>
          <Text style={styles.headerText}>{props.title}</Text>
        </Title>
      </Body>
      <Right style={styles.iconWrapper}>
        <Button transparent onPress={onClickMoreButton}>
          <Icon name="more" />
        </Button>
      </Right>
    </NativeHeader>
  );
};

const styles = StyleSheet.create({
  iconWrapper: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  headerText: {
    fontSize: 22,
    fontWeight: 'bold',
    textAlign: 'center',
    color: 'white',
    width: 200,
    flexDirection: 'row',
    flexWrap: 'nowrap'
  }
});

export default Header;

Eingesetzt wird hier die Header-Komponente aus dem NativeBase-Paket. Sie hat drei Bereiche. Links(<Left />) für die Menü-Schaltfläche, Mitte (<Body />) für den Titel und rechts (<Right />) für die Einstellungen-Schaltfläche. Der Titel wird aus den JSON-Übersetzungsdateien geholt. Die Schaltflächen lösen Ereignisse aus, die links bzw. rechts eine Sidebar mit den Auswahl-Optionen einblenden.


Anzeige

Screens erstellen

Es werden vier Screens erstellt. Home für die “Welcome to React”-Seite, Settings für die Einstellungen, About für die App-Informationen und Dummy als Vorlage für die weitere Screen-Implementierungen.

Zuerst einen neuen Ordner src/screens mit vier Dateien: Home.tsx, Settings.tsx, About.tsx und Dummy.tsx erstellen.

Home

Diese Seite enthält eine modifizierte Kopie der “Welcome to React” – Hauptseite aus App.tsx.

src/screens/Home.tsx

import React from 'react';
import { StyleSheet } from 'react-native';
import { Container, Content, Form, Label } from 'native-base';
import { useTranslation } from 'react-i18next';
import Header from '../components/Header';
import {
  Header as ReactHeader,
  LearnMoreLinks,
  Colors,
  DebugInstructions,
  ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
import { DefaultScreenProps } from '../types/default';

console.disableYellowBox = true;

const Home = (props: DefaultScreenProps): React.FunctionComponentElement<DefaultScreenProps> =>{ 
  const { t, i18n } = useTranslation(); 

  return (
    <Container>
      <Header navigation={props.navigation} title={t('home.headerTitle')} />
      <Content>
        <ReactHeader />
        <Form style={styles.body}>
          <Label style={styles.sectionContainer}>
            <Label style={styles.sectionTitle}>Step One</Label>
            <Label style={styles.sectionDescription}>
              Edit <Label style={styles.highlight}>App.tsx</Label> to change
              this screen and then come back to see your edits.
            </Label>
          </Label>
          <Form style={styles.sectionContainer}>
            <Label style={styles.sectionTitle}>See Your Changes</Label>
            <Label style={styles.sectionDescription}>
              <ReloadInstructions />
            </Label>
          </Form>
          <Form style={styles.sectionContainer}>
            <Label style={styles.sectionTitle}>Debug</Label>
            <Label style={styles.sectionDescription}>
              <DebugInstructions />
            </Label>
          </Form>
          <Form style={styles.sectionContainer}>
            <Label style={styles.sectionTitle}>Learn More</Label>
            <Label style={styles.sectionDescription}>
              Read the docs to discover what to do next:
            </Label>
          </Form>
          <LearnMoreLinks />
        </Form>
      </Content>
    </Container>
  );
};

const styles = StyleSheet.create({
  body: {
    backgroundColor: Colors.white,
  },
  sectionContainer: {
    marginTop: 32,
    paddingHorizontal: 24,
  },
  sectionTitle: {
    fontSize: 24,
    fontWeight: '600',
    color: Colors.black,
  },
  sectionDescription: {
    marginTop: 8,
    fontSize: 18,
    fontWeight: '400',
    color: Colors.dark,
  },
  highlight: {
    fontWeight: '700',
  },
});

export default Home;

Settings

Das Einstellungen-Bildschirm enthält eine Auswahl der Sprache und die Passworteingabe.

src/screens/Settings.tsx

import React from 'react';
import {
  Button, Container, Content, Form, Text, Item, Picker, Icon, Label, Input,
} from 'native-base';
import { useTranslation } from 'react-i18next';
import { StyleSheet, Alert } from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import Header from '../components/Header';
import { DefaultScreenProps } from '../types/default';
import { AppState } from '../reducers/appReducer';
import { setSelectedLanguage, SetPassword } from '../actions/appActions';

const Settings = (props: DefaultScreenProps): React.FunctionComponentElement<DefaultScreenProps> => {
  const { t, i18n } = useTranslation();
  const dispatch = useDispatch();
  const selectedLanguage = useSelector((state: { app: AppState}) => state.app.selectedLanguage);
  const password = useSelector((state: { app: AppState}) => state.app.password);

  const onChangePassword = (newPassword: string): void => {
    dispatch(SetPassword(newPassword));
  };

  const savePassword = () => {
    Alert.alert(t('settings.savePassword'));
  };

  const onSelectedLanguageChange = (newSelectedLanguage: string): void => {
    i18n.changeLanguage(newSelectedLanguage).then((value) => {
      dispatch(setSelectedLanguage(newSelectedLanguage));
    });
  };

  return (
      <Container>
        <Header navigation={props.navigation} title={t('settings.headerTitle')} />
        <Content>
          <Form>
            <Item picker style={styles.settingsPickerItem}>
              <Label>
                {t('settings.language')}:{' '}
              </Label>
              <Picker
                mode="dropdown"
                iosIcon={<Icon name="arrow-down" />}
                style={{ width: undefined }}
                iosHeader={t('settings.selectLanguage')}
                placeholder={t('settings.selectLanguage')}
                placeholderStyle={{ color: '#bfc6ea' }}
                placeholderIconColor="#007aff"
                selectedValue={selectedLanguage}
                onValueChange={onSelectedLanguageChange}
              >
                <Picker.Item label={t('settings.languages.german')} value="de" />
                <Picker.Item label={t('settings.languages.english')} value="en" />
              </Picker>
            </Item>
            <Item picker style={styles.settingsPickerItem}>
              <Label>
                {t('settings.password')}:{' '}
              </Label>
              <Input
                value={password}
                onChangeText={onChangePassword}
                secureTextEntry
                onEndEditing={savePassword}
              />
            </Item>
            <Button style={styles.saveButton}>
              <Text>{t('general.save')}</Text>
            </Button>
          </Form>
        </Content>
      </Container>
  );
};

const styles = StyleSheet.create({
  saveButton: {
    margin: 8,
    justifyContent: 'center',
  },
  settingsPickerItem: {
    marginLeft: 8,
  },
});

export default Settings;



Anzeige

Dummy

Dummy ist eine leere Vorlage für spätere Implementierungen. Sie enthält als Platzhalter nur einen Text und einen Link.

src/screens/Dummy.tsx

import React from 'react';
import {
  Container, Content, Form, Text,
} from 'native-base';
import { useTranslation } from 'react-i18next';
import { StyleSheet, Linking } from 'react-native';
import Header from '../components/Header';
import { DefaultScreenProps } from '../types/default';

const Dummy = (props: DefaultScreenProps): React.FunctionComponentElement<DefaultScreenProps> => {
  const { t, i18n } = useTranslation();

  return (
      <Container>
        <Header navigation={props.navigation} title={t('dummy.headerTitle')} />
        <Content>
          <Form>
            <Text style={styles.text}>{t('dummy.text')}</Text>
            <Text>Link: 
              <Text style={styles.link} 
                onPress={() => Linking.openURL('http://www.technik-tipps-und-tricks.de')}>
                Technik Tipps und Tricks
              </Text>
            </Text>
          </Form>
        </Content>
      </Container>
  );
};

const styles = StyleSheet.create({
  text: {
    color: 'red',
  },
  link: {
    color: "#1B95E0",
    textDecorationLine: "underline",
    fontWeight: "bold"
  },
});

export default Dummy;

About

Die Info-Seite wird später die App-Informationen anzeigen, wie die Version, den Anbieter, ein Icon usw.

src/screens/About.tsx

import React from 'react';
import {
  Container, Content, Form, Text,
} from 'native-base';
import { useTranslation } from 'react-i18next';
import { StyleSheet } from 'react-native';
import Header from '../components/Header';
import { DefaultScreenProps } from '../types/default';

const About = (props: DefaultScreenProps): React.FunctionComponentElement<DefaultScreenProps> => {
  const { t, i18n } = useTranslation();

  return (
      <Container>
        <Header navigation={props.navigation} title={t('about.headerTitle')} />
        <Content>
          <Form>
            <Text style={styles.text}>{t('about.text')}</Text>
          </Form>
        </Content>
      </Container>
  );
};

const styles = StyleSheet.create({
  text: {
    color: 'red',
  },
  link: {
    color: "#1B95E0",
    textDecorationLine: "underline",
    fontWeight: "bold"
  },
});

export default About;

Internationalisierung ergänzen

Die Internationalisierungsdateien mit der Übersetzung ergänzen.

src/translations/en.json

{
  "general": {
    "loading": "Load...",
    "save": "Save",
    "messages": {
      "someMessage": "Some Message"
    },
    "errors": {
      "unknown": "Unknown error occurred!"
    }
  },
  "profile": {
    "headerTitle": "Personal Data",
    "fields": {
      "firstName": "first name",
      "lastName": "family name",
      "phoneNumber": "phone number",
      "emailAddress": "email Address"
    }
  },
  "home": {
    "headerTitle": "Home"
  },
  "settings": {
    "headerTitle": "Settings",
    "selectLanguage": "Select the Language",
    "language": "Language",
    "languages": {
      "german": "German",
      "english": "English"
    },
    "password": "Password",
    "savePassword": "Password successfully changed"
  },
  "dummy": {
    "headerTitle": "Dummy",
    "text": "Empty Dummy Page."
  },
  "about": {
    "headerTitle": "About",
    "text": "Empty Info Page."
  }
}

src/translations/de.json

{
  "general": {
    "loading": "Laden...",
    "save": "Speichern",
    "messages": {
      "someMessage": "Irgendeine Nachricht"
    },
    "errors": {
      "unknown": "Unbekannter Fehler aufgetreten!"
    }
  },
  "profile": {
    "headerTitle": "Persönliche Daten",
    "fields": {
      "firstName": "Vorname",
      "lastName": "Familienname",
      "phoneNumber": "Telefonnummer",
      "emailAddress": "E-Mail-Adresse"
    }
  },
  "home": {
    "headerTitle": "Start"
  },
  "settings": {
    "headerTitle": "Einstellungen",
    "selectLanguage": "Wähle die Sprache",
    "language": "Sprache",
    "languages": {
      "german": "Deutsch",
      "english": "Englisch"
    },
    "password": "Passwort",
    "savePassword": "Passwort erfolgreich geändert"
  },
  "dummy": {
    "headerTitle": "Dummy",
    "text": "Leere Dummy Seite."
  },
  "about": {
    "headerTitle": "Info",
    "text": "Leere Info Seite."
  }
}


Anzeige

Die linke und rechte Optionsleiste mit den Einträgen sollen je nach Klick auf das Menü- oder Mehr-Icon im Header eingeblendet werden.

Eine neue Datei src/components/MenuSidebar.tsx erstellen.

src/components/MenuSidebar.tsx

import React from 'react';
import {
  Body, Container, Content, Icon, Left, ListItem, Right, Text,
} from 'native-base';
import { FlatList } from 'react-native';
import { useTranslation } from 'react-i18next';
import { DefaultScreenProps } from '../types/default';

const MenuSidebar = (props: DefaultScreenProps): React.FunctionComponentElement<DefaultScreenProps> => {
  const { navigation } = props;
  const { t } = useTranslation();

  const routes = [{
    route: 'Home',
    text: t('home.headerTitle'),
    icon: 'home',
    iconColor: 'red',
  }, {
    route: 'Dummy',
    text: t('dummy.headerTitle'),
    icon: 'account',
    iconColor: '#FF9501',
  }];

  return (
    <Container>
      <Content>
        <FlatList
          data={routes}
          keyExtractor={(item) => item.route}
          renderItem={({ item }): React.FunctionComponentElement<DefaultScreenProps> => (
            <ListItem
              icon
              key={item.route}
              onPress={(): void => {
                navigation.navigate(item.route);
                navigation.closeDrawer();
              }}
            >
              <Left>
                <Icon
                  active
                  name={item.icon}
                  type="MaterialCommunityIcons"
                />
              </Left>
              <Body>
                <Text>{item.text}</Text>
              </Body>
              <Right />
            </ListItem>
          )}
        />
      </Content>
    </Container>
  );
};

export default MenuSidebar;

More-Sidebar

Eine neue Datei src/components/MoreSidebar.tsx erstellen.

src/components/MoreSidebar.tsx

import React from 'react';
import {
  Body, Container, Content, Icon, Left, ListItem, Right, Text,
} from 'native-base';
import { FlatList } from 'react-native';
import { useTranslation } from 'react-i18next';
import { DefaultScreenProps } from '../types/default';

const MoreSidebar = (props: DefaultScreenProps): React.FunctionComponentElement<DefaultScreenProps> => {
  const { navigation } = props;
  const { t } = useTranslation();

  const routes = [{
    route: 'Settings',
    text: t('settings.headerTitle'),
    icon: 'settings',
    iconColor: '#FF9501',
  }, {
    route: 'About',
    text: t('about.headerTitle'),
    icon: 'information-outline',
    iconColor: '#FF9501',
  }];

  return (
    <Container>
      <Content>
        <FlatList
          data={routes}
          keyExtractor={(item) => item.route}
          renderItem={({ item }): React.FunctionComponentElement<DefaultScreenProps> => (
            <ListItem
              icon
              key={item.route}
              onPress={(): void => {
                navigation.navigate(item.route);
                navigation.closeDrawer();
              }}
            >
              <Left>
                <Icon
                  active
                  name={item.icon}
                  type="MaterialCommunityIcons"
                />
              </Left>
              <Body>
                <Text>{item.text}</Text>
              </Body>
              <Right />
            </ListItem>
          )}
        />
      </Content>
    </Container>
  );
};

export default MoreSidebar;



Anzeige

AppContainer (Router) erstellen

Der AppContainer oder Router kapselt die Seitennavigation. Je eine DrawerNavigator-Komponente für die linke und rechte Seitenleiste werden erstellt und in einem SwitchNavigator zusammengefasst. Aus diesem wird der AppContainer gebildet.

Zuerst wird das DrawerNavigator-Paket installiert:

$ npm install react-navigation-drawer

Die Datei src/AppContainer.tsx erstellen.

src/AppContainer.tsx

import React from 'react';
import { createAppContainer, createSwitchNavigator } from 'react-navigation';
import { createDrawerNavigator } from 'react-navigation-drawer';
import MenuSidebar from './components/MenuSidebar';
import MoreSidebar from './components/MoreSidebar';
import Home from './screens/Home';
import Dummy from './screens/Dummy';
import Settings from './screens/Settings';
import About from './screens/About';

const MoreNavigator = createDrawerNavigator(
  {
    Settings: { screen: Settings },
    About: { screen: About }
  }, 
  {
    contentComponent: (props) => <MoreSidebar {...props} />,
    drawerPosition: 'right',
    drawerType: 'slide',
  }
);

const MenuNavigator = createDrawerNavigator(
  {
    Home: { screen: Home },
    Dummy: { screen: Dummy },
  }, 
  {
    contentComponent: (props) => <MenuSidebar {...props} />,
    drawerPosition: 'left',
    drawerType: 'slide',
  }
);

const RootNavigator = createSwitchNavigator(
  {
    Menu: { screen: MenuNavigator },
    More: { screen: MoreNavigator },
  }
);

const Router = createAppContainer(RootNavigator);

export default Router;

Router einbinden

Die App-Hautdatei App.tsx mit folgendem Inhalt ersetzen.

App.tsx

import React from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { Root } from 'native-base';
import configureStore from './src/configureStore';
import { DefaultProps } from './src/types/default';
import Router from './src/AppContainer';
import I18nGate from './src/i18nGate';

const { store, persistor } = configureStore();

const App = (): React.FunctionComponentElement<DefaultProps> => {

  return (
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <I18nGate>
          <Root>
            <Router />
          </Root>
        </I18nGate>
      </PersistGate>
    </Provider>
  );
};

export default App;

Die Provider– und PersistGate-Komponenten stellen den permanenten Redux-Speicher zur Verfügung. I18Gate dient der Internationalisierung, Root ist der App-Hauptcontainer und Router die Navigation mit den Screens.

Anzeige

Die fertige Navigation

001-Navigation-Screens [Image]

002-Navigation-Screens [Image]

003-Navigation-Screens [Image]

004-Navigation-Screens [Image]
 

Wie geht es weiter?

 


Anzeige

War diese Seite für dich informativ? Hat sie dir gefallen und geholfen?

Dann unterstütze die Weiterentwicklung mit einer kleinen Spende!

Die Spenden werden für die Lizenzen sowie neue Hard- und Software verwendet, die für weitere Projekte auf dieser Webseite eingesetzt werden.




Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.