Одним из наиболее важных аспектов разработки приложений React Native является навигация. Это то, что позволяет пользователям переходить на страницы, которые они ищут. Вот почему важно выбрать лучшую навигационную библиотеку, соответствующую вашим потребностям.
Если в вашем приложении много экранов с относительно сложным пользовательским интерфейсом, возможно, стоит изучить React Native Navigation вместо React Navigation . Это связано с тем, что в React Navigation всегда будут узкие места в производительности, поскольку он работает с тем же потоком JavaScript, что и остальная часть приложения. Чем сложнее ваш пользовательский интерфейс, тем больше данных должно быть передано на этот мост, что потенциально может замедлить его.
В этом руководстве мы рассмотрим библиотеку React Native Navigation от Wix, альтернативную библиотеку навигации для тех, кто ищет более плавную навигацию для своих приложений React Native.
Хотите узнать React Native с нуля? Эта статья является выдержкой из нашей Премиум библиотеки. Получите полную коллекцию книг React Native, охватывающую основы, проекты, советы и инструменты и многое другое с SitePoint Premium. Присоединяйтесь сейчас всего за $ 9 / месяц .
Предпосылки
Знание React и React Native необходимо, чтобы следовать этому руководству. Предыдущий опыт работы с библиотекой навигации, такой как React Navigation, не является обязательным.
Обзор приложения
Чтобы продемонстрировать, как использовать библиотеку, мы создадим простое приложение, которое ее использует. Приложение будет иметь всего пять экранов:
- Инициализация : это служит начальным экраном для приложения. Если пользователь вошел в систему, он автоматически перейдет на главный экран. Если нет, пользователь переходит на экран входа в систему.
- Вход в систему : это позволяет пользователю войти в систему, чтобы они могли просматривать дом, галерею и канал. Чтобы упростить вещи, логин будет просто издеваться; Фактический код аутентификации не будет задействован. С этого экрана пользователь также может перейти на экран забытого пароля.
- ForgotPassword : экран заполнения , который запрашивает адрес электронной почты пользователя. Это будет просто использоваться для демонстрации стековой навигации.
- Домашняя страница : начальный экран, который пользователь увидит при входе в систему. Отсюда они также могут переходить либо к галерее, либо к экранам каналов через навигацию в нижней вкладке.
- Галерея : экран заливки, который показывает пользовательский интерфейс фотогалереи.
- Feed : экран заливки, который показывает интерфейс новостной ленты.
Вот как будет выглядеть приложение:
Вы можете найти исходный код примера приложения в этом репозитории GitHub .
Начальная загрузка приложения
Давайте начнем с создания нового проекта React Native:
react-native init RNNavigation --version [email protected]
Примечание: мы используем немного более старую версию React Native, потому что React Native Navigation плохо работает с более поздними версиями React Native. React Native Navigation не поспевала за изменениями в ядре React Native с версии 0.58. Единственная известная версия, которая безупречно работает с React Native, — это версия, которую мы собираемся использовать. Если вы проверите проблемы в их репо , вы увидите различные проблемы в версии 0.58 и 0.59 . В этих двух версиях могут быть обходные пути, но самая безопасная ставка — версия 0.57.
Что касается React Native версии 0.60 , основная команда внесла много изменений . Одним из них является переход на AndroidX , цель которого — прояснить, какие пакеты связаны с операционной системой Android. По сути это означает, что если нативный модуль использует какой-либо из старых пакетов, которые были перенесены на новый androidx.*
иерархия пакетов, она сломается. Есть такие инструменты, как jetifier , который позволяет перейти на AndroidX. Но это не гарантирует, что React Native Navigation будет работать.
Далее установите зависимости приложения:
-
react-native-navigation
— библиотека навигации, которую мы собираемся использовать. -
@react-native-community/async-storage
— для сохранения данных в локальном хранилище приложения. -
react-native-vector-icons
— для отображения значков для навигации по нижней вкладке.
yarn add react-native-navigation @react-native-community/async-storage react-native-vector-icons
В следующих нескольких разделах мы будем настраивать только что установленные пакеты.
Настройка React Native Navigation
Сначала мы настроим библиотеку React Native Navigation. Инструкции, которые мы рассмотрим здесь, также находятся в официальной документации . К сожалению, это написано не очень дружелюбно для начинающих, поэтому мы рассмотрим это более подробно.
Примечание: демонстрационный проект включает в себя папки Android и iOS . Вы можете использовать их в качестве справки, если у вас возникнут проблемы с настройкой.
Поскольку название библиотеки очень длинное, с этого момента я буду называть ее просто RNN.
Настройка Android
В этом разделе мы рассмотрим, как настроить RNN для Android. Прежде чем продолжить, важно обновить все пакеты SDK до последних версий. Вы можете сделать это через Android SDK Manager.
settings.gradle
Добавьте следующее в ваш файл android/settings.gradle
:
include ':react-native-navigation' project(':react-native-navigation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-navigation/lib/android/app/')
Свойства Gradle Wrapper
В вашем android/gradle/wrapper/gradle-wrapper.properties
обновите Grav’s distributionUrl
для использования версии 4.4, если она еще не используется:
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
build.gradle
Далее, в вашем файле android/build.gradle
добавьте mavenLocal()
и mavenCentral()
в buildscript
-> repositories
:
buildscript { repositories { google() jcenter() // add these: mavenLocal() mavenCentral() } }
Затем обновите buildscript
к classpath
в buildscript
от buildscript
->, чтобы указать версию Gradle, которая нам нужна:
buildscript { repositories { ... } dependencies { classpath 'com.android.tools.build:gradle:3.0.1' } }
В allprojects
-> repositories
добавьте mavenCentral()
и JitPack. Это позволяет нам извлекать данные из репозитория React Native Navigation из JitPack :
allprojects { allprojects { repositories { mavenLocal() google() jcenter() mavenCentral() // add this maven { url 'https://jitpack.io' } // add this } }
Затем добавьте глобальный конфиг для настройки инструментов сборки и версий SDK для Android:
allprojects { ... } ext { buildToolsVersion = "27.0.3" minSdkVersion = 19 compileSdkVersion = 26 targetSdkVersion = 26 supportLibVersion = "26.1.0" }
Наконец, мы все равно хотели бы сохранить стандартную команду react-native run-android
при компиляции приложения, поэтому нам нужно настроить Gradle на игнорирование других вариантов React Native Navigation, за исключением того, который мы используем в настоящее время ( reactNative57_5
). Игнорирование их гарантирует, что мы компилируем только конкретную версию, от которой мы зависим:
ext { ... } subprojects { subproject -> afterEvaluate { if ((subproject.plugins.hasPlugin('android') || subproject.plugins.hasPlugin('android-library'))) { android { variantFilter { variant -> def names = variant.flavors*.name if (names.contains("reactNative51") || names.contains("reactNative55") || names.contains("reactNative56") || names.contains("reactNative57")) { setIgnore(true) } } } } } }
Примечание: в настоящее время существует четыре других варианта RNN. Это те, которые мы игнорируем выше:
- reactNative51
- reactNative55
- reactNative56
- reactNative57
Android / приложение / build.gradle
В файле android/app/build.gradle
разделе android
-> compileOptions
убедитесь, что source
и target
версии совместимости — 1.8
:
android { defaultConfig { ... } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } }
Затем, в ваших dependencies
, включите react-native-navigation
как зависимость:
dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" implementation "com.facebook.react:react-native:+" implementation project(':react-native-navigation') // add this }
Наконец, под android
-> defaultConfig
, установите missingDimensionStrategy
чтобы reactNative57_5
с reactNative57_5
. Это версия RNN, совместимая с React Native 0.57.8:
defaultConfig { applicationId "com.rnnavigation" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion missingDimensionStrategy "RNN.reactNativeVersion", "reactNative57_5" // add this versionCode 1 versionName "1.0" ndk { abiFilters "armeabi-v7a", "x86" } }
MainActivity.java
В вашем файле app/src/main/java/com/rnnavigation/MainActivity.java
вместо расширения NavigationActivity
RNN:
package com.rnnavigation; // import com.facebook.react.ReactActivity; import com.reactnativenavigation.NavigationActivity; // public class MainActivity extends ReactActivity { public class MainActivity extends NavigationActivity { // remove all code inside }
Поскольку мы удалили код по умолчанию, который использует React Native, нам больше не нужно регистрировать основной компонент, как вы увидите позже в разделе « Создание приложения »:
// index.js import { AppRegistry } from "react-native"; import App from "./App"; import { name as appName } from "./app.json"; AppRegistry.registerComponent(appName, () => App);
MainApplication.java
Затем вам также необходимо обновить файл app/src/main/java/com/rnnavigation/MainApplication.java
чтобы он наследовал от класса RNN. Начните с импорта классов RNN:
import com.facebook.react.shell.MainReactPackage; import com.facebook.soloader.SoLoader; // add these import com.reactnativenavigation.NavigationApplication; import com.reactnativenavigation.react.NavigationReactNativeHost; import com.reactnativenavigation.react.ReactGateway;
Затем унаследуйте от класса RNN NavigationApplication
:
// public class MainApplication extends Application implements ReactApplication { public class MainApplication extends NavigationApplication { }
Наконец, полностью замените содержимое класса следующим:
@Override protected ReactGateway createReactGateway() { ReactNativeHost host = new NavigationReactNativeHost(this, isDebug(), createAdditionalReactPackages()) { @Override protected String getJSMainModuleName() { return "index"; } }; return new ReactGateway(this, isDebug(), host); } @Override public boolean isDebug() { return BuildConfig.DEBUG; } protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( ); } @Override public List<ReactPackage> createAdditionalReactPackages() { return getPackages(); }
В приведенном выше коде вам может показаться знакомым только метод getPackages()
. Как и в стандартном проекте React Native, здесь вы можете инициализировать классы ваших собственных зависимостей.
Настройка iOS
В этом разделе мы настроим RNN для iOS. Прежде чем продолжить, важно, чтобы вы обновили Xcode до последней доступной версии. Это помогает избежать потенциальных ошибок, которые приходят с устаревшими модулями Xcode.
Сначала откройте файл ios/RNNavigation.xcodeproj
с помощью Xcode. Это соответствующий проект Xcode для приложения.
После открытия в навигаторе проекта (на левой панели, где перечислены файлы проекта) щелкните правой кнопкой мыши Библиотеки -> Добавить файлы в RNNavigation . В ReactNativeNavigation.xcodeproj
окне ReactNativeNavigation.xcodeproj
файлов перейдите в node_modules/react-native-navigation/lib/ios
и выберите файл ReactNativeNavigation.xcodeproj
:
Затем нажмите RNNavigation в строке TARGETS и перейдите на вкладку Build Phases . Оттуда найдите Link Binary With Libraries , разверните его, затем нажмите кнопку для добавления нового элемента:
В libReactNativeNavigation.a
модальном окне libReactNativeNavigation.a
файл libReactNativeNavigation.a
, выберите его и нажмите кнопку « Добавить» :
Затем откройте файл AppDelegate.m
и замените его содержимое следующим. Это в основном точка входа в приложение React Native (аналогично MainApplication.java
в Android). Мы обновляем его, чтобы вместо него использовался RNN:
#import "AppDelegate.h" #import <React/RCTBundleURLProvider.h> #import <React/RCTRootView.h> #import <ReactNativeNavigation/ReactNativeNavigation.h> @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; [ReactNativeNavigation bootstrap:jsCodeLocation launchOptions:launchOptions]; return YES; } @end
Вот и все для связи RNN с iOS. Но если у вас возникнут какие-либо проблемы, вам, вероятно, придется также следовать следующему разделу.
Общие проблемы
В этом разделе мы рассмотрим некоторые наиболее распространенные проблемы, с которыми вы можете столкнуться при подключении RNN к iOS.
Когда вы отредактировали и сохранили файл AppDelegate.m
, вы, вероятно, столкнетесь с этой проблемой. Это появится в красных волнистых линиях в самом файле:
! 'RCTBundleURLProvider.h' file not found
Это происходит потому, что схема React
отсутствует в вашем проекте.
Чтобы сделать его доступным, выполните следующие команды в корневом каталоге вашего проекта ( RNNavigation
):
npm install -g react-native-git-upgrade react-native-git-upgrade
Это добавит недостающие файлы в ваш проект.
Как только это будет сделано, перейдите в Product -> Scheme -> Manage Schemes , затем нажмите кнопку + , чтобы добавить новую схему. В раскрывающемся списке « Цель» выберите « Реагировать» , затем нажмите « ОК», чтобы добавить его:
После добавления убедитесь, что установлены оба флажка Показать и Общий :
Следующая проблема, с которой вы можете столкнуться, это:
'ReactNativeNavigation/ReactNativeNavigation.h' file not found.
Это означает, что он не может найти указанный файл в пути вашего проекта. Чтобы решить эту проблему, нажмите на RNNavigation в ЦЕЛЯХ . Затем нажмите « Настройки компоновки» и под ним, убедитесь, что в качестве фильтра используется « Все» и « Комбинировано», а затем поищите заголовок в строке поиска. Дважды щелкните « Пути поиска в заголовке» , нажмите кнопку « +» и добавьте $(SRCROOT)/../node_modules/react-native-navigation/lib/ios
в качестве пути:
Настройка AsyncStorage и векторных иконок
Процесс настройки AsyncStorage и Vector Icons очень похож на процессы установки, описанные выше, поэтому я просто сделаю ссылку на официальную документацию.
Вот инструкции для AsyncStorage:
- Настройка Android .
- Настройка iOS . Обязательно следуйте версии не-pods, так как ее легче настроить. Non-pods (сокращение от CocoaPods) — это тот, который не использует CocoaPods в качестве менеджера зависимостей. Основной файл нативного модуля будет просто связан как библиотека. Это в основном то же самое, что мы делали с файлом
libReactNativeNavigation.a
ранее.
А вот инструкция для векторных иконок. Чтобы упростить задачу, просто следуйте инструкциям по добавлению всей папки Fonts в проект Xcode:
- Настройка Android . Обязательно следуйте инструкциям для getImageSource , потому что мы будем использовать это позже для загрузки значков.
- Настройка iOS .
Примечание: вы всегда можете проверить репозиторий GitHub, если заблудились.
Хорошо, так что это было много ручной настройки. Если вам действительно нужен RNN в вашем проекте, тогда это необходимо. Каждый раз, когда вы вводите новую зависимость, которая имеет собственную ссылку, вы должны следовать инструкциям по ее ручной привязке. ReactApplication
react-native link
не будет работать, потому что мы заменили ReactApplication
на NavigationApplication
в MainApplication.java
, поэтому связывание теперь выполняется немного по-другому. Вы, вероятно, можете избежать неприятностей, если все еще работаете с react-native link
если точно знаете, что она делает, и можете полностью отменить то, что она сделала. Но безопаснее просто следовать инструкциям по ручному связыванию.
Сборка приложения
Теперь мы готовы наконец приступить к созданию приложения.
index.js
Сначала откройте существующий index.js
в корне каталога проекта и замените его содержимое следующим. Это служит точкой входа в приложение. Если вы заметили, нам больше не нужно регистрировать основной компонент приложения с помощью AppRegistry от React Native. Вместо этого мы теперь используем метод registerComponent()
RNN. Это связано с обновлениями, которые мы сделали ранее для MainActivity.java
и AppDelegate.m
.
Метод registerComponent()
принимает уникальное имя экрана и компонент, используемый для визуализации экрана. После регистрации мы вызываем метод registerAppLaunchedListener()
чтобы установить для корневого экрана приложения значение LoadingScreen
. Это похоже на то, что AppRegistry.registerComponent()
:
// index.js import { Navigation } from "react-native-navigation"; import Icon from "react-native-vector-icons/FontAwesome"; import Loading from "./src/screens/Loading"; // the loading screen import "./loadIcons"; // file for loading the icons to be used in the bottom tab navigation Navigation.registerComponent(`LoadingScreen`, () => Loading); Navigation.events().registerAppLaunchedListener(() => { // set the root component Navigation.setRoot({ root: { component: { name: "LoadingScreen" } } }); });
Экран загрузки
Экран загрузки служит точкой входа в приложение. Но вы можете спросить, почему экран загрузки? Почему бы не экран входа в систему вместо этого? Это связано с тем, что в нашем примере приложения есть фиктивная система входа в систему, а это означает, что сначала нам нужно определить, вошел ли пользователь в систему или нет. Использование экрана загрузки работает лучше, чем первоначальная загрузка экрана входа в систему только для того, чтобы узнать, что пользователь уже вошел в систему, поэтому мы должны перейти к его начальному экрану.
Начните с создания файла src/screens/Loading.js
и добавьте следующее:
// src/screens/Loading.js import React, { Component } from "react"; import { View, Text, ActivityIndicator, StyleSheet } from "react-native"; import { goToLogin, goToTabs } from "../../navigation"; // import the functions for loading either the login screen or the tabs screen (shows home screen by default) import AsyncStorage from "@react-native-community/async-storage";
Далее создайте сам компонент. Когда компонент монтируется, мы пытаемся получить username
вошедшего в систему, из локального хранилища. Если он существует, то мы перемещаем пользователя на вкладки, в противном случае на экран входа в систему:
export default class Loading extends Component { async componentDidMount() { const username = await AsyncStorage.getItem("username"); if (username) { goToTabs(global.icons, username); } else { goToLogin(); } } render() { // show loading indicator return ( <View style={styles.container}> <ActivityIndicator size="large" color="#0000ff" /> </View> ); } } // const styles = StyleSheet.create({ container: { flex: 1, justifyContent: "center", alignItems: "center" } });
global.icons
внимание, что в приведенном выше коде мы global.icons
в качестве аргумента функции goToTabs()
. Это значение задается в loadIcons.js
который мы импортировали из файла index.js
ранее. Его работа заключается в загрузке значков, которые будут использоваться для нижних вкладок, как вы увидите позже.
navigation.js
Здесь мы регистрируем все экраны приложения и объявляем наши функции навигации для перехода между экраном входа и экранами с вкладками:
// navigation.js import { Navigation } from "react-native-navigation"; import Login from "./src/screens/Login"; import ForgotPassword from "./src/screens/ForgotPassword"; import Home from "./src/screens/Home"; import Feed from "./src/screens/Feed"; import Gallery from "./src/screens/Gallery"; Navigation.registerComponent(`LoginScreen`, () => Login); Navigation.registerComponent(`ForgotPasswordScreen`, () => ForgotPassword); Navigation.registerComponent(`HomeScreen`, () => Home); Navigation.registerComponent(`FeedScreen`, () => Feed); Navigation.registerComponent(`GalleryScreen`, () => Gallery);
Функция goToLogin()
создает навигацию по стеку. В RNN эти типы навигации называются «Макеты». В настоящее время их всего три: стеки, вкладки и ящики. В этом руководстве мы будем использовать только стеки и вкладки, но вот краткий обзор каждого из них:
- Стек : каждый новый экран, к которому вы переходите, располагается поверх текущего. Поэтому, когда вы возвращаетесь к предыдущему экрану, идея состоит в том, чтобы просто «вытолкнуть» текущий экран из стека. Мы будем использовать стековую навигацию для перемещения между экраном входа в систему и экраном ForgotPassword.
- Вкладка : доступ к каждому экрану осуществляется через нижнюю вкладку навигации. На каждой вкладке есть значок и текст, чтобы описать экран, к которому она ведет пользователя. Этот тип навигации обычно используется, если в приложении имеется два или более основных экрана. Наличие нижней вкладки навигации обеспечивает легкий доступ между этими экранами. Мы будем использовать навигацию по вкладкам для перехода между экранами «Дом», «Галерея» и «Лента».
- Выдвижной ящик : также называется боковым меню. Это называется «ящик», потому что он обычно скрыт в значке гамбургера и показывает только меню под ним при нажатии.
Возвращаясь к коду, мы только добавили экран входа в систему как дочерний элемент стековой навигации, хотя экран ForgotPassword также является его частью. Как упоминалось ранее, мы будем использовать стековую навигацию для перемещения между экраном входа в систему и экраном ForgotPassword. Тем не менее, мы только добавили экран входа в систему как ребенок. Добавление этого просто сделает его в качестве экрана по умолчанию для стека. При навигации по стеку вы должны добавить только начальный экран для этого конкретного стека в качестве дочернего, как вы увидите позже.
Минимальное требование для ребенка — добавить свойство name
для каждого экрана. Это имя экрана, который будет использоваться для рендеринга. Это должно быть то же имя, которое вы использовали при регистрации компонента:
export const goToLogin = () => Navigation.setRoot({ root: { stack: { // create a stack navigation id: "stackMain", children: [ { component: { name: "LoginScreen" } } ] } } });
Примечание: указывать идентификатор для навигации не требуется, но это хорошая практика, особенно если вы знаете, что в своем приложении вы начнете использовать один и тот же тип макета несколько раз.
Затем добавьте goToTabs()
. В отличие от предыдущей функции, она принимает два аргумента: icons
и username
. icons
— это массив значков, которые будут использоваться для отдельных вкладок, а username
— это имя пользователя, который вошел в систему. На этот раз мы используем навигацию bottomTabs
. Как следует из названия, это позволяет пользователю перемещаться между экранами, используя нижние вкладки. Вы можете создавать нижние вкладки, используя следующий формат :
const iconColor = "#444"; const selectedIconColor = "#0089da"; export const goToTabs = (icons, username) => { Navigation.setRoot({ root: { bottomTabs: { // create a bottom tabs navigation id: "bottomTabsMain", children: [ { component: { name: "HomeScreen", options: { bottomTab: { fontSize: 11, text: "Home", icon: icons[0], iconColor, selectedIconColor } }, // pass the username as a navigation prop to the Home screen passProps: { username } } }, { component: { name: "GalleryScreen", options: { bottomTab: { fontSize: 11, text: "Gallery", icon: icons[1], iconColor, selectedIconColor } } } }, { component: { name: "FeedScreen", options: { bottomTab: { fontSize: 11, text: "Feed", icon: icons[2], iconColor, selectedIconColor } } } } ] } } }); };
Как вы видели из приведенного выше кода, он в значительной степени использует тот же формат, что и стековая навигация. Единственное отличие состоит в том, что на этот раз мы также указываем свойство options
для отдельного bottomTab
. Эти параметры в основном используются для настройки стилей отдельной вкладки. Они говорят сами за себя, поэтому я не буду вдаваться в подробности, но я просто хочу объяснить свойство icon
. По умолчанию это принимает локальное изображение, которое требуется вызовом require('./path/to/image.png')
. Но поскольку мы уже установили векторные иконки, мы могли бы использовать их вместо источника иконок. Единственная проблема заключается в том, что мы не можем предоставить компонент React в качестве значения для icon
потому что он ожидает ресурс. Параметр icons
принимает массив значков ресурсов, и это то, что мы используем вместо этого. Вы узнаете, как мы их загружаем, в следующем разделе.
Примечание. Дополнительные параметры стиля для нижних вкладок можно найти в официальной документации по стилю . Просто поищите bottomTabs
или bottomTab
.
loadIcons.js
Вот код для файла loadIcons
который мы импортировали в файл index.js
ранее. Это использует значки от FontAwesome . Здесь мы используем метод getImageSource()
из Vector Icons, чтобы получить реальный ресурс изображения. Это позволяет нам использовать его как значок для нижних вкладок:
// loadIcons.js import Icon from "react-native-vector-icons/FontAwesome"; (function() { Promise.all([ Icon.getImageSource("home", 11), // name of icon, size Icon.getImageSource("image", 11), Icon.getImageSource("rss-square", 11) ]).then(async values => { global.icons = values; // make it available globally so we don't need to load it again }); })();
Экран входа
Экран входа в систему является экраном по умолчанию, который пользователь увидит, если он не вошел в систему. Отсюда они могут войти в систему, введя свое имя пользователя или щелкнув забытый пароль, чтобы просмотреть экран для сброса пароля. Как упомянуто ранее, все это просто подделано, и никакой подлинный код аутентификации не используется:
// src/screens/Login.js import React, { Component } from "react"; import { Navigation } from "react-native-navigation"; import { View, Text, TextInput, Button, TouchableOpacity, StyleSheet } from "react-native"; import AsyncStorage from "@react-native-community/async-storage"; import { goToTabs } from "../../navigation"; export default class Login extends Component { static get options() { return { topBar: { visible: false // need to set this because screens in a stack navigation have a header by default } }; } state = { username: "" }; render() { return ( <View style={styles.wrapper}> <View style={styles.container}> <View style={styles.main}> <View style={styles.fieldContainer}> <Text style={styles.label}>Enter your username</Text> <TextInput onChangeText={username => this.setState({ username })} style={styles.textInput} /> </View> <Button title="Login" color="#0064e1" onPress={this.login} /> <TouchableOpacity onPress={this.goToForgotPassword}> <View style={styles.center}> <Text style={styles.link_text}>Forgot Password</Text> </View> </TouchableOpacity> </View> </View> </View> ); } // next: add login code } // const styles = StyleSheet.create({ wrapper: { flex: 1 }, container: { flex: 1, alignItems: "center", justifyContent: "center", padding: 20 }, fieldContainer: { marginTop: 20 }, label: { fontSize: 16 }, textInput: { height: 40, marginTop: 5, marginBottom: 10, borderColor: "#ccc", borderWidth: 1, backgroundColor: "#eaeaea", padding: 5 } });
Вот код для входа. Это просто сохраняет username
в локальном хранилище и перемещает пользователя на экраны с вкладками:
login = async () => { const { username } = this.state; if (username) { await AsyncStorage.setItem("username", username); goToTabs(global.icons, username); } };
Наконец, вот код для перехода на другой экран с помощью стековой навигации. Просто вызовите метод Navigation.push()
и передайте идентификатор текущего экрана в качестве первого аргумента, а экран, к которому вы хотите перейти, — как второй. name
должно быть тем же, которое вы использовали, когда вызывали Navigation.registerComponent()
в файле navigation.js
ранее:
goToForgotPassword = () => { Navigation.push(this.props.componentId, { component: { name: "ForgotPasswordScreen" } }); };
Экран забытого пароля
Как упоминалось ранее, этот экран просто используется как заполнитель для демонстрации стековой навигации. Убедитесь, что topBar
установлен в visible
, потому что именно там находится кнопка «Назад» для возврата на экран входа в систему:
// src/screens/ForgotPassword.js import React, { Component } from "react"; import { View, Text, TextInput, Button, StyleSheet } from "react-native"; export default class ForgotPassword extends Component { static get options() { return { topBar: { visible: true, // visible title: { text: "Forgot Password" } } }; } state = { email: "" }; render() { return ( <View style={styles.wrapper}> <View style={styles.container}> <View style={styles.main}> <View style={styles.fieldContainer}> <Text style={styles.label}>Enter your email</Text> <TextInput onChangeText={email => this.setState({ email })} style={styles.textInput} /> </View> <Button title="Send Email" color="#0064e1" onPress={this.sendEmail} /> </View> </View> </View> ); } // sendEmail = async () => {}; } // const styles = StyleSheet.create({ wrapper: { flex: 1 }, container: { flex: 1, alignItems: "center", justifyContent: "center", padding: 20 }, fieldContainer: { marginTop: 20 }, label: { fontSize: 16 }, textInput: { height: 40, marginTop: 5, marginBottom: 10, borderColor: "#ccc", borderWidth: 1, backgroundColor: "#eaeaea", padding: 5 } });
Вы также можете иметь отдельную кнопку для возврата к предыдущему экрану. Все, что вам нужно сделать, это вызвать метод Navigation.pop()
:
Navigation.pop(this.props.componentId);
Домашний экран
Главный экран — это экран по умолчанию для навигации с вкладками, поэтому пользователь увидит его по умолчанию при входе в систему. На этом экране отображается имя пользователя, которое было передано в качестве реквизита навигации, а также кнопка выхода из системы. Нажав кнопку выхода, вы просто удалите username
из локального хранилища и вернетесь обратно к экрану входа в систему:
// src/screens/Home.js import React, { Component } from "react"; import { View, Text, Button, StyleSheet } from "react-native"; import Icon from "react-native-vector-icons/FontAwesome"; import AsyncStorage from "@react-native-community/async-storage"; import { goToLogin } from "../../navigation"; export default class Home extends Component { render() { const { username } = this.props; return ( <View style={styles.container}> <Text style={styles.text}>Hi {username}!</Text> <Button onPress={this.logout} title="Logout" color="#841584" /> </View> ); } // logout = async () => { await AsyncStorage.removeItem("username"); goToLogin(); }; } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: "center", alignItems: "center" }, text: { fontSize: 18, fontWeight: "bold" } });
Если вам интересно, как мы получили доступ к имени username
, мы передали его как навигационную опору из файла навигации ранее:
// navigation.js { component: { name: "HomeScreen", options: { ... }, // here: passProps: { username }, } },
Экран галереи
Экран галереи — просто экран наполнителя, поэтому мы не будем слишком углубляться в него. В основном, это просто показывает интерфейс фотогалереи:
// src/screens/Gallery.js import React, { Component } from "react"; import { View, Text, FlatList, Image, Dimensions, StyleSheet } from "react-native"; const { width } = Dimensions.get("window"); const base_width = width / 2; const images = [ { id: 1, src: require("../images/blake-richard-verdoorn-20063-unsplash.jpg") }, { id: 2, src: require("../images/casey-horner-487085-unsplash.jpg") }, { id: 3, src: require("../images/sacha-styles-XK7thML3zEQ-unsplash.jpg") }, { id: 4, src: require("../images/eberhard-grossgasteiger-1036384-unsplash.jpg") }, { id: 5, src: require("../images/justin-kauffman-449060-unsplash.jpg") }, { id: 6, src: require("../images/vincent-guth-182001-unsplash.jpg") } ]; export default class Gallery extends Component { render() { return ( <View style={styles.container}> <FlatList data={images} keyExtractor={(item, index) => item.id.toString()} numColumns={2} renderItem={this.renderImage} /> </View> ); } // renderImage = ({ item }) => { return ( <Image source={item.src} style={{ width: base_width, height: 250 }} /> ); }; } const styles = StyleSheet.create({ container: { flex: 1 } });
Экран подачи
Как и экран «Галерея», экран «Лента» также является наполнителем. Он просто показывает интерфейс новостной ленты:
// src/screens/Feed.js import React, { Component } from "react"; import { View, Text, FlatList, Image, TouchableOpacity, StyleSheet } from "react-native"; const news_items = [ { id: 1, title: "The HTML Handbook", summary: "HTML is the foundation of the marvel called the Web. Discover all you need to know about it in this handy handbook!", image: require("../images/amanda-phung-1281331-unsplash.jpg") }, { id: 2, title: "Angular RxJs In-Depth", summary: "In this tutorial, we'll learn to use the RxJS 6 library with Angular 6 or Angular 7...", image: require("../images/daniil-silantev-318853-unsplash.jpg") }, { id: 3, title: "How to Create Code Profiles in VS Code", summary: "This post piggybacks off of the work done by @avanslaars who is a fellow instructor at egghead.io....", image: require("../images/vincent-van-zalinge-38358-unsplash.jpg") } ]; export default class Feed extends Component { render() { return ( <View style={styles.container}> <FlatList data={news_items} keyExtractor={(item, index) => item.id.toString()} renderItem={this.renderItem} /> </View> ); } // renderItem = ({ item }) => { return ( <TouchableOpacity onPress={this.goToNews}> <View style={styles.news_item}> <View style={styles.news_text}> <View style={styles.text_container}> <Text style={styles.title}>{item.title}</Text> <Text>{item.summary}</Text> </View> </View> <View style={styles.news_photo}> <Image source={item.image} style={styles.photo} /> </View> </View> </TouchableOpacity> ); }; // goToNews = () => {}; } // const styles = StyleSheet.create({ container: { flex: 1 }, news_item: { flex: 1, flexDirection: "row", paddingRight: 20, paddingLeft: 20, paddingTop: 20, paddingBottom: 20, borderBottomWidth: 1, borderBottomColor: "#E4E4E4" }, news_text: { flex: 2, flexDirection: "row", padding: 15 }, title: { fontSize: 28, fontWeight: "bold", color: "#000", fontFamily: "georgia" }, news_photo: { flex: 1, justifyContent: "center", alignItems: "center" }, photo: { width: 120, height: 120 } });
Запуск приложения
На этом этапе вы сможете запустить приложение:
react-native run-android react-native run-ios
Попробуйте приложение и посмотрите, работает ли оно лучше, чем React Navigation (если вы использовали его раньше).
Заключение и последующие шаги
Из этого руководства вы узнали, как использовать библиотеку React Native Navigation. В частности, вы узнали, как настроить React Native Navigation и использовать навигацию по стеку и вкладкам. Вы также узнали, как загружать значки из React Native Vector Icons вместо использования значков изображений.
В качестве следующего шага вы можете проверить, как можно настроить анимацию , как реализовать навигацию по боковому меню или просмотреть примеры различных типов макетов .
Если вы все еще не уверены в том, какую библиотеку навигации использовать для вашего следующего проекта, обязательно ознакомьтесь со следующим постом: Реагируйте с навигацией против Реагируйте с собственной навигацией: что вам подходит?
Вы можете найти исходный код примера приложения в этом репозитории GitHub .