react native bottom sheet and tab bar - reactjs

I want to implement a player bar in my custom tab bar like apple music or spotify does.
For TabNavigator I have:
const TabNavigator = createBottomTabNavigator({
HomeStack,
LibraryStack,
},
{
initialRouteName: 'HomeStack',
tabBarComponent: props => <CustomTabBar {...props} />,
tabBarOptions: {
showLabel: false,
}
});
And for my custom TabBar I wanted something like that:
import React from 'react';
import { BottomTabBar } from 'react-navigation';
import BottomSheet from 'reanimated-bottom-sheet'
const CustomTabBar = props => {
return (
<React.Fragment>
<BottomSheet
snapPoints = {[450, 300, 0]}
renderContent = {this.renderInner}
renderHeader = {this.renderHeader}
/>
<BottomTabBar {...props} />
</React.Fragment>
);
};
export default CustomTabBar;
Problem is: the BottomSheet is behind the TabBar and blocks touches on the tabbar, so I cannot switch between tabs anymore. How to deal with this problem?
Kind Regards

Related

How to add and remove screens to RAM manually through code in React Native

Problem
I am new to React Native. Recently I faced with such a problem: when I move from the login screen to the signup screen, the signup screen is loaded again each time. How I can load the screen only once and store it in RAM or do something like that, to avoid rerendering? How to add and remove screens to RAM manually through code.
Environment
MacOS 13.0.1 (M1)
React Native CLI
dependencies:
#react-navigation/native: ^6.1.2,
#react-navigation/stack: ^6.3.11,
expo-linear-gradient: ^12.0.1,
react: 18.1.0,
react-native: ^0.70.6,
react-native-color-picker: ^0.6.0,
react-native-event-listeners: ^1.0.7,
react-native-gesture-handler: ^2.9.0,
react-native-localization: ^2.3.1,
react-native-paper: ^5.1.3,
react-native-paper-dropdown: ^1.0.7,
react-native-safe-area-context: ^4.4.1,
react-native-screens: ^3.19.0,
react-native-vector-icons: ^9.2.0,
Code
App.navigator.tsx
import React from "react"
import { createStackNavigator, StackView } from "#react-navigation/stack"
import { NavigationContainer } from "#react-navigation/native"
import { SignupScreen } from "./screens/signup/signup.screen"
import { LoginScreen } from "./screens/login/login.screen"
import { HomeScreen } from "./screens/home/home.screen"
import { SettingsScreen } from "./screens/settings/settings.screen"
const Stack = createStackNavigator()
// App navigation. Used to relocate between the app's screens
const AppNavigator = () => (
<NavigationContainer>
<Stack.Navigator screenOptions={{headerShown: false,
headerMode: "screen",
cardStyle: {backgroundColor: "white"}}}
initialRouteName="Login">
<Stack.Screen name="Login"
component={LoginScreen} />
<Stack.Screen name="SignUp"
component={SignupScreen} />
<Stack.Screen name="Home"
component={HomeScreen} />
<Stack.Screen name="Settings"
component={SettingsScreen} />
</Stack.Navigator>
</NavigationContainer>
)
export default AppNavigator
App.tsx
import React, {useState, useEffect} from 'react';
import {Provider as PaperProvider} from 'react-native-paper';
import { EventRegister } from 'react-native-event-listeners';
import AppNavigator from './app.navigator';
import mainAppTheme, {fontTheme} from './config/theme';
import themeContext from './config/themeContext';
import { CHANGE_THEME_LITERAL } from './config/theme';
const App = () => {
// Theme update
const [theme, setTheme] = useState(mainAppTheme)
useEffect(() => {
let eventListener = EventRegister.addEventListener(CHANGE_THEME_LITERAL, (theme) => {
setTheme(theme)
})
return () => {
EventRegister.removeEventListener(eventListener.toString())
}
})
return (
<themeContext.Provider value={theme}>
<PaperProvider theme={fontTheme}>
<AppNavigator />
</PaperProvider>
</themeContext.Provider>
);
};
export default App;
Login.screen.tsx
import React, { useContext, useState } from "react";
import { SafeAreaView, View } from "react-native";
import { Button, Card, TextInput } from "react-native-paper";
import { PasswordComponent } from "../../components/password.component";
import mainAppTheme, {appTheme} from "../../config/theme";
import themeContext from "../../config/themeContext";
import { loginScreenTrans } from "../../translations/login.screen.trans"
import { loginStyle } from "./login.style";
interface LoignScreenProps{
navigation: any
}
// Login app screen. Meets new users
export const LoginScreen = (props:LoignScreenProps) => {
// Theme update
const theme: appTheme = useContext(themeContext)
loginScreenTrans.setLanguage(theme.lang)
return(
<SafeAreaView style={loginStyle().content}>
<View style={loginStyle().view}>
<Card>
<Card.Title title="BuildIn" titleStyle={loginStyle().cardTitle}></Card.Title>
<Card.Content>
<TextInput selectionColor={theme.colors.mainAppColor}
activeUnderlineColor={theme.colors.mainAppColor}
label={loginScreenTrans.email}
keyboardType="email-address"
style={loginStyle().email} />
<PasswordComponent label={loginScreenTrans.password}
color={theme.colors.mainAppColor.toString()}
style={loginStyle().password}/>
<Button uppercase={false}
textColor={theme.colors.mainAppTextColor}>
{loginScreenTrans.forgot}
</Button>
<Button buttonColor={theme.colors.mainAppColor}
textColor={"#ffffff"}
uppercase={true}
mode="contained"
onPress={() => {props.navigation.navigate("Home");
props.navigation.reset({
index: 0,
routes: [{ name: 'Home' }]})
}}>
{loginScreenTrans.login}
</Button>
<Button uppercase={true}
textColor={mainAppTheme.colors.mainAppTextColor}
onPress={() => props.navigation.navigate("SignUp")}>
{loginScreenTrans.signup}
</Button>
</Card.Content>
</Card>
</View>
</SafeAreaView>
);
}
What i tried
Next steps changed nothing in my case:
Use enableFreeze(true) and enableScreens(true) at the top of loaded screen file and top of navigation file.
Wrap my screen in React.memo()
Set freezeOnBlur: true prop into <Stack.Navigator>
navigation.dispatch(CommonActions.reset({ routes[...]
Maybe I used it incorrectly, I would be grateful if someone could explain to me why.
But it seems that the problem is specifically in the way react navigation works. And I don't know how to set it up the way I want.

How to use React Navigation Drawer with Next.js?

I'd like to use the React Navigation v5 (Drawer navigation) with Next.js but I have a question about their integration.
In short: React Navigation (Drawer navigation) based on the React Navigation Screens component.
It conditionally renders the correct Screen.
The problem is: Next.js has it's own routing system based on the Folder structure. eg. each file in /pages folder automatically generates an appropriate route so I can't add these files as a React Navigation Screen (at least I'm not sure it's possible at all)
How to make these tools to work together and save the Next.js SSR feature?
Example of the React Navigation Drawer:
import * as React from 'react';
import { Button, View } from 'react-native';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { NavigationContainer } from '#react-navigation/native';
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
onPress={() => navigation.navigate('Notifications')}
title="Go to notifications"
/>
</View>
);
}
function NotificationsScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button onPress={() => navigation.goBack()} title="Go back home" />
</View>
);
}
const Drawer = createDrawerNavigator();
export default function App() {
return (
<NavigationContainer>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Notifications" component={NotificationsScreen} />
</Drawer.Navigator>
</NavigationContainer>
);
}
Thanks for any help!
You should use file based routing system from Nextjs on web and do your own navigation on mobile using React Navigation.
Below is my approach,
// this is how your directory might look like
- pages/
- index.tsx // this is your entry point for web
- about.tsx
App.tsx // this is your entry point for native
// pages/index.tsx
import React from 'react';
import { Text, View } from 'react-native';
const Home: React.FC = () => (
<View>
<Text>Welcome to Expo + Next.js 👋</Text>
</View>
);
export default Home;
// pages/about.tsx
import React from 'react';
import { Text, View } from 'react-native';
const About: React.FC = () => (
<View>
<Text>This is about page!</Text>
</View>
);
export default About;
Define your navigator for native app in App.tsx, it will only work on mobile so it doesn't have to be the same as what you have in pages/ folder. (actually if you only want your app run in browser, you don't need it at all.
Nextjs will handle all the route things, SSR etc... just like a normal Nextjs app when you run it in a browser.
// App.tsx
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createDrawerNavigator } from '#react-navigation/drawer';
import Home from '../pages/index';
import About from '../pages/about';
const Drawer = createDrawerNavigator();
const App: React.FC = () => (
<NavigationContainer>
<Drawer.Navigator>
<Drawer.Screen name="Home" component={Home} />
<Drawer.Screen name="About" component={About} />
</Drawer.Navigator>
</NavigationContainer>
);
export default App;
The important thing is how should you change routes when you have your navigation on native app but an automatically routing system on web?
There is a package to solve this expo-next-react-navigation, check the documentation for details! Make sure you're using the correct version of this package, if you're using React Navigation 5, you should install expo-next-react-navigation#1.1.6 at this moment.
And here is an example, it should work on both platforms,
import React from 'react';
import { FlatList, Text } from 'react-native';
import { Link } from 'expo-next-react-navigation';
const links = [
{ key: 'home', route: '' },
{ key: 'about', route: 'about' },
];
const Links: React.FC = () => (
<FlatList
data={links}
renderItem={({ item }) => (
<Link routeName={item.route}>
{item.key}
</Link>
)}
/>
);
export default Links;

admin on rest hide resource component in sidebar

I need a resource with all its configuration, but I don't want it to be showed in sidebar
You can omit the list prop for a Resource if you want to hide it in the sidebar menu.
<Resource name="posts" />
I found a different "hacky" way
You can add in your css the following to hide from the menu the resource
.MuiDrawer-root a[href^='#/resource-to-exclude'] {
display: none;
}
As explained in the documentation, you can provide your Menu component to the Admin component using it's menu prop. See
https://marmelab.com/react-admin/Admin.html#menu
Please note that this prop will be deprecated soon in favor of appLayout but you'll still use this custom menu in your custom layout anyway.
// in src/Menu.js
import React from 'react';
import { connect } from 'react-redux';
import { MenuItemLink, getResources } from 'react-admin';
import { withRouter } from 'react-router-dom';
import Responsive from '../layout/Responsive';
const Menu = ({ resources, onMenuClick, logout }) => (
<div>
{resources
.filter(resource => resource.name !== 'excluded-resource')
.map(resource => (
<MenuItemLink to={`/${resource.name}`} primaryText={resource.name} onClick={onMenuClick} />
))
}
<Responsive
small={logout}
medium={null} // Pass null to render nothing on larger devices
/>
</div>
);
const mapStateToProps = state => ({
// Rerieve all known resources
resources: getResources(state),
});
export default withRouter(connect(mapStateToProps)(Menu));
If your goal is to hide the entire sidebar, and make it not visible to the user, in your theme.js
try add the following code:
RaSidebar: {
drawerPaper: {
display: 'none',
},
},
eg.
const baseTheme = createTheme({
overrides: {
...<components you want override etc>...,
// React-admin
RaSidebar: {
drawerPaper: {
display: 'none',
},
},
},
});

react-native, how to factorize code over many tabs

I use react-native to create ios and android app
I create a test app with TabNavigator working good plus a sidemenu ( the button to open it should be in the top action bar ) and floatings actions buttons (the red circle labeled FAB on the picture). All this code is new defined on first tab : app.js.
Each tab have it's own js file with code and render.
My question is :
How to get this sidemenu,action bar and floating buttons on ALL tabs without coping all the render code and js functions over all the other tabs Js files.
when i click on a tab only the green part will change
my App.js
import React, { Component } from "react";
import {...} from "react-native";
import { TabNavigator } from "react-navigation";
import Imagetest from "./Photo";
import ListFlatlist from "./ListFlatlist";
... // importing the other file for the tabs
import styles from "./css/styles";
import SideMenu from "react-native-side-menu";
import Menu from "./Menu";
class App extends Component {
constructor(props) { ... }
...
render() {
const menu = <Menu onItemSelected={this.onMenuItemSelected} />;
return (
<SideMenu
menu={menu}
isOpen={this.state.isOpen}
onChange={isOpen => this.updateMenuState(isOpen)}
>
<View style={styles.container}>
{/* my app.js content
*/}
// this is floating buttons
<ActionButton buttonColor="rgba(231,76,60,1)">
<ActionButton.Item buttonColor='#9b59b6' title="New Task" onPress={() => console.log("notes tapped!")}>
<Icon name="md-create" style={styles.actionButtonIcon} />
</ActionButton.Item>
</ActionButton>
</View>
</SideMenu>
);
}
}
const AppNavigator = TabNavigator(
{
H: { screen: App },
f: { screen: ListFlatlist },
Img: { screen: Imagetest },
B: { screen: Buttonpage },
S: { screen: ListSectionlist },
F: { screen: Fetchpage }
}
);
export default AppNavigator;
If a create new components for sidemenu, action bar and FAB I have to put them on all the tab render, not the cleanest way for me but i don't see other solution now.
I put an example repo together on Github. Thats actually all it needs:
// #flow
import React, { Component } from "react";
import { View } from "react-native";
import { StackNavigator, TabNavigator } from "react-navigation";
import ActionButton from "react-native-action-button";
import Profile from "./Profile";
import Tab1View from "./Tab1";
import Tab2View from "./Tab2";
import Tab3View from "./Tab3";
const TabView = TabNavigator({
tab1: { screen: Tab1View },
tab2: { screen: Tab2View },
tab3: { screen: Tab3View },
})
const Home = (props) => {
return (
<View style={{flex: 1}}>
<TabView navigation={props.navigation} />
<ActionButton offsetY={100} />
</View>
);
}
Home.router = TabView.router;
const StackView = StackNavigator({
home: { screen: Home },
profile: { screen: Profile },
});
export default class App extends Component {
render() {
return (
<View style={{ flex: 1 }}>
<StackView />
</View>
);
}
}

Passing props in react-native-flux-router

So I'm still a bit new to React Native, but I've got an app working nearly how I want it to. I have App.js which uses the TabBarIOS component to display my tabbed navigation at the bottom of the screen. When you touch a tab, the state updates and the relevant content is loaded and displayed:
App.js
import React, { Component } from 'react';
import { TabBarIOS, Platform, Image } from 'react-native';
import IconII from 'react-native-vector-icons/Ionicons';
import IconMCI from 'react-native-vector-icons/MaterialCommunityIcons';
import Colors from './Colors';
import RouterItem1 from './routers/RouterItem1';
import RouterItem2 from './routers/RouterItem2';
import RouterHome from './routers/RouterHome';
import Settings from './components/Settings';
class FooterNav extends Component {
state = {
selectedTab: 'home',
};
handleClick = (routeData) => {
console.log('This has received data', routeData);
this.setState({ selectedTab: routeData });
console.log(this.state);
}
renderCurrentView() {
switch (this.state.selectedTab) {
case 'home':
return (
<RouterHome handleClick={this.handleClick} />
);
case 'item2':
return (
<RouterItem2 />
);
case 'item1':
return (
<RouterItem1 />
);
case 'settings':
return (
<Settings />
);
default:
return false;
}
}
render() {
return (
<TabBarIOS
unselectedTintColor={Colors.third} //Unselected labels
unselectedItemTintColor={Colors.third} //Unselected icons
tintColor={Colors.brandPrimary} //Selected
barTintColor={Colors.light} //Bar background
>
<IconII.TabBarItem
title="Home"
iconName="ios-home-outline"
selectedIconName="ios-home"
selected={this.state.selectedTab === 'home'}
receiveData={this.receiveData}
onPress={() => {
this.setState({
selectedTab: 'home',
});
}}
>
{this.renderCurrentView()}
</IconII.TabBarItem>
<TabBarIOS.Item
icon={require('./images/item2-icon-line#3x.png')}
selectedIcon={require('./images/item2-icon-selected#3x.png')}
title="item2"
selected={this.state.selectedTab === 'item2'}
onPress={() => {
this.setState({
selectedTab: 'item2',
});
}}
>
{this.renderCurrentView()}
</TabBarIOS.Item>
<IconMCI.TabBarItem
title="item1"
iconName="cookie"
selectedIconName="cookie"
selected={this.state.selectedTab === 'item1'}
onPress={() => {
this.setState({
selectedTab: 'item1',
});
}}
>
{this.renderCurrentView()}
</IconMCI.TabBarItem>
<IconII.TabBarItem
title="More"
iconName="ios-more-outline"
selectedIconName="ios-more"
selected={this.state.selectedTab === 'settings'}
onPress={() => {
this.setState({
selectedTab: 'settings',
});
}}
>
{this.renderCurrentView()}
</IconII.TabBarItem>
</TabBarIOS>
);
}
}
module.exports = FooterNav;
react-native-router-flux is a great plugin, once it pulls in a router the flow is exactly what I want it to be. The only problem I have is with the <RouterHome> module:
RouterHome.js
import React from 'react';
import { Scene, Router } from 'react-native-router-flux';
import styles from '../Styles';
import { content } from '../content.js';
import AnotherScene from '../components/pages/AnotherScene';
import Home from '../components/Home';
const RouterHomeComponent = () => {
return (
<Router
sceneStyle={styles.sceneStyle}
navigationBarStyle={styles.navBar}
titleStyle={styles.navBarTitle}
barButtonIconStyle={styles.barButtonIconStyle}
>
<Scene
key="Home"
component={Home}
title={content.heading}
hideNavBar
/>
<Scene
key="AnotherScene"
component={AnotherScene}
title='title'
hideNavBar={false}
/>
</Router>
);
};
export default RouterHomeComponent;
The default view that is loaded is the <Home> module:
Home.js
import React, { Component } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { Actions } from 'react-native-router-flux';
import styles from '../Styles';
import { content } from '../content.js';
class Home extends Component {
displayItem1 = () => {
this.props.handleClick('item1');
}
displayItem2 = () => {
this.props.handleClick('item2');
}
displayAnotherScene() {
Actions.AnotherScene();
}
render() {
return (
<View style={styles.viewWrapperHome}>
<Text style={styles.heading}>{content.greeting}</Text>
<TouchableOpacity onPress={this.displayItem1}>
<Text>First item</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.displayItem2}>
<Text>Second item</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.displayAnotherScene}>
<Text>Another scene</Text>
</TouchableOpacity>
</View>
);
}
}
export default Home;
So what I expect to happen is that if the user clicks on the first two buttons displayed on the screen (First item/Second item), the prop will be passed back to App.js, the state will change and the scene will be updated. It basically is an alternative way of navigating without using the footer menu.
I know that this would work without using the react-native-router-flux module as I had it running before I added it in. The problem is that I really need it to handle the other pages from the <Home> module (many more to add).
Can anyone suggest a way I can pass back the props through the router back to my App.js?
Okay, so I actually figured this out - it turned out to totally be my fault; the props weren't getting passed to RouterHome and so they got lost either side. The new RouterHome looks like this:
RouterHome.js
import React from 'react';
import { Scene, Router } from 'react-native-router-flux';
import styles from '../Styles';
import { content } from '../content.js';
import AnotherScene from '../components/pages/AnotherScene';
import Home from '../components/Home';
const RouterHomeComponent = (props) => { <---Didn't add props
return (
<Router
sceneStyle={styles.sceneStyle}
navigationBarStyle={styles.navBar}
titleStyle={styles.navBarTitle}
barButtonIconStyle={styles.barButtonIconStyle}
>
<Scene
key="Home"
component={Home}
title={content.heading}
hideNavBar
handleClick={props.handleClick} <---Didn't pass props
/>
<Scene
key="AnotherScene"
component={AnotherScene}
title='title'
hideNavBar={false}
/>
</Router>
);
};
export default RouterHomeComponent;
I've marked the two changes I made. Hopefully this will help someone! :)

Resources