React Native中嵌套路由和模态框

1,126 阅读4分钟

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

全局模态框

类似于Alert.alert("...") ,显示临时阻止于主视图交互的内容

创建堆栈模态框

import * as React from 'react';
import { View, Text, Button } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

// 首页
function HomeScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text style={{ fontSize: 30 }}>This is the home screen!</Text>
      <Button
        onPress={() => navigation.navigate('MyModal')}
        title="Open Modal"
      />
    </View>
  );
}

// 模态框
function ModalScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text style={{ fontSize: 30 }}>This is a modal!</Text>
      <Button onPress={() => navigation.goBack()} title="Dismiss" />
    </View>
  );
}

// 详情页
function DetailsScreen() {
  return (
    <View>
      <Text>Details</Text>
    </View>
  );
}

// 创建堆栈式导航
const MainStack = createStackNavigator();
const RootStack = createStackNavigator();

function MainStackScreen() {
  return (
    <MainStack.Navigator>
      <MainStack.Screen name="Home" component={HomeScreen} />
      <MainStack.Screen name="Details" component={DetailsScreen} />
    </MainStack.Navigator>
  );
}

function App() {
  return (
    <NavigationContainer>
      <RootStack.Navigator mode="modal" headerMode="none">
        <RootStack.Screen name="Main" component={MainStackScreen} />
        <RootStack.Screen name="MyModal" component={ModalScreen} />
      </RootStack.Navigator>
    </NavigationContainer>
  );
}

export default App;

上面代码中,MainStackScreen组件作为RootstackScreen内的屏幕! 通过这样做,我们嵌套另一个堆栈导航器内的堆栈导航器。因为我们希望为模态使用不同的转换方式。 由于RootStackScreen呈现堆栈导航器并具有自己的标题和一些其他属性。

tree-c522585f78ec7e8aeaa7d09d3568012a.png

标签导航

文档传送门

很多时候一个应用都会或多或少的出现标签导航,比如底部一排快捷标签导航。

$ npm install @react-navigation/bottom-tabs
or 
$ yarn add @react-navigation/bottom-tabs

常用属性

  • tabBarIcon 设置标签导航的图标,包含是否选中、颜色和字体大小等等
  • tabBarOptions
    • activeTintColor 选中时的标题字体颜色
    • inactiveTintColor 未选中时的标题字体颜色
    • size 字体大小
  • createBottomTabNavigator 创建标签导航按钮,返回两个参数Navigator和`Screen``
  • tabBarBadge 设置徽章,接受字符串或者数字的文本
  • tabBarBadgeStyle 设置徽章样式

底部导航案例

实现底部两个标签导航HomeScreenSettingsScreen

import * as React from 'react';
import { Text, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

function HomeScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Home!</Text>
    </View>
  );
}

function SettingsScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Settings!</Text>
    </View>
  );
}

// 创建标签导航
const Tab = createBottomTabNavigator();

export default function App() {
  return (
    <NavigationContainer>
      <Tab.Navigator>
        <Tab.Screen name="Home" component={HomeScreen} />
        <Tab.Screen name="Settings" component={SettingsScreen} />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

自定义标签导航的样式

比如自定义文字颜色、文字大小、粗细、字体、选中和未选中时的图标

import * as React from 'react';
import { Text, View } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { NavigationContainer } from '@react-navigation/native';

function HomeScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Home!</Text>
    </View>
  );
}

function SettingsScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Settings!</Text>
    </View>
  );
}

const Tab = createBottomTabNavigator();

export default function App() {
  return (
    <NavigationContainer>
      <Tab.Navigator
        screenOptions={({ route }) => ({
    			// 设置图标
          tabBarIcon: ({ focused, color, size }) => {
            let iconName;

            if (route.name === 'Home') {
              iconName = focused ? 'ios-information-circle' : 'ios-information-circle-outline';
            } else if (route.name === 'Settings') {
              iconName = focused ? 'ios-list-box' : 'ios-list';
            }

            // You can return any component that you like here!
            return <Ionicons name={iconName} size={size} color={color} />;
          },
        })}
        tabBarOptions={{
          activeTintColor: 'tomato',
          inactiveTintColor: 'gray',
        }}
      >
        <Tab.Screen name="Home" component={HomeScreen} />
        <Tab.Screen name="Settings" component={SettingsScreen} />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

添加徽章图标

import * as React from 'react';
import { Text, View } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

function HomeScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Home!</Text>
    </View>
  );
}

function SettingsScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Settings!</Text>
    </View>
  );
}

const Tab = createBottomTabNavigator();

export default function App() {
  return (
    <NavigationContainer>
      <Tab.Navigator
        screenOptions={({ route }) => ({
          tabBarIcon: ({ focused, color, size }) => {
            if (route.name === 'Home') {
              return (
                <Ionicons
                  name={
                    focused
                      ? 'ios-information-circle' // 选中时的图标
                      : 'ios-information-circle-outline' // 未选中时的图标
                  }
                  size={size}
                  color={color}
                />
              );
            } else if (route.name === 'Settings') {
              return (
                <Ionicons
                  name={focused ? 'ios-list-box' : 'ios-list'}
                  size={size}
                  color={color}
                />
              );
            }
          },
        })}
        tabBarOptions={{
          activeTintColor: 'tomato',
          inactiveTintColor: 'gray',
        }}
      >
        <Tab.Screen 
            name="Home" 
            component={HomeScreen} 
            options={{ 
              // 设置徽章
           		tabBarBadge: 3 
        		}} 
         />
        <Tab.Screen name="Settings" component={SettingsScreen} />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

效果图如下:

image.png

事件跳转

类似于web中的a标签跳转

function HomeScreen({ navigation }) {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Home!</Text>
      <Button
        title="Go to Settings"
        onPress={() => navigation.navigate('Settings')}
      />
    </View>
  );
}

function SettingsScreen({ navigation }) {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Settings!</Text>
      <Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
    </View>
  );
}

抽屉式导航

效果如下:

常用属性 文档

  • createDrawerNavigator 创建一个drawer导航,返回两个参数Navigator和Screen
  • title
  • drawerLabel
  • drawerLabel
  • swiperEnabled
  • gestureEnabled
  • header
  • headerShow
  • headerTitle
  • headerTitleAlign
  • headerTitleAllowFontScaling
  • headerTitleStyle
  • headerLeft
  • headerPressColorAndroid
  • headerStyle
  • headerStatusBarHeight
  • unmountOnBlur

事件

  • drawerOpen. 此事件会在drawer打开时触发

    avigation.addListener('drawerOpen', (event) => {
        // Do something
    });
    
  • drawerClose 此事件会在drawer关闭时触发

    navigation.addListener('drawerClose', (event) => {
        // Do something
    });
    
  • openDrawer() 打开drawer方法

    navigation.openDrawer();
    
  • closeDrawer() 关闭drawer方法

    navigation.closeDrawer();
    
  • toggleDrawer() 状态切换;如果关闭,打开抽屉窗格,如果打开,关闭抽屉窗格。

    navigation.toggleDrawer();
    
  • jumpTo() 跳转到抽屉导航器中的现有屏幕。可接受两个参数,第一个是被跳转到的屏幕组件的名称,第二个参数是可选传递参数对象

    navigation.jumpTo('Profile', { owner: 'Satya' });
    
  • useIsDrawerOpen() 检测drawer是否打开,只能在函数组件中使用;

示例

// drawer.tsx
import React, { Component } from 'react';
import { createDrawerNavigator } from '@react-navigation/drawer';
import Detail from '../detail/Detail';
import CustomTitleBar from '../custom-title-bar/CustomTitleBar';

export type DrawerParamList = {
    CustomTitleBar: undefined;
    Detail: undefined;
};

const Drawers = createDrawerNavigator<DrawerParamList>();
export default class Drawer extends Component {
    render() {
        return (
            <Drawers.Navigator initialRouteName="CustomTitleBar">
                <Drawers.Screen
                    name="CustomTitleBar"
                    component={CustomTitleBar}
                />
                <Drawers.Screen name="Detail" component={Detail} />
            </Drawers.Navigator>
        );
    }
}
// CustomTitlebar
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { Button } from '@ant-design/react-native';
import { DrawerNavigationProp } from '@react-navigation/drawer';
import { DrawerParamList } from '../../pages/drawer/Drawer';

export type IProps = {
    navigation: DrawerNavigationProp<DrawerParamList>;
};

interface IState {
    isOpen: boolean;
}

export default class CustomTitleBar extends Component<IProps> {
    state: IState = {
        isOpen: false,
    };

  	// 响应事件drawer开闭事件
    handleClickOpenOrClose() {
        const { navigation } = this.props;
        navigation.toggleDrawer();
    }
  
    render() {
        const { isOpen } = this.state;
        return (
            <View>
                <Text>自定义头部组件</Text>
                <Button onPress={() => this.handleClickOpenOrClose()}>
                    {isOpen ? 'close' : 'open'} drawer
                </Button>
            </View>
        );
    }
}

标签按钮导航

屏幕底部的一个简单的标签栏,可让您在不同的路由之间切换。 路线懒惰初始化 - 它们的屏幕组件未安装,直到它们首先聚焦。

@react-navigation/bottom-tabs依赖于@react-navigation/native

文档: 官网

顶部标签切换

可通过点击选项卡或水平滑动来在不同的路由之间切换。 过渡默认情况下会动画。如下:

import React, { Component } from 'react';
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
import Home from '../home/Home';
import List from '../list/List';
import Found from '../found/Found';

export type TopTabParmasList = {
    Home: undefined;
    List: undefined;
    Found: undefined;
};
const Tab = createMaterialTopTabNavigator<TopTabParmasList>();

export default class TopTab extends Component {
    render() {
        return (
            <Tab.Navigator>
                <Tab.Screen name="Home" component={Home} />
                <Tab.Screen name="List" component={List} />
                <Tab.Screen name="Found" component={Found} />
            </Tab.Navigator>
        );
    }
}