How to use the helper function provided by a library? - android

I am using react-native-animated-charts to render a chart. However, I am having difficulty reading x, and y values of the moveable chart dot. The documentation mentions that useChartData helper function gives access to this information. However, I am unsure how to use (or even where to use or initialize this function).
EDIT: I have added the code below
import React, {useEffect, useState, useRef} from 'react';
import {Text, Dimensions, View, TouchableHighlight, StyleSheet} from 'react-native';
import {
ChartDot,
ChartPath,
ChartPathProvider,
ChartYLabel,
ChartXLabel,
useChartData,
monotoneCubicInterpolation,
} from '#rainbow-me/animated-charts';
import {runOnJS} from 'react-native-reanimated';
import Card2View from '..//Card2View/Card2View';
import styles from './styles';
export const {width: SIZE, height} = Dimensions.get('window');
const TABLE_ITEM_OFFSET = 10;
const TABLE_ITEM_MARGIN = TABLE_ITEM_OFFSET * 2;
const SCREEN_WIDTH = SIZE < height ? SIZE : height;
export const data = [
{x: 1453075200, y: 1.47},
{x: 1453161600, y: 1.37},
{x: 1453248000, y: 1.53},
{x: 1453334400, y: 1.54},
{x: 1453420800, y: 1.52},
{x: 1453507200, y: 2.03},
{x: 1453593600, y: 2.1},
{x: 1453680000, y: 2.5},
{x: 1453766400, y: 2.3},
{x: 1453852800, y: 2.42},
{x: 1453939200, y: 2.55},
{x: 1454025600, y: 2.41},
{x: 1454112000, y: 2.43},
{x: 1454198400, y: 2.2},
];
const points = monotoneCubicInterpolation({data, range: 40});
const LineChartView1 = ({priceData}) => {
const [activeChart, setActiveChart] = useState(0)
const lineChartTables = ['1D', '1W', '1M', '3M', '1Y', 'ALL'];
const output = useChartData()
console.log(output);
const getX = value => {
'worklet';
// console.log(runOnJS(useChartData("state")));
if (value === '') {
return '';
}
return `$ ${value.toLocaleString('en-US', {
currency: 'USD',
})}`;
};
const getY = value => {
'worklet';
// console.log(runOnJS(useChartData("state")));
if (value === '') {
return '';
}
const date = new Date(Number(value * 1000));
const s = date.getSeconds();
const m = date.getMinutes();
const h = date.getHours();
const d = date.getDate();
const n = date.getMonth();
const y = date.getFullYear();
return `${y}-${n}-${d} ${h}:${m}:${s}`;
};
renderTable = (item, index) => (
<TouchableHighlight
onPress={() => setActiveChart(index)}
underlayColor="rgba(73,182,77,1,0.9)"
key={index}
style={
activeChart == index
? {
justifyContent: 'center',
backgroundColor: '#617180',
borderRadius: 5,
flex: 1,
alignItems: 'center',
margin: TABLE_ITEM_OFFSET,
width:
(SCREEN_WIDTH - TABLE_ITEM_MARGIN) / lineChartTables.length -
TABLE_ITEM_OFFSET,
height:
(SCREEN_WIDTH - TABLE_ITEM_MARGIN) / lineChartTables.length -
TABLE_ITEM_OFFSET,
maxWidth: 50,
maxHeight: 50
}
: {
justifyContent: 'center',
backgroundColor: 'white',
borderRadius: 5,
flex: 1,
alignItems: 'center',
margin: TABLE_ITEM_OFFSET,
width:
(SCREEN_WIDTH - TABLE_ITEM_MARGIN) / lineChartTables.length -
TABLE_ITEM_OFFSET,
height:
(SCREEN_WIDTH - TABLE_ITEM_MARGIN) / lineChartTables.length -
TABLE_ITEM_OFFSET,
maxWidth: 50,
maxHeight: 50
}
}
>
<Text style={activeChart == index ? chart.activeChartTxt : chart.chartTxt}>
{item}
</Text>
</TouchableHighlight>
);
return(
<View>
<Card2View item={{title:priceData.symbol, text:priceData.lastUpdatedPrice, money:`Rs. ${priceData.lastUpdatedPrice}`, procent:`${(priceData.percentageChange).toFixed(2)}`}} />
<View
style={{backgroundColor: 'white'}}>
<ChartPathProvider
data={{
points,
smoothingStrategy: 'bezier',
}}
>
<ChartPath height={SIZE / 2} stroke="black" strokeWidth="2" selectedOpacity="0.3" width={SIZE} />
<ChartDot
style={{
backgroundColor: 'black',
}}
size={15}
/>
{/* <ChartYLabel format={getX} style={{backgroundColor: 'white', color: 'black'}}/>
<ChartXLabel format={getY} style={{backgroundColor: 'white', color: 'black'}}/> */}
</ChartPathProvider>
<View style={{ flexDirection: 'row', justifyContent: 'space-around', flex: 1 }}>
{lineChartTables.map((data, index) => renderTable(data, index))}
</View>
</View>
</View>
)
}
export default LineChartView1;
const chart = StyleSheet.create({
chartTxt: {
fontSize: 14,
color: 'black'
},
activeChartTxt: {
fontSize: 14,
color: 'white',
fontWeight: 'bold'
}
});

You need to call the hook inside the ChartPathProvider. Example:
const ChildComponent = () => {
const values = useChartData();
return <Text>{values.greatestX}</Text>
}
const ParentComponent = ({ points }) => (
<ChartPathProvider
data={{
points,
smoothingStrategy: 'bezier',
}}
>
{/* other chart components */}
<ChildComponent />
</ChartPathProvider>
)
In your example, you are calling the hook before you define the provider (i.e. before the return statement).

in addition to the answer provided above by #Paul Kuhle
you can use the following to be able to access chartData
import React, {useContext} from 'react';
import ChartContext from '#rainbow-me/animated-charts/src/helpers/ChartContext';
const {positionX, positionY, dotScale, providedData, greatestY, layoutSize} = useContext(ChartContext);
and you may want to use useEffect to detect changes in the chart, let me know if that helps

Related

Custom video control keep making video stutter

Current behavior / Bug
As soon as I pause/play the video on custom video control or slide on slider the video keep stutter to 1 or 2 seconds later sometim even 3-5 seconds
Video Clip Bug
https://drive.google.com/file/d/1C9Nj7FMcgA-I-I5Zha5jdJ-IBGXYGWJL/view?usp=sharing
Reproduction steps
click button to enter the new screen
video autoplay as soon the new screen load (if not doobleTap or singleTap on overlay, it will play as normal)
singleTap on video's overlay for the custom controls to appear then click pause button to pause video.
click play button to play video again, then video stutter.
Sometime slide on slider to seek new video's currentTime, video stutter (rarely not stutter)
If you pause the video the slide and play again and custom video control fadeout, video will play as normal not stutter
Expected behavior
pause / play video not stutter.
slide on progress video and play video again not stutter
Platform
Which player are you experiencing the problem on:
Android
React Native Setup
react-native : 0.64.2
gradle: 6.9
JDK: 11
Android Studio: Dolphin 2021.3.1
Android SDK API: 30
build tools: 30.0.2
#react-native-community/slider: 4.3.1
react-native-video: 5.2.0
react-native-orientation-locker: 1.5.0
react-native-vector-icons: 8.1.0
Remote Video URL
I called video URL from google cloud storage by using redux-saga.
I already called dispatch function to called api from parent screen. So, in this screen I only useSelector to get URL from redux-saga.
Can use any video URL with .mp4 file to substitute it.
Video sample
https://drive.google.com/uc?export=download&id=1C9Nj7FMcgA-I-I5Zha5jdJ-IBGXYGWJL
Sample Code
import React, { useRef, useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { View, StyleSheet, BackHandler, Dimensions, TouchableNativeFeedback, Text, StatusBar, Platform } from 'react-native';
import Video from 'react-native-video';
import Orientation from 'react-native-orientation-locker';
import Icon from 'react-native-vector-icons/MaterialIcons';
import Slider from '#react-native-community/slider';
import { normalize } from 'react-native-elements';
const { width, height } = Dimensions.get("window");
Icon.loadFont();
let overlayTimer;
let Timer;
const VideoPlayerScreen = (props) => {
let lastTap = null;
const dispatch = useDispatch();
const { navigation } = props;
const [Fullscreen, setFullscreen] = useState(false);
const [paused, setpaused] = useState(false);
const [currentTime, setcurrentTime] = useState(0);
const [duration, setduration] = useState(0.1);
const [overlay, setoverlay] = useState(false);
const playerRef = useRef();
const contract = useSelector((state) => state.contract.contract);
useEffect(() => {
const backHandler = BackHandler.addEventListener(
"hardwareBackPress",
backAction
);
return () => backHandler.remove();
}, [])
const backAction = () => {
// navigation.goBack();
return true;
}
const videoUri = contract.videoURL
const FullscreenToggle = () => {
if (Fullscreen) {
Orientation.lockToPortrait();
StatusBar.setHidden(false)
navigation.setOptions({ headerShown: true });
setFullscreen(false)
} else {
Orientation.lockToLandscape();
StatusBar.setHidden(true)
navigation.setOptions({ headerShown: false });
setFullscreen(true);
}
}
const handleDoubleTap = (doubleTapCallback, singleTapCallback) => {
const now = Date.now();
const DOUBLE_PRESS_DELAY = 300;
if (lastTap && (now - lastTap) < DOUBLE_PRESS_DELAY) {
clearTimeout(Timer);
doubleTapCallback();
} else {
lastTap = now;
Timer = setTimeout(() => {
singleTapCallback();
}, DOUBLE_PRESS_DELAY);
}
}
const ShowHideOverlay = () => {
handleDoubleTap(() => {
}, () => {
setoverlay(true)
overlayTimer = setTimeout(() => setoverlay(false), 5000);
})
}
const backward = () => {
playerRef.current.seek(currentTime - 5);
clearTimeout(overlayTimer);
overlayTimer = setTimeout(() => setoverlay(false), 3000);
}
const forward = () => {
playerRef.current.seek(currentTime + 5);
clearTimeout(overlayTimer);
overlayTimer = setTimeout(() => setoverlay(false), 3000);
}
const onslide = (slide) => {
playerRef.current.seek(slide * duration);
clearTimeout(overlayTimer);
overlayTimer = setTimeout(() => setoverlay(false), 3000);
}
const getTime = (t) => {
const digit = n => n < 10 ? `0${n}` : `${n}`;
const sec = digit(Math.floor(t % 60));
const min = digit(Math.floor((t / 60) % 60));
const hr = digit(Math.floor((t / 3600) % 60));
// return hr + ':' + min + ':' + sec;
return min + ':' + sec;
}
const load = ({ duration }) => setduration(duration);
const progress = ({ currentTime }) => setcurrentTime(currentTime);
return (
<View style={styles.container}>
{Platform.OS === 'android' ?
< View style={Fullscreen ? styles.fullscreenVideo : styles.video}>
<Video
source={{ uri: videoUri }}
style={{ ...StyleSheet.absoluteFill }}
ref={playerRef}
paused={paused}
repeat={true}
onLoad={load}
onProgress={progress}
resizeMode={"contain"}
rate={1.0}
/>
<View style={styles.overlay}>
{overlay ?
<View style={{ ...styles.overlaySet, backgroundColor: '#0006', alignItems: 'center', justifyContent: 'space-around' }}>
<View style={{ width: 50, height: 50 }}>
<Icon name='replay-5' style={styles.icon} onPress={backward} />
</View>
<View style={{ width: 50, height: 50 }}>
<Icon name={paused ? 'play-arrow' : 'pause'} style={styles.icon} onPress={() => setpaused(!paused)} />
</View>
<View style={{ width: 50, height: 50 }}>
<Icon name='forward-5' style={styles.icon} onPress={forward} />
</View>
<View style={styles.sliderCont}>
<View style={{ ...styles.timer, alignItems: 'center' }}>
<View style={{ flexDirection: 'row' }}>
<Text style={{ color: 'white' }}>{getTime(currentTime)}/</Text>
<Text style={{ color: 'white' }}>{getTime(duration)}</Text>
</View>
<View style={{ margin: 5 }}>
<Icon onPress={FullscreenToggle}
name={Fullscreen ? 'fullscreen' : 'fullscreen-exit'}
style={{ fontSize: 20, color: 'white' }} />
</View>
</View>
<Slider
style={{ margin: 5 }}
maximumTrackTintColor='white'
minimumTrackTintColor='white'
thumbTintColor='white'
value={currentTime / duration}
onValueChange={onslide}
/>
</View>
</View>
:
<View style={styles.overlaySet}>
<TouchableNativeFeedback onPress={ShowHideOverlay}><View style={{ flex: 1 }} /></TouchableNativeFeedback>
</View>
}
</View>
</View>
:
<View style={styles.video}>
<Video
source={{ uri: videoUri }}
style={{ width: width, aspectRatio: width / (height - normalize(110)) }}
controls
// ref={(ref) => {
// this.player = ref;
// }}
/>
</View>
}
</View >
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'black'
},
// video: { width, height: width * .6, backgroundColor: 'black', justifyContent: 'center', alignItems: 'center' },
video: { width: "100%", aspectRatio: width / (height - normalize(80)), backgroundColor: 'black', alignItems: 'center', justifyContent: 'center' },
fullscreenVideo: {
width: "100%",
aspectRatio: 2 / 1,
backgroundColor: 'black',
...StyleSheet.absoluteFill,
elevation: 1
},
overlay: {
...StyleSheet.absoluteFillObject,
},
overlaySet: {
flex: 1,
flexDirection: 'row',
},
icon: {
color: 'white',
flex: 1,
textAlign: 'center',
textAlignVertical: 'center',
fontSize: 25
},
TextStyle: {
fontSize: 20, textAlign: 'center',
marginVertical: 100, color: '#6200ee', fontWeight: 'bold'
},
sliderCont: {
position: 'absolute',
left: 0,
right: 0,
bottom: 0
},
timer: {
width: '100%',
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 5
},
});
export default VideoPlayerScreen;

Blur doesn't work correctly on Android #react-native-community/blur

On Android elements wrapped in BlurVIew don't positioning right.
It works on iOS devices fine. But on Android, elements wrapped in <BlurView>{children}</BlurView> run into each other. Screenshot is below
Sometimes when I change something in styles in dev mode, it works correctly, but when I build apk, bug appears again.
How can I fix it on Android?
Code
const BottomNavigationTabs: React.FC<BottomTabBarProps> = ({
descriptors,
state,
navigation,
}) => {
const focusedOptions = descriptors[state.routes[state.index].key].options;
return (
<>
<BlurView blurType="light"
blurAmount={15} style={{ height: 85, width: '100%', position: 'absolute', bottom: 0, }}>
<View style={{ display: 'flex', flexDirection: 'row', position: 'absolute', bottom: 0, justifyContent: 'space-between' }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const focusedColor = isFocused ? colors.text1 : colorsOld.black;
//Weird snippet, to render passed icon just call function with any return value, then just set color
const icon =
options.tabBarIcon &&
React.cloneElement(
//#ts-ignore
options.tabBarIcon((props: any) => props),
{ color: focusedColor }
);
const onPress = () => {
const event = navigation.emit({
type: "tabPress",
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: "tabLongPress",
target: route.key,
});
};
const tabBtn = (
<TouchableOpacity
accessibilityRole="button"
accessibilityState={isFocused ? { selected: true } : {}}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
style={styles.tabButton}
onLongPress={onLongPress}
activeOpacity={0.7}
key={route.key}
>
<View style={styles.tabItem}>
<View style={styles.tabItemIcon}>{icon}</View>
<Text style={{ ...styles.tabItemLabel, color: focusedColor }}>
{label}
</Text>
</View>
</TouchableOpacity>
);
return tabBtn;
})}
</View>
</BlurView>
</>
);
};
const styles = StyleSheet.create({
container: {
alignItems: "center",
justifyContent: "center",
paddingTop: 9,
paddingBottom: 15,
},
tabButton: {
paddingTop: 8,
paddingBottom: 15,
paddingHorizontal: 10,
borderRadius: 10,
zIndex: 10000
},
tabsContainer: {
backgroundColor: colors.secondaryBg(0.5),
width: "100%",
left: 0,
bottom: 0,
zIndex: 999,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-around",
},
tabItem: {
justifyContent: "center",
alignItems: "center",
},
tabItemIcon: {
marginBottom: 6,
},
tabItemLabel: {
fontSize: 12,
fontFamily: "Inter-Medium",
},
});
Found a solution here https://github.com/Kureev/react-native-blur/issues/483#issuecomment-1199210714.
Solution is to not set to <BlurView> position: absolute and to not wrapping anything with it. In my case looks like:
import React from "react";
import {
Text,
View,
ImageBackground,
TouchableOpacity,
StyleSheet,
} from "react-native";
import { BlurView } from "#react-native-community/blur";
import { BottomTabBarProps } from "#react-navigation/bottom-tabs";
import colorsOld from "src/theme/colorsOld";
import colors from "src/theme/colors";
const BottomNavigationTabs: React.FC<BottomTabBarProps> = ({
descriptors,
state,
navigation,
}) => {
const focusedOptions = descriptors[state.routes[state.index].key].options;
return (
<View
style={{
display: "flex",
flexDirection: "row",
backgroundColor: colors.secondaryBg(0.5),
position: "absolute",
bottom: 0,
justifyContent: "space-between",
}}
>
<BlurView
blurType="light"
blurAmount={15}
style={{ height: 85, width: "100%", bottom: 0 }}
/>
<View style={styles.tabsContainer}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const focusedColor = isFocused ? colors.text1 : colorsOld.black;
//Weird snippet, to render passed icon just call function with any return value, then just set color
const icon =
options.tabBarIcon &&
React.cloneElement(
//#ts-ignore
options.tabBarIcon((props: any) => props),
{ color: focusedColor }
);
const onPress = () => {
const event = navigation.emit({
type: "tabPress",
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: "tabLongPress",
target: route.key,
});
};
const tabBtn = (
<TouchableOpacity
accessibilityRole="button"
accessibilityState={isFocused ? { selected: true } : {}}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
style={styles.tabButton}
onLongPress={onLongPress}
activeOpacity={0.7}
key={route.key}
>
<View style={styles.tabItem}>
<View style={styles.tabItemIcon}>{icon}</View>
<Text style={{ ...styles.tabItemLabel, color: focusedColor }}>
{label}
</Text>
</View>
</TouchableOpacity>
);
return tabBtn;
})}
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
alignItems: "center",
justifyContent: "center",
paddingTop: 9,
paddingBottom: 15,
},
tabButton: {
paddingTop: 8,
paddingBottom: 15,
paddingHorizontal: 10,
borderRadius: 10,
zIndex: 10000,
},
tabsContainer: {
width: "100%",
left: 0,
bottom: 0,
zIndex: 999,
position: 'absolute',
flexDirection: "row",
alignItems: "center",
justifyContent: "space-around",
},
tabItem: {
justifyContent: "center",
alignItems: "center",
},
tabItemIcon: {
marginBottom: 6,
},
tabItemLabel: {
fontSize: 12,
fontFamily: "Inter-Medium",
},
});
export default BottomNavigationTabs;

Pan Responder with transform not working in Android react native?

Problem:
In my react native app I have created an animation like tinder cards. It is working correctly in Ios but in Android Animated view which has panResponder and transform even does not render in the view . This is how I have organized my code.
const TinderCardAnimation = ({ theme, data, onHeartClick }) => {
const { width, height } = useWindowDimensions();
const [currentIndex, setCurrentIndex] = useState(0);
const pan = useRef(new Animated.ValueXY()).current;
const rotate = pan.x.interpolate({
inputRange: [-width / 2, 0, width / 2],
outputRange: ['-10deg', '0deg', '10deg'],
extrapolate: 'clamp'
})
const rotateAndTranslate = {
transform: [{
rotate: rotate
},
...pan.getTranslateTransform()
]
}
const likeOpacity = pan.x.interpolate({
inputRange: [-width / 2, 0, width / 2],
outputRange: [0, 0, 1],
extrapolate: 'clamp'
})
const nopeOpacity = pan.x.interpolate({
inputRange: [-width / 2, 0, width / 2],
outputRange: [1, 0, 0],
extrapolate: 'clamp'
})
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) =>
true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) =>
true,
onPanResponderTerminationRequest: (evt, gestureState) =>
true,
onPanResponderGrant: () => {
pan.setOffset({
x: pan.x._value,
y: pan.y._value
});
},
onPanResponderMove: (evt, gestureState) => {
pan.setValue({
x: gestureState.dx,
y: gestureState.dy
})
},
onPanResponderRelease: () => {
pan.flattenOffset();
},
onShouldBlockNativeResponder: (evt, gestureState) => {
// Returns whether this component should block native components from becoming the JS
// responder. Returns true by default. Is currently only supported on android.
return true;
}
})
).current;
return <View style={{ backgroundColor: '#ffffff' }}>{data?.map((item, index) => {
if (index < currentIndex) {
return null
} else if (index == currentIndex) {
return (<Animated.View {...panResponder.panHandlers} key={index} style={[rotateAndTranslate, { height: height / 5, width: width, padding: 10, position: 'absolute' }]}>
<Animated.View
style={{
opacity: likeOpacity,
transform: [{ rotate: "-30deg" }],
position: "absolute",
top: 50,
left: 40,
zIndex: 1000
}}
>
<TouchableOpacity >
<Image style={{ marginTop: height / 4 }} source={require("_assets/images/XCircleb.png")} />
</TouchableOpacity>
</Animated.View>
<Animated.View
style={{
opacity: nopeOpacity,
transform: [{ rotate: "30deg" }],
position: "absolute",
top: 50,
right: 40,
zIndex: 1000
}}
>
<TouchableOpacity onPress={() => onHeartClick(item?.id)}>
<Image style={{ marginTop: height / 4 }} source={require("_assets/images/HCircleb.png")} />
</TouchableOpacity>
</Animated.View>
<ProfileCard key={index} profile={item} />
</Animated.View>)
} else {
return (<Animated.View key={index} style={{ height: height / 5, width: width, padding: 10, position: 'absolute' }}>
<ProfileCard key={index} profile={item} />
</Animated.View>)
}
}).reverse()}
</View >
}
export default withTheme(TinderCardAnimation);
I tried a lot to make it work in Android. But I was unable to do so. Can someone help me to solve this issue? Thank you

How to go from top to bottom of a ScrollView in React Native?

I'm in a React Native project that the client wants the product image scrolls from top to bottom vice versa in a modal, how can I achive this?
I already know how to solve this...
I had to create a counter that increase or decrease Y axis of ScrollView every 0.5 seconds and checking if reached the top or bottom.
In the modal component file:
import React, { useState, useEffect } from 'react';
import { StyleSheet, Modal, ScrollView, View, Image, NativeSyntheticEvent, NativeScrollEvent } from 'react-native';
import { Feather } from '#expo/vector-icons';
const ImageModal: React.FC<{ product: ProductType }> = ({ product }) => {
const [ axisY, setAxisY ] = useState<number>(0); // State that is used to know the current Y axis of ScrollView
const [ scrollToTop, setScrollToTop ] = useState<boolean>(false); // State that is used to checks if should go to top or bottom
// Handler that checks if ScrollView is scrolling to top or bottom
const handleScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
// HELP: https://newbedev.com/detect-scrollview-has-reached-the-end
const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent; // Get scroll event properties
// If scrolling to top
if (scrollToTop) {
const isNearTop = contentOffset.y != 0; // Checks if Y axis reached the top of ScrollView
setScrollToTop(isNearTop); // Change the state value to FALSE, making ScrollView goes to bottom
} else {
const isNearBottom = layoutMeasurement.height + contentOffset.y >= contentSize.height; // Checks if Y axis reached the bottom of ScrollView
setScrollToTop(isNearBottom); // Change the state value to TRUE, making ScrollView goes to top
}
}
// Increase or decrease current Y axis every 0.5 seconds
useEffect(() => {
const timer = setInterval(() => {
setAxisY(prev => !scrollToTop ? prev + 1.5 : prev - 1.5);
}, 50);
return () => clearInterval(timer);
}, [scrollToTop]);
return (
<Modal
visible={ true }
transparent={ true }
statusBarTranslucent={ true }
animationType="fade"
>
<View style={ styles.container }>
<View style={ styles.box }>
<ScrollView
overScrollMode="never"
style={ styles.scroll }
scrollEnabled={ false }
showsVerticalScrollIndicator={ false }
contentOffset={{ x: 0, y: axisY }}
onScroll={ handleScroll }
>
<View style={ styles.imageBox }>
<Image source={{ uri: product.image_url }} style={ styles.image } />
</View>
</ScrollView>
<View>
<Text>Some random text!</Text>
</View>
</View>
<TouchableOpacity style={ styles.closeButton } onPress={ onClose }>
<Feather name="x" size={ 30 } color="#fff" />
</TouchableOpacity>
</View>
</Modal>
);
}
// Main Styles
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
padding: 20
},
closeButton: {
width: 60,
height: 60,
borderWidth: 2,
borderRadius: 12,
marginLeft: 20,
borderColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#58585a'
},
box: {
backgroundColor: '#fff',
width: '80%',
borderRadius: 10,
flexDirection: 'column'
},
scroll: {
width: '100%',
height: '80%',
borderBottomWidth: 1,
borderColor: '#58585a'
},
imageBox: {
width: '100%',
height: 600,
},
image: {
width: '100%',
height: '100%',
resizeMode: 'cover',
borderTopLeftRadius: 10,
borderTopRightRadius: 10
}
});
export default ImageModal;

Trying to make tab Icons clickable away from TabBar on Android

I am trying to make my tab icons to render their screens when clicked upon and when they are not within the TabBar. It is working with IOS but not with Android. It seems that the tab selection range can only be reached within the TabBar and not outside, above that it is not attached to its icons. Is there any way to make it work outside the TabBar when clicking on its icon? Thanks
Another way I tried is by making the TabBar height at a 100% of the screen and making its backgroundColor Transparent to see the screen behind but it shows a white screen instead and hides the content behind it.
import React from 'react'
import {
StyleSheet,
Text,
View,
Image
} from 'react-native'
import {
createBottomTabNavigator,
createAppContainer
} from 'react-navigation'
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp
} from 'react-native-responsive-screen'
class DocSelection extends React.Component {
render() {
return ( <
View style = {
styles.container
} >
<
Text > CustomerService < /Text>
<
/View>
)
}
}
class Printing extends React.Component {
render() {
return ( <
View style = {
styles.container
} >
<
Text > hfhdfjedhfeehfjeh < /Text>
<
/View>
)
}
}
class Design extends React.Component {
render() {
return ( <
View style = {
styles.container
} >
<
Text > 874877847484787 < /Text>
<
/View>
)
}
}
const RouteConfigs = {
'Home': {
screen: DocSelection,
navigationOptions: { //tabBarButtonComponent: tabBarIcon: ({ tintColor, horizontal }) => (
<
Image style = {
{
margin: 15,
width: 35,
height: 35,
tintColor: "red"
}
}
source = {
require("../Icons/home.png")
}
/> ), }, }, 'Order history':{ screen: Printing, navigationOptions: { backgroundColor: '#262A2C', top:-60, borderTopWidth: 0, tabBarIcon: ({ tintColor }) => ( <
Image style = {
{
width: 32,
height: 32,
tintColor: "red"
}
}
source = {
require("../Icons/history-clock-button.png")
}
/> ), }, }, 'Customer service':{ screen: Design, navigationOptions: { tabBarIcon: ({ tintColor }) => ( <
Image style = {
{
top: 0,
margin: 15,
width: 40,
height: 40,
tintColor: "red"
}
}
source = {
require("../Icons/customer-service.png")
}
/> ), }, }, }; const DrawerNavigatorConfig = { intialRouteName: 'Home', navigationOptions: { tabBarVisible: true }, tabBarOptions: { tabStyle:{ top:-130, height:0 }, showLabel: false, style:{ backgroundColor:"rgba(255, 0, 0, 0)" }, }, }; const Navigator = createBottomTabNavigator(RouteConfigs, DrawerNavigatorConfig);
export default createAppContainer(Navigator);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
top: 300
}
});
If there was anyone interested in the answer, You just have to create a component and navigate to the concerned tab. Make sure the tab visibility is false and then place the component where you want. This practice will ensure that going back to previous tabs will save your previous state on previous pages.
If anyone is interested I can post an example. There is currently no reply so I will submit this answer instead.
I have structured it a little differently but here is a simpler case i hope would help you
example:
import React from 'react';
import {View,TouchableOpacity, PixelRatio, Image} from 'react-native'
import { createBottomTabNavigator, createAppContainer} from 'react-navigation'
import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen'
class Home extends React.Component{
render(){
const {navigation} = this.props;
const { routeName } = navigation.state;
return(
<View style={{flex:1,
backgroundColor:'grey'}}>
<TouchableOpacity
activeOpacity={0.7}
onPress={() => {
navigation.navigate("Home")
}}
style={ {position: 'relative',
width: PixelRatio.get() <= 2 ? 40 : 45,
height: PixelRatio.get() <= 2 ? 40 : 45,
alignItems: 'center',
justifyContent: 'center',
left: wp('5%'),
top: hp('11.5%'),
backgroundColor:routeName==="Home" ? 'black' : 'white' ,
borderRadius: PixelRatio.get() <= 2 ? 40/2 : 45/2}}>
<Image
source={require("./Icons/category.png")}
//pay FlatIcon or design personal one
style={{ resizeMode: 'contain',
width: PixelRatio.get() <= 2 ? 25 : 30,
height: PixelRatio.get() <= 2 ? 25 : 30,
tintColor: routeName==='Home'?'#81F018' : '#262A2C',
}}
/>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.7}
onPress={() => {
navigation.navigate("Alt")
}}
style={ {position: 'relative',
width: PixelRatio.get() <= 2 ? 40 : 45,
height: PixelRatio.get() <= 2 ? 40 : 45,
alignItems: 'center',
justifyContent: 'center',
left: wp('5%'),
top: hp('11.5%'),
backgroundColor:routeName==="Alt" ? 'black' : 'white' ,
borderRadius: PixelRatio.get() <= 2 ? 40/2 : 45/2}}>
<Image
source={require("./Icons/category.png")}
//pay FlatIcon or design personal one
style={{ resizeMode: 'contain',
width: PixelRatio.get() <= 2 ? 25 : 30,
height: PixelRatio.get() <= 2 ? 25 : 30,
tintColor: routeName==='Alt'?'#81F018' : '#262A2C',
}}
/>
</TouchableOpacity>
</View>)}}
class Alt extends React.Component{
render(){
const {navigation} = this.props;
const { routeName } = navigation.state;
return(
<View style={{flex:1,
backgroundColor:'white'}}>
<TouchableOpacity
activeOpacity={0.7}
onPress={() => {
navigation.navigate("Home")
}}
style={ {position: 'relative',
width: PixelRatio.get() <= 2 ? 40 : 45,
height: PixelRatio.get() <= 2 ? 40 : 45,
alignItems: 'center',
justifyContent: 'center',
left: wp('5%'),
top: hp('11.5%'),
backgroundColor:routeName==="Home" ? 'black' : 'white' ,
borderRadius: PixelRatio.get() <= 2 ? 40/2 : 45/2}}>
<Image
source={require("./Icons/category.png")}
//pay FlatIcon or design personal one
style={{ resizeMode: 'contain',
width: PixelRatio.get() <= 2 ? 25 : 30,
height: PixelRatio.get() <= 2 ? 25 : 30,
tintColor: routeName==='Home'?'#81F018' : '#262A2C',
}}
/>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.7}
onPress={() => {
navigation.navigate("Alt")
}}
style={ {position: 'relative',
width: PixelRatio.get() <= 2 ? 40 : 45,
height: PixelRatio.get() <= 2 ? 40 : 45,
alignItems: 'center',
justifyContent: 'center',
left: wp('5%'),
top: hp('11.5%'),
backgroundColor:routeName==="Alt" ? 'black' : 'white' ,
borderRadius: PixelRatio.get() <= 2 ? 40/2 : 45/2}}>
<Image
source={require("./Icons/category.png")}
//pay FlatIcon or design personal one
style={{ resizeMode: 'contain',
width: PixelRatio.get() <= 2 ? 25 : 30,
height: PixelRatio.get() <= 2 ? 25 : 30,
tintColor: routeName==='Alt'?'#81F018' : '#262A2C',
}}
/>
</TouchableOpacity>
</View>)}}
const AppTabNavigator = createBottomTabNavigator({
"Home": {
screen: Home,
},
"Alt": {
screen: Alt,
},
},
{
defaultNavigationOptions: {
tabBarVisible: false
},
}
)
export default createAppContainer(AppTabNavigator)

Categories

Resources