Element with fixed position on viewport in React Native Web? - expo

I need to stick a footer component to the bottom of the viewport. The content above it is in a ScrollView. Im using React Native and React Native Web (thank you Expo) to build for both the web and native.
This works for native but not web:
export default function App() {
return (
<View style={styles.container}>
<ScrollView>
{new Array(100).fill("").map((_, index) => {
return <Text key={index}>Blah {index}</Text>;
})}
</ScrollView>
<View style={styles.footer}>
<Text>Im a footer</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
},
footer: {
backgroundColor: "green",
padding: 20,
width: "100%",
},
});
I can hack it with this code. fixed is not officially supported but the CSS is applied in the web.
footer: {
// #ts-ignore
position: Platform.OS === "web" ? "fixed" : undefined,
bottom: 0,
left: 0,
}
Without the #ts-ignore I get this error from TypeScript:
  Type '"fixed"' is not assignable to type '"absolute" | "relative" | undefined'.
Is there a non-hacky way to do this?

Related

fontFamily "FontAwesome5Free-Regular" is not a system font and has not been loaded through Font.loadAsync

I'm working on a dental app in which there are icons in almost every screens. But, suddenly all the icons are not visible and just show a box with a question mark. The icon groups keep throwing errors. I changed the way the icons were imported according to expo icons document but it still wasn't working. This is the error I'm getting:
Code of one of the screens in which I am using the icons:
import React, { Component } from "react";
import * as db from '../Config/Firebase';
import {
View,
Text,
StatusBar,
TouchableOpacity,
FlatList,
} from "react-native";
import AntDesign from '#expo/vector-icons/AntDesign'
import Ionicons from '#expo/vector-icons/Ionicons'
import { ListItem, Avatar, Badge } from "react-native-elements";
import firebase from 'firebase/compat/app';
import 'firebase/compat/firestore'
import 'firebase/compat/auth'
import theme from "../Props/theme";
import Constants from "expo-constants";
export default class Pending extends Component {
constructor() {
super();
this.state = {
patients: [],
};
this.patient = null;
}
componentDidMount = async () => {
this.patient = await firebase
.firestore()
.collection(firebase.auth().currentUser.email)
.where("doctorEmail", "==", auth.currentUser.email)
.where("allVisitsCompleted", "==", false)
.onSnapshot((snapshot) => {
var docData = snapshot.docs.map((document) => document.data());
this.setState({
patients: docData,
});
});
};
render() {
return (
<View style={{ flex: 1, backgroundColor: "#FFF" }}>
<StatusBar hidden />
{this.state.patients.length !== 0 ? (
<View>
<FlatList
data={this.state.patients}
style={{ marginTop: 20 }}
renderItem={({ item }) => (
<ListItem>
<ListItem.Content
style={{
backgroundColor: "#f0f0f0",
padding: 20,
borderRadius: 20,
}}
>
<View style={{ flexDirection: "row" }}>
<View>
<Avatar
rounded
icon={{ name: "user", type: "font-awesome" }}
activeOpacity={0.7}
source={{
uri: "https://s3.amazonaws.com/uifaces/faces/twitter/adhamdannaway/128.jpg",
}}
/>
<Badge
containerStyle={{
position: "absolute",
top: -1,
right: -3,
}}
badgeStyle={{
width: 15,
height: 15,
borderRadius: 7.5,
backgroundColor: theme.darkPink,
}}
/>
</View>
<View style={{ flexDirection: "column", marginLeft: 20 }}>
<ListItem.Title>{item.patientName}</ListItem.Title>
<ListItem.Subtitle>{item.patientId}</ListItem.Subtitle>
</View>
</View>
</ListItem.Content>
</ListItem>
)}
keyExtractor={(item, index) => index.toString()}
/>
</View>
) : (
<View>
<Text
style={{
marginTop: Constants.statusBarHeight + 250,
fontSize: 35,
fontWeight: "200",
alignSelf: "center",
}}
>
No patients found
</Text>
<View style={{ marginVertical: 48, alignItems: "center" }}>
<TouchableOpacity
style={{
borderWidth: 2,
borderColor: theme.blue,
borderRadius: 4,
padding: 15,
alignItems: "center",
justifyContent: "center",
}}
onPress={() => this.props.navigation.navigate("Add")}
>
<AntDesign name="plus" size={16} color={theme.darkBlue} />
</TouchableOpacity>
<Text
style={{
color: theme.darkBlue,
fontWeight: "600",
fontSize: 14,
marginTop: 8,
}}
>
Add patient
</Text>
</View>
</View>
)}
</View>
);
}
}
Any ideas on how to solve this? Thanks in advance!
This is related to Expo, it's a known issue.
You will either have to load the font manually, see this.
Or use a library for your icons, VectorIcons has all fontawesome icons.

Search from list in React Native

I just wanna know how can I make search function in react native. I have a very big list of text(in local js file) and also Text input space. I want to make possible when users type something they can find what they looking for from list below. Here is my code and screenshot of App. I'm new in programming so please use easy terms =) app screenshot
project datebase sample
import React from 'react';
import { SafeAreaView, View, FlatList, StyleSheet, Text, StatusBar, TextInput } from 'react-native';
import {DATA} from "../Data/AbrData";
const Item = ({ title }) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const SearchScreen = ({navigator}) => {
const renderItem = ({ item }) => (
<Item title={item.title} />
);
return (
<SafeAreaView style={styles.container}>
<TextInput
style={{
height: 50,
borderColor: '#919191',
borderWidth: 1,
margin: 10,
paddingLeft: 15,
borderRadius:10
}}
placeholder="Axtaris..."
/>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
marginBottom:75,
},
item: {
backgroundColor: '#ededed',
padding: 20,
marginVertical: 2,
marginHorizontal: 10,
borderRadius: 20,
},
title: {
fontSize: 20,
},
});
export default SearchScreen;
Really Fast this is what I came up with.
import React, {useState, useEffect} from 'react';
import {
SafeAreaView,
StatusBar,
StyleSheet,
Text,
View,
FlatList,
TextInput,
} from 'react-native';
const App = () => {
const DATA = [{title: 'lorumn ispum'}, {title: 'lorumn ispum'}];
const [searchText, onChangeSearch] = useState('');
const [filteredData, setFilteredData] = useState([]);
useEffect(() => {
const filtered = DATA.filter(item =>
item.title.toLowerCase().includes(searchText.toLowerCase()),
);
if (searchText === '') {
return setFilteredData(DATA);
}
setFilteredData(filtered);
}, [searchText]);
const Item = ({title}) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const renderItem = ({item}) => <Item title={item.title} />;
return (
<SafeAreaView style={styles.container}>
<TextInput
style={{
height: 50,
borderColor: '#919191',
borderWidth: 1,
margin: 10,
paddingLeft: 15,
borderRadius: 10,
}}
onChangeText={newText => onChangeSearch(newText)}
placeholder="Axtaris..."
/>
<FlatList
data={filteredData}
renderItem={renderItem}
keyExtractor={(item, index) => item.key}
/>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
marginBottom: 75,
},
item: {
backgroundColor: '#ededed',
padding: 20,
marginVertical: 2,
marginHorizontal: 10,
borderRadius: 20,
},
title: {
fontSize: 20,
},
});
export default App;
I suggest watching this video! I learned how to simply filter a list from this video. Its plain js but the idea is the same.
https://www.youtube.com/watch?v=TlP5WIxVirU&ab_channel=WebDevSimplified
If you look at the repo of that video it boils down to the following:
searchInput.addEventListener("input", e => {
const value = e.target.value.toLowerCase()
users.forEach(user => {
const isVisible =
user.name.toLowerCase().includes(value) ||
user.email.toLowerCase().includes(value)
user.element.classList.toggle("hide", !isVisible)
})
})
Everytime the input changes (someone type something in the search field), the event listener is fired and converts the fields to lower case and compares it to the value your looking for. If the text contains the value it unhides it from the list. it does this for every entry in your dataset and thus filters the dataset to what you are looking for.

react native `TouchableOpacity` in 'absolute' view can not be clickable on android

I'v try to make a SlectBox with react native.
To make that dropdown menu can be floated, I gava an absolute position to wrapper View, then make a clickable component with TouchableOpacity inside that View. It works well on Ios and Web, but not on android. There were many solutions on google like reordering component or give a zIndex to WrapperView or use Pressable etc.., but I could not find proper solution for my case.
Belows are my whole code. and You can test this code here.
Thank you in advance.
import {
Animated,
Easing,
Platform,
StyleProp,
TextStyle,
TouchableOpacity,
View,
ViewStyle,
} from 'react-native';
import {DoobooTheme, light, useTheme} from '../theme';
import React, {FC, ReactElement, useEffect, useRef, useState} from 'react';
import {Icon} from '../Icon';
import {Typography} from '../Typography';
import styled from '#emotion/native';
import {withTheme} from '#emotion/react';
const Title = styled.View`
width: 200px;
height: 30px;
border-width: 1px;
flex-direction: row;
justify-content: center;
align-items: center;
`;
const Item = styled.View`
height: 30px;
width: 200px;
border-bottom-width: 1px;
border-left-width: 1px;
border-right-width: 1px;
justify-content: center;
align-items: center;
`;
type Styles = {
titleContainer?: StyleProp<ViewStyle>;
titleText?: StyleProp<TextStyle>;
rightElementContainer?: StyleProp<ViewStyle>;
itemContainer?: StyleProp<ViewStyle>;
itemText?: StyleProp<TextStyle>;
};
interface ItemCompProps {
value: string;
order: number;
styles?: Styles;
setIsOpened: (value: boolean) => void;
itemActiveOpacity: number;
onPress?: (i: number) => void;
}
const ItemComp: FC<ItemCompProps> = ({
value,
order,
styles,
setIsOpened,
itemActiveOpacity,
onPress,
}) => {
const {theme} = useTheme();
const handlePress = (): void => {
onPress?.(order);
setIsOpened(false);
};
return (
<TouchableOpacity onPress={handlePress} activeOpacity={itemActiveOpacity}>
<Item
style={[
{
borderColor: theme.primary,
backgroundColor: theme.textContrast,
},
styles?.itemContainer,
]}>
<Typography.Body2 style={styles?.itemText}>{value}</Typography.Body2>
</Item>
</TouchableOpacity>
);
};
interface Props {
data: string[];
onPress?: (i: number) => void;
selectedIndex?: number;
theme?: DoobooTheme;
style?: StyleProp<ViewStyle>;
styles?: Styles;
rotateDuration?: number;
titleActiveOpacity?: number;
itemActiveOpacity?: number;
isRotate?: boolean;
rightElement?: ReactElement | null;
}
const Component: FC<Props> = ({
data,
onPress,
selectedIndex = 0,
style,
styles,
rotateDuration = 200,
titleActiveOpacity = 1,
itemActiveOpacity = 1,
isRotate: shouldRotate = true,
rightElement = <Icon name="chevron-down-light" />,
}) => {
const {theme} = useTheme();
const [isOpened, setIsOpened] = useState(false);
const rotateAnimValue = useRef(new Animated.Value(0)).current;
useEffect(() => {
const toValue = isOpened ? 1 : 0;
if (!shouldRotate) rotateAnimValue.setValue(toValue);
Animated.timing(rotateAnimValue, {
toValue,
duration: rotateDuration,
easing: Easing.linear,
useNativeDriver: Platform.OS !== 'web' ? true : false,
}).start();
}, [isOpened, rotateAnimValue, rotateDuration, shouldRotate]);
return (
<View style={[style]}>
<TouchableOpacity
onPress={() => setIsOpened((prev) => !prev)}
activeOpacity={titleActiveOpacity}>
<Title
style={[
{
borderColor: theme.primary,
backgroundColor: theme.textContrast,
},
styles?.titleContainer,
]}>
<Typography.Body2 style={styles?.titleText} testID="selected-value">
{data[selectedIndex]}
</Typography.Body2>
{rightElement ? (
<Animated.View
style={[
{
position: 'absolute',
right: 10,
transform: [
{
rotate: rotateAnimValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '180deg'],
}),
},
],
},
styles?.rightElementContainer,
]}>
{rightElement}
</Animated.View>
) : null}
</Title>
</TouchableOpacity>
<View>
<View style={{position: 'absolute'}}>
{isOpened &&
data.map((datum, key) => (
<ItemComp
key={key}
order={key}
value={datum}
styles={styles}
setIsOpened={setIsOpened}
onPress={onPress}
itemActiveOpacity={itemActiveOpacity}
/>
))}
</View>
</View>
</View>
);
};
Component.defaultProps = {theme: light};
export const SelectBox = withTheme(Component);
My solution
I resolved this problem with react-native-gesture-handler. :)
Install react-native-gesture-handler, then using TouchableOpacity from that.
Try to render out the component you want to be on top of stack below the component you want to bring below it
How I solve this issue:
I was implementing custom dynamic toaster using context to use it globally and wasn't able to access touchable opacity due to absolute positioning but i realized i was rendering my Toaster component before my main Stack Navigator which was bringing my toaster below navigation layer so i rendered it after stack navigator and it worked
BEFORE:
<ToasterContext.Provider value={{toasts:this.state.toastState, showToaster: this.showToaster,closeToaster: this.closeToaster }}>
<Provider store={store.store}>
<PersistGate loading={null} persistor={store.persistor}>
{toastState.length > 0
&& toastState.length <= 3
&& toastState.map((t, i) => {
return(
<DynamicToast i={i} key={i} t={t} position={"bottom"} duration={4000} closeToaster={this.closeToaster} />
)
}
)}
<NetworkConnectionModal/>
<AppContainer /> // I was rendering before my AppContainer
</PersistGate>
</Provider>
</ToasterContext.Provider>
AFTER: (Resolved my problem)
<ToasterContext.Provider value={{toasts:this.state.toastState, showToaster: this.showToaster,closeToaster: this.closeToaster }}>
<Provider store={store.store}>
<PersistGate loading={null} persistor={store.persistor}>
<NetworkConnectionModal/>
<AppContainer />
{toastState.length > 0
&& toastState.length <= 3
&& toastState.map((t, i) => {
return(
<DynamicToast i={i} key={i} t={t} position={"bottom"} duration={4000} closeToaster={this.closeToaster} />
)
}
)}
</PersistGate>
</Provider>
</ToasterContext.Provider>
Most of this is because the element is hidden behind other elements after absolute positioning.
Just like the picture in PPT is set to the bottom, but in some cases in HTML, you can still see your element. In this case, we need to set this element to the top.
Open the developer tool, select your element, add a Z-index to the element style of this element, and increase from 2 to see which value can just select your element. After confirmation, add a style to the project file.
z-index: 2,//add this to your component style

Changing backgroundColor depending on the state value

I am learning how to use React components on React Native and I am now starting the Handling Events. I created a handler that turns a text component to ON or OFF whenever the user presses the button. I managed to change the color of the button whenever the boolean value of the state changes but I haven't managed to do it with the backgroundColorof the screen. I tried to create a function {color} to render the color depending on the isToggleOn but my attempt was unsuccessful.
I think I have to pass a props to it but I don't know how to apply it in this case. Could you give me a hand?
import React from 'react';
import { View, Text, Button } from 'react-native';
import { render } from 'react-dom';
export default class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.state = { isToggleOn: true };
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState((state) => ({
isToggleOn: !state.isToggleOn,
}));
}
render() {
//I tried to render the `color` by creating a function
const { color } = this.state.isToggleOn ? 'red' : 'blue';
return (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: color,
}}>
<Text>{this.state.isToggleOn ? 'ON' : 'OFF'}</Text>
<Button
color={this.state.isToggleOn ? 'red' : 'blue'}
title={this.state.isToggleOn ? 'TURN OFF' : 'TURN ON'}
onPress={this.handleClick}
/>
</View>
);
}
}
import React from 'react';
import {View, Text, Button} from 'react-native';
import { render } from 'react-dom';
export default class HomeScreen extends React.Component{
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
// missing this here
isToggleOn: !this.state.isToggleOn
}));
}
render() {
// use variable
const color = this.state.isToggleOn ? 'red' : 'blue';
return(
<View
style={{
flex:1,
alignItems:'center',
justifyContent:'center',
backgroundColor:color}}>
<Text>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</Text>
<Button color={this.state.isToggleOn ? 'red' : 'blue'} title={this.state.isToggleOn ? 'TURN OFF' : 'TURN ON'} onPress={this.handleClick}/>
</View>
)
}
}
Ciao, you could use a conditional style on View component like this:
<View style={this.state.isToggleOn ? styles.bg_red : styles.bg_blue}>
Then in your styles:
...
bg_blue: {
flex: 1,
alignItems: "center",
justifyContent: "center",
backgroundColor: "blue"
},
bg_red: {
flex: 1,
alignItems: "center",
justifyContent: "center",
backgroundColor: "red"
}
...
Here your code modified.

Displaying data next to each other in react native

In my react-native app I'm returning data from an api, everything is working except for my layout it got messed up, i have an albums list and i need to display each 2 albums next to each other, but all of them are getting displayed under each other, here is my code:
Album detail:
const AlbumDetails= (props) => {
return(
<Album>
<Image source={{ uri: props.album.thumbnail_image }}/>
<Text>{props.album.title}</Text>
</Album>
);
};
export default AlbumDetails;
Album:
const Album= (props) => {
return(
<View style={{flex: 1}}>
<View style={{display: "flex", flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<View style={styles.albumContainer}>
<View>{props.children}</View>
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
albumContainer: {
backgroundColor: 'red',
width: '50%',
height: 180,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginBottom: 20,
flexDirection: 'column'
},
});
export default Album;
I suggest to use FlatList component with numColumns param equal 2. Check Expo Snack or code below.
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<FlatList
style={{ margin: 5 }}
data={data}
numColumns={2}
keyExtractor={(item, index) => item.id}
renderItem={dataItem => (
<AlbumDetails
album={{
title: dataItem.item.title,
thumbnail_image: dataItem.item.image,
}}
/>
)}
/>
</View>
);
}
}