What would be the way to achieve this.
callback with Animated.event works fine
callback with block gives no feedback :(
import React, { useState, useEffect } from "react";
import { View, Text, FlatList, Dimensions, Image } from "react-native";
import Animated from "react-native-reanimated";
import { PanGestureHandler, TapGestureHandler, State } from "react-native-gesture-handler";
const { Value, event, call, set, add, block } = Animated;
const bottom = new Value(100);
//want to achieve something like this
const onHandlerStateChange = block([set(bottom, add(bottom, 10))]);
// the line below Does work
// const onHandlerStateChange = event([{ nativeEvent: { y: bottom } }]);
const Comp = () => {
return (
<TapGestureHandler onHandlerStateChange={onHandlerStateChange}>
<Animated.View style={{ flex: 1}}>
<Animated.View style={{ flex: 1, position: "absolute", bottom: bottom }}>
<Text> some text</Text>
</Animated.View>
</Animated.View>
</TapGestureHandler>
);
};
export default Comp;
*const onHandlerStateChange = block([set(bottom, add(bottom, 10))]); * Doesn't work here
Here's the solution that works
Tip :
Understanding how reanimated evaluates nodes will be very useful
Here's the link to offical doc that might help you understand
https://software-mansion.github.io/react-native-reanimated/about-reanimated.html#at-most-once-evaluation-the-algorithm-
import React, { useState, useEffect } from "react";
import { View, Text, FlatList, Dimensions, Image } from "react-native";
import Animated from "react-native-reanimated";
import {
PanGestureHandler,
TapGestureHandler,
State,
} from "react-native-gesture-handler";
const { Value, event, call, set, add, cond, eq, block } = Animated;
const gstate = new Value(-1);
const onHandlerStateChange = event([{ nativeEvent: { state: gstate } }]);
let bottom = new Value(100);
bottom = cond(eq(gstate, State.END), [set(bottom, add(bottom, 10))], bottom);
const Comp = () => {
return (
<TapGestureHandler onHandlerStateChange={onHandlerStateChange}>
<Animated.View style={{ flex: 1, backgroundColor: "pink" }}>
<Animated.View
style={{ flex: 1, position: "absolute", bottom: bottom }}
>
<Text> some text</Text>
</Animated.View>
</Animated.View>
</TapGestureHandler>
);
};
export default Comp;
Related
I'm currently having a problem with the clickable size of a reusable button which includes an icon and text. When I run this code it seems like the entire row becomes clickable when I only want the icon and text to become clickable. Does anyone know how to solve this problem? Thanks
App.js
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
import IconTextButton from './components/iconTextButton';
export default function App() {
return (
<View style={styles.container}>
<Text style={{marginTop: 100}}>My First React App! Sike </Text>
<IconTextButton iconFont="ionicons" iconName="pencil" iconSize={25} text="Add Items"/>
</View>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'powderblue',
},
});
iconTextButton.js
import React from 'react';
import { StyleSheet, TouchableOpacity, Text, View } from 'react-native';
import Ionicon from 'react-native-vector-icons/Ionicons';
export default function IconTextButton({ iconFont, iconName, iconSize, text, onPress }) {
const getIconFont = (iconFont) => {
switch (iconFont) {
case "ionicons":
return Ionicon;
}
};
const FontIcon = getIconFont(iconFont);
return (
<TouchableOpacity onPress={onPress} style={styles(iconSize).container>
<FontIcon name={iconName} size={iconSize} style={styles(iconSize).buttonIcon}>
<Text style={styles(iconSize).buttonText}>{text}</Text>
</FontIcon>
</TouchableOpacity>
)
}
const styles = (size) => StyleSheet.create({
container: {
backgroundColor: 'pink',
},
buttonIcon: {
backgroundColor: 'yellow',
width: size,
},
buttonText: {
backgroundColor: 'green'
},
})
Along with the code I've tried, I've also tried to keep and as seperate contents whilst adding a flexDirection: 'row' inside styles.container. This keeps the contents in the same line but it still makes the whole row clickable. I've also tried putting everything in a and moving the styles.container to the component and adding a height: size into styles.container. This makes the clickable component limited however, the component is hidden underneath due to the restricted height. I have also tried simply just using instead of making a reusable const that its an input. The same thing applies.
You can wrap your Icon and Text Component in a View component and then wrap it inside a TouchableOpacity Component
Try this or do something like this :
import React from 'react';
import { StyleSheet, TouchableOpacity, Text, View } from 'react-native';
import Ionicon from 'react-native-vector-icons/Ionicons';
export default function IconTextButton({ iconFont, iconName, iconSize, text, onPress }) {
const getIconFont = (iconFont) => {
switch (iconFont) {
case "ionicons":
return Ionicon;
}
};
const FontIcon = getIconFont(iconFont);
return (
<TouchableOpacity onPress={onPress} style={styles(iconSize).container}>
<View style={styles(iconSize).iconTextContainer}>
<FontIcon name={iconName} size={iconSize} style={styles(iconSize).buttonIcon} />
<Text style={styles(iconSize).buttonText}>{text}</Text>
</View>
</TouchableOpacity>
)
}
const styles = (size) => StyleSheet.create({
container: {
backgroundColor: 'pink',
},
iconTextContainer: {
flexDirection: 'row',
alignItems: 'center',
},
buttonIcon: {
backgroundColor: 'yellow',
width: size,
},
buttonText: {
backgroundColor: 'green'
},
})
I am pretty new to React Native and reanimated so please bear with me if I cant explain this as I need to.
I have a FlatList that animates a single item using animted when clicked. This works great but the problem is I am trying to use it with code that uses reanimated and I get a lot of errors for some reason? I thought it was backwards compatible?
Because I need the animation to run only on a single item I can not use the reanimated way, it seems a lot harder to keep track of everything?
The code below the working code but if I change it to reanimated, no good.
Is there a way of converting the code to run properly when using reanimated or do I just have to start the animations again using reanimated animations from scratch?
The working code :-
import React, { useState, useEffect } from 'react';
import {
Text,
View,
StyleSheet,
FlatList,
TouchableOpacity,
Animated,
} from 'react-native';
import Constants from 'expo-constants';
const dummyData = [...Array(10)].map((_, i) => ({
title: `title ${i}`,
id: i,
}));
const App = () => {
const [Data, setData] = React.useState(dummyData);
const [activeItem, setActiveItem] = React.useState(null);
const animateValue = React.useRef(new Animated.Value(0)).current;
renderItem = ({ item, index }) => {
const animate = (index) => {
setActiveItem(index);
Animated.sequence([
Animated.spring(animateValue, {
toValue: 1,
}),
Animated.spring(animateValue, {
toValue: 0,
}),
]).start(() => console.log('animation finished'));
};
animationMap = animateValue.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.5],
});
return (
<TouchableOpacity onPress={(e) => animate(index)}>
{activeItem === index && (
<Animated.View
style={[styles.button, { transform: [{ scale: animationMap }] }]}>
<Text>{item.title}</Text>
</Animated.View>
)}
{activeItem !== index && (
<View style={styles.button}>
<Text>{item.title}</Text>
</View>
)}
</TouchableOpacity>
);
};
return (
<View style={styles.container}>
<FlatList
data={Data}
renderItem={renderItem}
keyExtractor={({ id }) => {
return id;
}}
extraData={activeItem}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
button: {
padding: 10,
borderWidth: 1,
alignItems: 'center',
backgroundColor: 'skyblue',
},
});
export default App;
If I change Animated from reanimated, it doesnt work?
import {
StatusBar,
Text,
View,
StyleSheet,
Image,
Dimensions,
TouchableOpacity,
Platform,
TouchableWithoutFeedback,
UIManager,
LayoutAnimation,
} from "react-native";
import { FlatList } from "react-native-gesture-handler";
import Animated, {
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
SlideInDown,
BounceOut,
BounceIn,
FadeIn,
FadeOut,
Easing,
withSpring,
withRepeat,
withTiming,
} from "react-native-reanimated";
or
import {
StatusBar,
Text,
View,
StyleSheet,
Image,
Dimensions,
TouchableOpacity,
Platform,
TouchableWithoutFeedback,
UIManager,
LayoutAnimation,
Animated,
} from "react-native";
import { FlatList } from "react-native-gesture-handler";
import {
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
SlideInDown,
BounceOut,
BounceIn,
FadeIn,
FadeOut,
Easing,
withSpring,
withRepeat,
withTiming,
} from "react-native-reanimated";
can you try using "useSharedValue" and set the value using
"withSequence" as below mentioned:
const App = () => {
const [Data, setData] = React.useState(dummyData);
const [activeItem, setActiveItem] = React.useState(null);
//const animateValue = React.useRef(new Animated.Value(0)).current;
const animateValue = useSharedValue(0);
renderItem = ({ item, index }) => {
const animate = (index) => {
setActiveItem(index);
animateValue.value = withSequence(withTiming(1), withTiming(0))
animationMap = animateValue.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.5],
});
return (
<TouchableOpacity onPress={(e) => animate(index)}>
{activeItem === index && (
<Animated.View
style={[styles.button, { transform: [{ scale: animationMap }] }]}>
<Text>{item.title}</Text>
</Animated.View>
)}
{activeItem !== index && (
<View style={styles.button}>
<Text>{item.title}</Text>
</View>
)}
</TouchableOpacity>
);
};
return (
<View style={styles.container}>
<FlatList
data={Data}
renderItem={renderItem}
keyExtractor={({ id }) => {
return id;
}}
extraData={activeItem}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
button: {
padding: 10,
borderWidth: 1,
alignItems: 'center',
backgroundColor: 'skyblue',
},
});
export default App;
I am trying to dim the background of this bottomSheet when it is activated or being rendered in the screen.
Also how do you make sure the bottomsheet disappears from the screen when the user touches the part of the screen that is not covered by the bottomSheet when it is active?
This is the code in app.js
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, View, ImageBackground, Text } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { TouchableOpacity } from 'react-native';
import BottomSheet from './components/BottomSheet';
import { useCallback, useRef } from 'react';
import Volcano from './Images/Volcano.jpg'
export default function App() {
const firstRef = useRef (null)
const onPress = useCallback (() => {
const isActive = firstRef?.current?.isActive1();
if (isActive) {
firstRef?.current?.scrollTo(35);
} else {
firstRef?.current?.scrollTo(-200);
}
})
return (
<GestureHandlerRootView style={{flex:1}}>
<ImageBackground source={Volcano} resizeMode='repeat' style={{
flex: 1,
width : '100%',
// flexDirection: 'column',
justifyContent: 'center',
}}>
<StatusBar style="auto" />
<TouchableOpacity style={{
height:50,
width: '10%',
backgroundColor:'green',
aspectRatio:1,
borderRadius:25,
opacity:.6,
marginLeft:360,
}} onPress={onPress}/>
<BottomSheet ref={firstRef}/>
</ImageBackground>
</GestureHandlerRootView>
);
};
This is the one in the bottomsheet.js
import { Dimensions, StyleSheet, Text, View } from 'react-native'
import React, { useCallback, useImperativeHandle } from 'react'
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, { event, Extrapolate, interpolate, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
const {height: SCREEN_HEIGHT} = Dimensions.get('window');
const MAX_TRANSLATE_Y = -SCREEN_HEIGHT + 50;
const BottomSheet = React.forwardRef(({},ref) => {
const translateY = useSharedValue(35);
const active = useSharedValue()
const scrollTo = useCallback ((destination = Number) =>{
'worklet';
active.value = destination !== 35;
translateY.value = withSpring(destination, {damping:15});
}, []);
const isActive1 = useCallback (()=> {
return active.value;
},[])
useImperativeHandle(ref, () => ({scrollTo, isActive1}), [scrollTo, isActive1]);
const updatePan = useSharedValue({y:0});
const activateGesture = Gesture.Pan()
.onStart(() => {
updatePan.value = { y:translateY.value};
})
.onUpdate((e) => {
translateY.value = e.translationY + updatePan.value.y;
translateY.value = Math.max(translateY.value, MAX_TRANSLATE_Y);
})
.onEnd (() => {
if (translateY.value > -SCREEN_HEIGHT/3){scrollTo(35) ;
} else if (translateY.value < -SCREEN_HEIGHT / 1.5) {
scrollTo(MAX_TRANSLATE_Y)
}
});
const rBottomSheetStyle = useAnimatedStyle (() => {
const borderRadius = interpolate(
translateY.value,
[MAX_TRANSLATE_Y + 100, MAX_TRANSLATE_Y],
[25, 5],
Extrapolate.CLAMP
);
return {
borderRadius,
transform: [{ translateY: translateY.value }],
};
});
return (
<GestureDetector gesture={activateGesture}>
<Animated.View
style= {[styles.bottomSheetContainer, rBottomSheetStyle]}
>
<View style={styles.line} />
</Animated.View>
</GestureDetector>
)
})
const styles = StyleSheet.create({
bottomSheetContainer: {
height: SCREEN_HEIGHT,
width:'100%',
backgroundColor: 'white',
position: 'absolute',
top: SCREEN_HEIGHT,
borderRadius: 25,
},
line:{
width: 75,
backgroundColor: 'grey',
height: 4,
alignSelf: 'center',
marginVertical: 15,
borderRadius:2,
}
})
export default BottomSheet
As you can see on the image the background is not dim when the bottomsheet is activatted.
[1]: https://i.stack.imgur.com/RdEzm.jpg
Please assist
I am creating a map application with two simple components: a search bar, using react-native-paper and a map, using react-native-maps. By now, only the UI is implemented.
When placing only the search bar, everything works fine. I am able to type my search query and the keyboard pops up when I click on the bar:
import { StatusBar } from 'expo-status-bar';
import * as React from 'react';
import MapView, { Marker } from 'react-native-maps';
import { StyleSheet, View, Dimensions, PermissionsAndroid } from 'react-native';
import {
Provider as PaperProvider,
DarkTheme,
Drawer,
DefaultTheme,
BottomNavigation,
Text,
Appbar,
Theme,
TextInput,
Searchbar,
} from 'react-native-paper';
const styles=StyleSheet.create({
searchBar:{
width: '90%',
position: 'absolute',
top: '7%',
alignSelf: 'center',
}
})
const MyComponent = () => {
const [searchQuery, setSearchQuery] = React.useState('');
const onChangeSearch = query => setSearchQuery(query);
return (
<View style={{width: "100%", height: "100%"}}>
<Searchbar style={styles.searchBar}
placeholder="Search"
onChangeText={onChangeSearch}
value={searchQuery}
/>
</View>
);
};
export default MyComponent;
After adding the map, nothing happens when clicking the search box. If I type something on my physical keyboard, the input seems to be captured as the icon displays an animation, but nothing else happens.
import * as React from 'react';
import MapView, { Marker } from 'react-native-maps';
import { StyleSheet, View, Dimensions, PermissionsAndroid } from 'react-native';
import {
Provider as PaperProvider,
DarkTheme,
Drawer,
DefaultTheme,
BottomNavigation,
Text,
Appbar,
Theme,
TextInput,
Searchbar,
} from 'react-native-paper';
const styles=StyleSheet.create({
searchBar:{
width: '90%',
position: 'absolute',
top: '7%',
alignSelf: 'center',
}
})
const MyComponent = () => {
const [searchQuery, setSearchQuery] = React.useState('');
const { height, width } = Dimensions.get( 'window' );
const onChangeSearch = query => setSearchQuery(query);
return (
<View style={{width: "100%", height: "100%"}}>
<Searchbar style={styles.searchBar}
placeholder="Search"
onChangeText={onChangeSearch}
value={searchQuery}
/>
<MapView
style={{flex: 1}}
showsUserLocation={true}
showsMyLocationButton={true}
//onMapReady={_onMapReady}
region={{
latitude: -8.327352784611014,
longitude: -36.14413540428909,
latitudeDelta: 55.5,
longitudeDelta: 55.5 * (width / height),
}}></MapView>
</View>
);
};
export default MyComponent;
Any help is appreciated! This is my first react project.
have you tried messing around with zIndex?
maybe adding zIndex: 10 to the text input style
You have to change the order of TextInput and Mapview as you can find in their readme file: https://github.com/react-native-maps/react-native-maps#inputs-dont-focus
Bad:
<View>
<TextInput/>
<MapView/>
</View>
Good:
<View>
<MapView/>
<TextInput/>
</View>
I am a beginner in React-Native.
I tried to change the state value.
But it seems different with "class" mode. Now I am using "const" mode.
In const mode, how can I change the state value?
import React from "react";
import {
View,
Text,
Image,
TouchableOpacity
} from "react-native";
const Product = props => {
this.state = {
checkIconURL : require('../../assets/ic_check_circle.png')
}
set = () => {
this.setState({ checkIconURL : require('../../assets/empty.png') }) //But this lines gives err.
}
return (
<View>
<Text style={{ fontSize: 16}}>Everyday</Text>
<TouchableOpacity onPress = {this.set}>
<Image source = {this.state.checkIconURL} style={{width: 30, height: 30}} />
</TouchableOpacity>
</View>
)
}
state is not available in function components, this.state and this.setState() can only be used in class component, u have to use useState hook from react to use the state...
import React ,{ useState } from "react";
import {
View,
Text,
Image,
TouchableOpacity
} from "react-native";
const Product = props => {
const initialState = require('../../assets/ic_check_circle.png');
const [iconUrl, setIconUrl ] = useState(initialState);
const set = () => {
const emptyImageUrl = require('../../assets/empty.png')
setIconUrl(emptyImageUrl);
}
return (
<View>
<Text style={{ fontSize: 16}}>Everyday</Text>
<TouchableOpacity onPress = {set}>
<Image source = {iconUrl} style={{width:
30, height: 30}} />
</TouchableOpacity>
</View>
)
}
the following code is incorrect as you need to use Hook in a functional component to manage state.
React provides the useState hook to manage state in a functional component.
import React ,{ useState } from "react";
import {
View,
Text,
Image,
TouchableOpacity
} from "react-native";
const Product = props => {
const initialState = require('../../assets/ic_check_circle.png');
const [ state,setState ] = useState({checkIconURL: initialState});
set = () => {
const emptyImageUrl = require('../../assets/empty.png') });
setState({...state, checkIconURL: emptyImageUrl })
}
return (
<View>
<Text style={{ fontSize: 16}}>Everyday</Text>
<TouchableOpacity onPress = {this.set}>
<Image source = {this.state.checkIconURL} style={{width:
30, height: 30}} />
</TouchableOpacity>
</View>
)
}