mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-12 09:21:09 +08:00
feat: add theme support
This commit is contained in:
@@ -1,31 +1,44 @@
|
||||
import * as React from 'react';
|
||||
import { registerRootComponent } from 'expo';
|
||||
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
|
||||
import { createStackNavigator } from 'react-navigation';
|
||||
import { createAppContainer } from '@react-navigation/native';
|
||||
import { View, TouchableOpacity, StyleSheet } from 'react-native';
|
||||
import {
|
||||
Assets as StackAssets,
|
||||
createStackNavigator,
|
||||
} from 'react-navigation-stack';
|
||||
import { Themed, createAppContainer } from '@react-navigation/native';
|
||||
import { ThemeColors, useTheme } from '@react-navigation/core';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { Asset } from 'expo-asset';
|
||||
|
||||
import BottomTabs from './src/BottomTabs';
|
||||
import MaterialTopTabs from './src/MaterialTopTabs';
|
||||
|
||||
class Home extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<TouchableOpacity
|
||||
style={styles.item}
|
||||
onPress={() => this.props.navigation.push('BottomTabs')}
|
||||
>
|
||||
<Text>Bottom tabs</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={styles.item}
|
||||
onPress={() => this.props.navigation.push('MaterialTopTabs')}
|
||||
>
|
||||
<Text>Material top tabs</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
// Load the back button etc
|
||||
Asset.loadAsync(StackAssets);
|
||||
|
||||
const Home = props => {
|
||||
let theme = useTheme();
|
||||
|
||||
return (
|
||||
<View>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.8}
|
||||
style={theme === 'dark' ? styles.itemDark : styles.itemLight}
|
||||
onPress={() => props.navigation.push('BottomTabs')}
|
||||
>
|
||||
<Themed.Text>Bottom tabs</Themed.Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.8}
|
||||
style={theme === 'dark' ? styles.itemDark : styles.itemLight}
|
||||
onPress={() => props.navigation.push('MaterialTopTabs')}
|
||||
>
|
||||
<Themed.Text>Material top tabs</Themed.Text>
|
||||
</TouchableOpacity>
|
||||
<Themed.StatusBar />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const List = createStackNavigator({
|
||||
Home: {
|
||||
@@ -42,15 +55,75 @@ const List = createStackNavigator({
|
||||
},
|
||||
});
|
||||
|
||||
const App = createAppContainer(List);
|
||||
const Navigation = createAppContainer(List);
|
||||
|
||||
const App = () => {
|
||||
let [theme, setTheme] = React.useState('light');
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Navigation theme={theme} />
|
||||
<View style={styles.buttonContainer}>
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.button,
|
||||
{
|
||||
backgroundColor: ThemeColors[theme].bodyContent,
|
||||
borderColor: ThemeColors[theme].bodyBorder,
|
||||
shadowColor: ThemeColors[theme].label,
|
||||
},
|
||||
]}
|
||||
onPress={() => {
|
||||
setTheme(theme === 'light' ? 'dark' : 'light');
|
||||
}}
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
name="theme-light-dark"
|
||||
size={30}
|
||||
color={ThemeColors[theme].label}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = {
|
||||
item: {
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
buttonContainer: {
|
||||
position: 'absolute',
|
||||
bottom: 60,
|
||||
right: 20,
|
||||
},
|
||||
button: {
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 2,
|
||||
},
|
||||
shadowOpacity: 0.4,
|
||||
shadowRadius: 2,
|
||||
borderRadius: 25,
|
||||
width: 50,
|
||||
height: 50,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
elevation: 5,
|
||||
borderWidth: 1,
|
||||
},
|
||||
itemLight: {
|
||||
padding: 16,
|
||||
backgroundColor: '#fff',
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: '#eee',
|
||||
},
|
||||
itemDark: {
|
||||
padding: 16,
|
||||
backgroundColor: ThemeColors.dark.bodyContent,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: ThemeColors.dark.bodyBorder,
|
||||
},
|
||||
};
|
||||
|
||||
registerRootComponent(App);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"expo": {
|
||||
"name": "React Navigation Tabs Example",
|
||||
"description": "Demonstrates the various capabilities of react-navigation-tabs: https://github.com/react-navigation/tabs",
|
||||
"description": "Demonstrates the various capabilities of react-navigation-tabs: https://github.com/react-navigation/react-navigation-tabs",
|
||||
"slug": "react-navigation-tabs-demos",
|
||||
"sdkVersion": "32.0.0",
|
||||
"sdkVersion": "33.0.0",
|
||||
"version": "1.0.0",
|
||||
"primaryColor": "#2196f3",
|
||||
"packagerOpts": {
|
||||
|
||||
@@ -9,26 +9,27 @@
|
||||
},
|
||||
"main": "App.js",
|
||||
"dependencies": {
|
||||
"@expo/vector-icons": "^10.0.1",
|
||||
"@react-navigation/core": "^3.4.0",
|
||||
"@react-navigation/native": "^3.3.0",
|
||||
"expo": "32.0.6",
|
||||
"react": "16.5.0",
|
||||
"react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
|
||||
"@expo/vector-icons": "^10.0.0",
|
||||
"@react-navigation/core": "3.5.0-alpha.7",
|
||||
"@react-navigation/native": "^3.6.0-alpha.1",
|
||||
"expo": "^33.0.7",
|
||||
"expo-asset": "^6.0.0",
|
||||
"expo-constants": "~5.0.1",
|
||||
"react": "16.8.3",
|
||||
"react-native": "https://github.com/expo/react-native/archive/sdk-33.0.0.tar.gz",
|
||||
"react-native-safe-area-view": "^0.13.1",
|
||||
"react-native-screens": "^1.0.0-alpha.22",
|
||||
"react-native-tab-view": "^2.0.3",
|
||||
"react-navigation": "^3.6.0"
|
||||
"react-native-screens": "1.0.0-alpha.22",
|
||||
"react-native-tab-view": "^1.2.0",
|
||||
"react-navigation-stack": "1.4.0-alpha.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-module-resolver": "^3.2.0",
|
||||
"babel-preset-expo": "^5.1.1",
|
||||
"babel-preset-expo": "^5.0.0",
|
||||
"glob-to-regexp": "^0.4.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"**/@expo/vector-icons": "10.0.1",
|
||||
"**/@react-navigation/core": "3.4.0",
|
||||
"**/hoist-non-react-statics": "2.5.0",
|
||||
"**/react-native-tab-view": "2.0.3"
|
||||
"**/react-native-tab-view": "1.2.0",
|
||||
"**/prop-types": "15.6.1"
|
||||
}
|
||||
}
|
||||
|
||||
78
packages/tabs/example/src/Shared/PhotoGrid.js
Normal file
78
packages/tabs/example/src/Shared/PhotoGrid.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import * as React from 'react';
|
||||
import { View, Image, ScrollView, Dimensions, StyleSheet } from 'react-native';
|
||||
import { withNavigation } from '@react-navigation/core';
|
||||
|
||||
@withNavigation
|
||||
class NavigationAwareScrollView extends React.Component {
|
||||
componentDidMount() {
|
||||
this.props.navigation.addListener('willFocus', () => {
|
||||
this._isFocused = true;
|
||||
});
|
||||
|
||||
this.props.navigation.addListener('willBlur', () => {
|
||||
this._isFocused = false;
|
||||
});
|
||||
|
||||
this.props.navigation.addListener('refocus', () => {
|
||||
if (this._isFocused) {
|
||||
this._component.scrollTo({ x: 0, y: 0 });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setNativeProps(props) {
|
||||
this._component.setNativeProps(props);
|
||||
}
|
||||
|
||||
_setComponentRef(c) {
|
||||
this._component = c;
|
||||
}
|
||||
|
||||
getNode() {
|
||||
return this._component;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ScrollView
|
||||
{...this.props}
|
||||
ref={view => {
|
||||
this._component = view;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function PhotoGrid({ id }) {
|
||||
const PHOTOS = Array.from({ length: 24 }).map(
|
||||
(_, i) => `https://unsplash.it/300/300/?random&__id=${id}${i}`
|
||||
);
|
||||
|
||||
return (
|
||||
<NavigationAwareScrollView contentContainerStyle={styles.content}>
|
||||
{PHOTOS.map(uri => (
|
||||
<View key={uri} style={styles.item}>
|
||||
<Image source={{ uri }} style={styles.photo} />
|
||||
</View>
|
||||
))}
|
||||
</NavigationAwareScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
content: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
padding: 4,
|
||||
},
|
||||
item: {
|
||||
height: Dimensions.get('window').width / 2,
|
||||
width: '50%',
|
||||
padding: 4,
|
||||
},
|
||||
photo: {
|
||||
flex: 1,
|
||||
resizeMode: 'cover',
|
||||
},
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@ import {
|
||||
Keyboard,
|
||||
Platform,
|
||||
} from 'react-native';
|
||||
import { ThemeColors, ThemeContext } from '@react-navigation/core';
|
||||
import { SafeAreaView } from '@react-navigation/native';
|
||||
|
||||
import CrossFadeIcon from './CrossFadeIcon';
|
||||
@@ -20,12 +21,17 @@ type LabelPosition =
|
||||
| Position
|
||||
| ((options: { deviceOrientation: Orientation }) => Position);
|
||||
|
||||
export type ThemedColor = {
|
||||
light: string,
|
||||
dark: string,
|
||||
};
|
||||
|
||||
export type TabBarOptions = {
|
||||
keyboardHidesTabBar: boolean,
|
||||
activeTintColor?: string,
|
||||
inactiveTintColor?: string,
|
||||
activeBackgroundColor?: string,
|
||||
inactiveBackgroundColor?: string,
|
||||
activeTintColor?: string | ThemedColor,
|
||||
inactiveTintColor?: string | ThemedColor,
|
||||
activeBackgroundColor?: string | ThemedColor,
|
||||
inactiveBackgroundColor?: string | ThemedColor,
|
||||
allowFontScaling: boolean,
|
||||
showLabel: boolean,
|
||||
showIcon: boolean,
|
||||
@@ -93,11 +99,19 @@ class TouchableWithoutFeedbackWrapper extends React.Component<*> {
|
||||
}
|
||||
|
||||
class TabBarBottom extends React.Component<Props, State> {
|
||||
static contextType = ThemeContext;
|
||||
|
||||
static defaultProps = {
|
||||
keyboardHidesTabBar: true,
|
||||
activeTintColor: '#007AFF',
|
||||
activeTintColor: {
|
||||
light: '#007AFF',
|
||||
dark: '#fff',
|
||||
},
|
||||
inactiveTintColor: {
|
||||
light: '#8e8e93',
|
||||
dark: '#7f7f7f',
|
||||
},
|
||||
activeBackgroundColor: 'transparent',
|
||||
inactiveTintColor: '#8E8E93',
|
||||
inactiveBackgroundColor: 'transparent',
|
||||
showLabel: true,
|
||||
showIcon: true,
|
||||
@@ -166,23 +180,62 @@ class TabBarBottom extends React.Component<Props, State> {
|
||||
});
|
||||
};
|
||||
|
||||
_getActiveTintColor = () => {
|
||||
let { activeTintColor } = this.props;
|
||||
if (!activeTintColor) {
|
||||
return;
|
||||
} else if (typeof activeTintColor === 'string') {
|
||||
return activeTintColor;
|
||||
}
|
||||
|
||||
return activeTintColor[this.context];
|
||||
};
|
||||
|
||||
_getInactiveTintColor = () => {
|
||||
let { inactiveTintColor } = this.props;
|
||||
if (!inactiveTintColor) {
|
||||
return;
|
||||
} else if (typeof inactiveTintColor === 'string') {
|
||||
return inactiveTintColor;
|
||||
}
|
||||
|
||||
return inactiveTintColor[this.context];
|
||||
};
|
||||
|
||||
_getActiveBackgroundColor = () => {
|
||||
let { activeBackgroundColor } = this.props;
|
||||
if (!activeBackgroundColor) {
|
||||
return;
|
||||
} else if (typeof activeBackgroundColor === 'string') {
|
||||
return activeBackgroundColor;
|
||||
}
|
||||
|
||||
return activeBackgroundColor[this.context];
|
||||
};
|
||||
|
||||
_getInactiveBackgroundColor = () => {
|
||||
let { inactiveBackgroundColor } = this.props;
|
||||
if (!inactiveBackgroundColor) {
|
||||
return;
|
||||
} else if (typeof inactiveBackgroundColor === 'string') {
|
||||
return inactiveBackgroundColor;
|
||||
}
|
||||
|
||||
return inactiveBackgroundColor[this.context];
|
||||
};
|
||||
|
||||
_renderLabel = ({ route, focused }) => {
|
||||
const {
|
||||
activeTintColor,
|
||||
inactiveTintColor,
|
||||
labelStyle,
|
||||
showLabel,
|
||||
showIcon,
|
||||
allowFontScaling,
|
||||
} = this.props;
|
||||
const { labelStyle, showLabel, showIcon, allowFontScaling } = this.props;
|
||||
|
||||
if (showLabel === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const activeTintColor = this._getActiveTintColor();
|
||||
const inactiveTintColor = this._getInactiveTintColor();
|
||||
const label = this.props.getLabelText({ route });
|
||||
const horizontal = this._shouldUseHorizontalLabels();
|
||||
const tintColor = focused ? activeTintColor : inactiveTintColor;
|
||||
const horizontal = this._shouldUseHorizontalLabels();
|
||||
|
||||
if (typeof label === 'string') {
|
||||
return (
|
||||
@@ -214,20 +267,15 @@ class TabBarBottom extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
_renderIcon = ({ route, focused }) => {
|
||||
const {
|
||||
navigation,
|
||||
activeTintColor,
|
||||
inactiveTintColor,
|
||||
renderIcon,
|
||||
showIcon,
|
||||
showLabel,
|
||||
} = this.props;
|
||||
const { navigation, renderIcon, showIcon, showLabel } = this.props;
|
||||
if (showIcon === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const horizontal = this._shouldUseHorizontalLabels();
|
||||
|
||||
const activeTintColor = this._getActiveTintColor();
|
||||
const inactiveTintColor = this._getInactiveTintColor();
|
||||
const activeOpacity = focused ? 1 : 0;
|
||||
const inactiveOpacity = focused ? 0 : 1;
|
||||
|
||||
@@ -302,8 +350,6 @@ class TabBarBottom extends React.Component<Props, State> {
|
||||
const {
|
||||
navigation,
|
||||
keyboardHidesTabBar,
|
||||
activeBackgroundColor,
|
||||
inactiveBackgroundColor,
|
||||
onTabPress,
|
||||
onTabLongPress,
|
||||
safeAreaInset,
|
||||
@@ -312,9 +358,14 @@ class TabBarBottom extends React.Component<Props, State> {
|
||||
} = this.props;
|
||||
|
||||
const { routes } = navigation.state;
|
||||
const isDark = this.context === 'dark';
|
||||
|
||||
const activeBackgroundColor = this._getActiveBackgroundColor();
|
||||
const inactiveBackgroundColor = this._getInactiveBackgroundColor();
|
||||
|
||||
const tabBarStyle = [
|
||||
styles.tabBar,
|
||||
isDark ? styles.tabBarDark : styles.tabBarLight,
|
||||
this._shouldUseHorizontalLabels() && !Platform.isPad
|
||||
? styles.tabBarCompact
|
||||
: styles.tabBarRegular,
|
||||
@@ -408,11 +459,17 @@ const COMPACT_HEIGHT = 29;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
tabBar: {
|
||||
backgroundColor: '#fff',
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderTopColor: 'rgba(0, 0, 0, .3)',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
tabBarLight: {
|
||||
backgroundColor: ThemeColors.light.header,
|
||||
borderTopColor: ThemeColors.light.headerBorder,
|
||||
},
|
||||
tabBarDark: {
|
||||
backgroundColor: ThemeColors.dark.header,
|
||||
borderTopColor: ThemeColors.dark.headerBorder,
|
||||
},
|
||||
container: {
|
||||
left: 0,
|
||||
right: 0,
|
||||
|
||||
Reference in New Issue
Block a user