I have a Flatlist rendered with the following ListItemSwipeable and they hold as renderRightActions the RightActionsButton function. Swiping works and the button appears. However, I can not achieve that the button disappears when another element is swiped or the button is pressed.
const RightActionsButton = ({ progress, dragX, onPress }) => {
const scale = dragX.interpolate({
inputRange: [-100, 0],
outputRange: [1, 0],
extrapolate: "clamp"
});
return (
<TouchableOpacity onPress={onPress}>
<View>
<Animated.Text style={{ transform: [{ scale }] }}>
Button!
</Animated.Text>
</View>
</TouchableOpacity>
);
};
const ListItemSwipeable = ({data, onAction}) => {
return (
<Swipeable
renderRightActions={(progress, dragX) => (
<RightActionsButton progress={progress} dragX={dragX} onPress={onAction} />
)}
>
<View>
<Text>{data}</Text>
</View>
</Swipeable>
)
}
};
There are some hints here, however it is class based and I would need it for function based component.
Related
I have a Floating Action Button with two internal buttons, the idea is that after clicking on the first FAB (Floating Action Button) the others are displayed, what I require is that I can click on one of the buttons that was displayed and send me to the login or to another tab, page, route.
The error I have is that I don't know where I can do the "const navigation = useNavigation()" to use the navigator and send the user to another tab
i tried to use this.props, but i couldn't get it
export default class FloatingButton extends React.Component {
handleSignOut = () => {
Alert.alert("Position 1")
authentication
.signOut()
.then(() => {
this.props.navigation.navigate("Login")
})
.catch(error => alert(error.message))
}
handleSignOut2 = () =>{Alert.alert("Position 2")}
animation = new Animated.Value(0);
toggleMenu = () => {
const toValue = this.open ? 0 : 1;
Animated.spring(this.animation, {
toValue,
friction: 5,
useNativeDriver: false
}).start();
this.open = !this.open;
};
render() {
const pinStyle2 = {
transform: [
{ scale: this.animation },
{
translateY: this.animation.interpolate({
inputRange: [0, 1],
outputRange: [0, -10]
})
}
]
};
const pinStyle = {
transform: [
{ scale: this.animation },
{
translateY: this.animation.interpolate({
inputRange: [0, 1],
outputRange: [0, -20]
})
}
]
};
const rotation = {
transform: [
{
rotate: this.animation.interpolate({
inputRange: [0, 1],
outputRange: ["0deg", "45deg"]
})
}
]
};
const opacity = this.animation.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [0, 0, 1]
})
return (
<View style={[styles.container, this.props.style]}>
<TouchableWithoutFeedback onPress={this.handleSignOut}>
<Animated.View style={[styles.button, styles.secondary, styles.menu, pinStyle, opacity]}>
<Entypo name="add-to-list" size={24} color="white" />
</Animated.View>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={this.handleSignOut2}>
<Animated.View style={[styles.button, styles.secondary, styles.menu, pinStyle2, opacity]}>
<Entypo name="check" size={24} color="white" />
</Animated.View>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={this.toggleMenu}>
<Animated.View style={[styles.button, styles.menu, rotation]}>
<AntDesign name="plus" size={24} color="white" />
</Animated.View>
</TouchableWithoutFeedback>
</View>
);
}
According to the react documentation "You can’t use Hooks inside a class component". (https://reactjs.org/docs/hooks-faq.html). And thats what you're doing useNavigation is a hook and you're calling it inside a class. So either
- Change your class into a function
or
- Find a way to express the same functionality as the useNavigation hook but in a class
I have a React Native FlatList with a ListHeaderComponent with 2 internal Text. The structure is:
header
section 1 - non sticky
section 2 - sticky
list items
This means that as the list scrolls up, Section 1 should disappear (non-sticky) whereas section 2 should stay at the top of the list (sticky).
This is the code:
<FlatList
data={ items }
renderItem={ renderItem }
ListHeaderComponent={
<View>
<Text>Section 1</Text>
<Text>Section 2</Text>
</View>
}
stickyHeaderIndices={[0]}
/>
I have to set the indices to [0] so it picks the header but there is no way to select the second within the header. Any ideas?
BTW - I thought of capturing the vertical offset as the list scrolls and then put on the HeaderComponent main <View style={{marginTop: -offset }}> so it simulates a scroll. But my understanding is that Android does not support negative margins.
BTW-2 - I am using react-native-draggable-flatlist so I wouldn't like to put the Text in the list itself as it would complicated the logic of the list items.
Thanks!
This worked for me when trying to use SectionList
import { View, Animated, FlatList, Text } from 'react-native';
const ITEM_H = 30;
const items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'];
const renderItem = ({ item }) => <Text style={{ height: ITEM_H }}>{item}</Text>;
export default () => {
// animated value and interpolate setting
const offset = React.useRef(new Animated.Value(0)).current;
const animValue = offset.interpolate({
inputRange: [0, ITEM_H],
outputRange: [ITEM_H, 0],
extrapolate: 'clamp',
});
// sticky second item and FlatList
return (
<SafeAreaView>
<View style={{ position: 'relative' }}>
<Animated.Text
style={{
backgroundColor: 'red',
position: 'absolute',
top: animValue,
height: ITEM_H,
zIndex: 10,
}}>
{items[1]}
</Animated.Text>
<FlatList
data={items}
renderItem={renderItem}
onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: offset } } }], {
useNativeDriver: false,
})}
/>
</View>
</SafeAreaView>
);
};
How about using Animated API?
You basically set the fixed height for each items.
Only the second item (sticky one) is outside of the FlatList and positioned absolute.
Sticky item has its offset as the item height.
As you scroll up, the offset value will be interpolated into zero, which means sticks at the top.
Working example below.
import { View, Animated, FlatList, Text } from 'react-native';
const ITEM_H = 30;
const items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'];
const renderItem = ({ item }) => <Text style={{ height: ITEM_H }}>{item}</Text>;
export default () => {
// animated value and interpolate setting
const offset = React.useRef(new Animated.Value(0)).current;
const animValue = offset.interpolate({
inputRange: [0, ITEM_H],
outputRange: [ITEM_H, 0],
extrapolate: 'clamp',
});
// sticky second item and FlatList
return (
<SafeAreaView>
<View style={{ position: 'relative' }}>
<Animated.Text
style={{
backgroundColor: 'red',
position: 'absolute',
top: animValue,
height: ITEM_H,
zIndex: 10,
}}>
{items[1]}
</Animated.Text>
<FlatList
data={items}
renderItem={renderItem}
onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: offset } } }], {
useNativeDriver: false,
})}
/>
</View>
</SafeAreaView>
);
};
I just found a different way to get this done, not sure if it's best practice though, but couldn't find a different method.
Instead of placing both components inside the header, one sticky and the other not, you can add an item to the front of the array that you'll pass to the data property of the flatlist
const getData = (data: number[]) => {
const updatedData = ["sticky header", ...data]
return updatedData;
}
const YourListComponent = () => {
const someData = [1, 2, 3, 4]
return
<FlatList
stickyHeaderIndices={[1]} // 0 represents the ListHeaderComponent, 1 represents the first item in the data array
ListHeaderComponent={
<View>
<Text>Header</Text>
</View>
}
data={getData(someData)}
renderItem={({ item }) => (
<>
{typeof item === "string" && (
<View>
<Text>{item}</Text>
</View>
)}
{typeof item !== "string" && (
<View>
<Text>{item}</Text>
</View>
)}
</>
)}
/>
}
You need to set prop to Flatlist as stickyHeaderIndices={[0]}
Hello! I recently started learning React Native and I want to make two colors constantly flash on this button ((( Please check my code, i can't get it to work. It should of course work on two platforms IOS & Android
const InviteChildButton = ({ changeInvite }: IProps) => {
const { invitation } = useTranslation();
const { username } = useSelector((state: RootState) => state.child);
const animatedColor = new Animated.Value(0);
const backgroundColorAnimation = animatedColor.interpolate(
{
inputRange: [0, 0.5,],
outputRange: ['rgba(113, 255, 139, 0.1)', 'rgba(255, 169, 139, 0.1)']
});
useEffect(() => {
Animated.timing(backgroundColorAnimation, {
toValue: 1,
duration: 5000,
useNativeDriver:true
}).start();
}, []);
return (
<View style={styles.container}>
<View>
<CustomText
style={styles.text}
text={`${invitation} ${username}`}
variant='h3'
type='normal'
/>
</View>
<Animated.View style={[styles.button, {backgroundColor: backgroundColorAnimation }]}>
<TouchableOpacity onPress={changeInvite}>
<Image style={styles.image} source={Images.inviteLogoButton} />
</TouchableOpacity>
</Animated.View>
</View>
);
};
export default InviteChildButton;
Animated.timing expects animated value, try to change backgroundColorAnimation to animatedColor and disable native driver, I don't think it supports background color interpolation.
I have an animation header and I could change the height by scrolling the view.
My question I want the animation to start when scrollview passes a certain point.
In the gif, animation is immediately activated when I scroll the view. I want the animation to start when when I start the see the yellow view.
This is my code:
constructor(props) {
super(props)
this.state = {
scrollY:new Animated.Value(0)
}
}
render() {
const HeaderHeight = this.state.scrollY.interpolate({
inputRange: [0, 200],
outputRange: [200, 0],
extrapolate: 'clamp'
})
return (
<View style={styles.container}>
<Animated.View style={{width:'100%', height:HeaderHeight, backgroundColor:'red', justifyContent:'center', alignItems:'center'}}>
<Text>Animated Header </Text>
</Animated.View>
<ScrollView
scrollEventThrottle={16}
onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: this.state.scrollY } } }])}
style={{width:'100%', backgroundColor:'gray'}}>
<View style={{width:'100%', height:500, backgroundColor:'blue'}}/>
<View style={{width:'100%', height:500, backgroundColor:'green'}}/>
<View style={{width:'100%', height:500, backgroundColor:'yellow'}}/>
</ScrollView>
</View>
);
}
}
I figured it out.
I get the y position like the code below.
onLayout = (e) => {
this.setState({
y: e.nativeEvent.layout.y
})
}
In my scrollview I apply it like this.
<ScrollView
onLayout={this.onLayout}/>
Then I could conditionally use the y position to activate the animation.
I am trying to make the screen flip like a coin when button pressed on it. What I have done up-till now is I have successfully flipped two small screens, but it comes to a bit large screen the code is not working. Like here in my code two simple buttons are working fine, but when I call view from other big screen, the view doesn't show up. I think it is because the view that will is flipped becomes invisible but remains on its place and other view can't be placed on that space, so when it comes to larger screen the whole thing doesn't show up. I am new to RN. Help.
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableOpacity,
Animated
} from 'react-native';
var screenWidth = require('Dimensions').get('window').width;
export default class animatedbasic extends Component {
componentWillMount() {
this.animatedValue = new Animated.Value(0);
this.value = 0;
this.animatedValue.addListener(({ value }) => {
this.value = value;
})
this.frontInterpolate = this.animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['0deg', '180deg'],
})
this.backInterpolate = this.animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['180deg', '360deg']
})
this.frontOpacity = this.animatedValue.interpolate({
inputRange: [89, 90],
outputRange: [1, 0]
})
this.backOpacity = this.animatedValue.interpolate({
inputRange: [89, 90],
outputRange: [0, 1]
})
}
flipCard() {
if (this.value >= 90) {
Animated.spring(this.animatedValue,{
toValue: 0,
friction: 8,
tension: 10
}).start();
} else {
Animated.spring(this.animatedValue,{
toValue: 180,
friction: 8,
tension: 10
}).start();
}
}
render() {
const frontAnimatedStyle = {
transform: [
{ rotateY: this.frontInterpolate }
]
}
const backAnimatedStyle = {
transform: [
{ rotateY: this.backInterpolate }
]
}
return (
<View style={{ flex:1, justifyContent:'center', alignItems:'center'}} >
<View >
<Animated.View style={[styles.flipCard, frontAnimatedStyle, {opacity: this.frontOpacity}]}>
</Animated.View>
<Animated.View style={[styles.flipCard, styles.flipCardBack, backAnimatedStyle, {opacity: this.backOpacity}]}>
<View>
<TouchableOpacity onPress={() => this.flipCard()} style={{ width:(screenWidth/2), height:70, backgroundColor:'black'}}>
<Text style={{color:'white', fontSize:22, fontWeight:'bold'}}> I am on Back</Text>
</TouchableOpacity>
</View>
</Animated.View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
flipCard: {
backfaceVisibility: 'hidden',
},
flipCardBack: {
top: 0,
},
});
I have also tried react-native-card-flip
Flipping.js
render() {
return (
<CardFlip style={styles.cardContainer} ref={(card) => this.card
=card}>
<FronEnd />
<Backend />
</CardFlip> }
FronEnd.js
render()
{
return (
<View>
<CardFlip style={styles.cardContainer} ref={(card) => this.card = card} >
....................................................
<TouchableOpacity onPress={() => this.card.flip()}
</TouchableOpacity >
</CardFlip>
</View>
);
}
}
Backend.js
render()
{
return (
<View>
<CardFlip style={styles.cardContainer} ref={(card) => this.card = card} >
.......................
<TouchableOpacity onPress={() => this.card.flip()}
</TouchableOpacity >
</CardFlip>
</View>
);
}
You can use a npm module for this :
Installation :
npm install --save react-native-card-flip
Usage :
import CardFlip from 'react-native-card-flip';
<CardFlip style={styles.cardContainer} ref={(card) => this.card = card} >
<TouchableOpacity style={styles.card} onPress={() => this.card.flip()} ><Text>AB</Text></TouchableOpacity>
<TouchableOpacity style={styles.card} onPress={() => this.card.flip()} ><Text>CD</Text></TouchableOpacity>
</CardFlip>
See Output
Npm module : Github