Im trying to see if I can add a button on top of the keyboard when its called i've seen this feature in many apps but not sure if its possible on expo and react-native. the button should appear with the keyboard and become invisible when the pressed or the keyboard goes away. Ive put a my sample class that goes with the added image
import React, {Component} from 'react';
import {
StyleSheet,
View,
TextInput
} from 'react-native';
export default class test extends Component {
constructor(props) {
super(props);
this.state = {
a: props.navigation,
b: '',
}
}
componentWillMount() {
}
render() {
return (
<View style={styles.container}>
<TextInput
style={{marginTop: 300,marginLeft: 50,borderWidth: 1}}
placeholder="type"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 22,
backgroundColor: '#42444f',
},
});
try this approach
constructor(props)
{
super(props)
this.state = {
buttonVisible: false // add this state. this state is the flag for button appearance
}
}
componentDidMount() {
this.keyboardDidShowListener = Keyboard.addListener(
'keyboardDidShow',
this._keyboardDidShow,
);
this.keyboardDidHideListener = Keyboard.addListener(
'keyboardDidHide',
this._keyboardDidHide,
);
}
_keyboardDidShow = () => {
this.setState({
buttonVisible: true
})
}
_keyboardDidHide = () => {
this.setState({
buttonVisible: false
})
}
render() {
return (
<View style={styles.container}>
<View style={{ flex: 4 }}>
<TextInput
style={{marginTop: 300,marginLeft: 50,borderWidth: 1}}
placeholder="type"
/>
</View>
<View style={{ flex: 1 }}>
{this.state.buttonVisible ? <TouchableOpacity style={{ flex: 1, backgroundColor: 'red', justifyContent: 'center', alignItems: 'center' }}><Text>Button</Text></TouchableOpacity> : <View/>}
</View>
</View>
);
}
the button will be visible depending on the buttonVisible state value, the state changed inside the keyboard event listener that initialized in componentDidMount
Related
import React, { Component } from 'react';
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
class App extends Component {
constructor(props){
super(props)
this.state={
selected:null
}
}
handle=()=>{
this.setState({selected:1})
}
render() {
return (
<View>
<TouchableOpacity style={[styles.Btn, {backgroundColor:this.state.selected===1?"green":"white"}]} onPress={this.handle}>
<Text style={styles.BtnText}>Button 1</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.Btn} onPress={this.handle}>
<Text style={styles.BtnText}>Button 2</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.Btn} onPress={this.handle}>
<Text style={styles.BtnText}>Button 3</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
Btn: {
borderWidth: 1,
width: 100,
height: 20,
borderRadius: 8,
margin: 5,
padding: 10,
justifyContent: 'center',
flexDirection: 'row',
alignItems: 'center',
},
BtnText: {
fontSize: 15,
},
});
export default App;
Snack Link : https://snack.expo.dev/U_fX-6Tao-
I want to make it so when I click a button, the active button backgroundColor should change to "green" and text to "white" and the rest of the buttons backgroundColor and textColor should stay "red". But when I click another button then that button should become active and the previous active button should get back to its normal state.
It would be wonderful if you could also explain the logic behind it as I'm a newbie in React Native.
Thank you.
You are always setting the active button to the first one. Also, I would use an array to render the buttons. I would do something like this:
class App extends Component {
constructor(props){
super(props)
this.state = {
selected: null
}
}
handlePress = (item) => {
this.setState({ selected: item })
}
render() {
return (
<View>
{[...Array(3).keys()].map((item) => {
return (
<TouchableOpacity key={item} style={[styles.Btn, {backgroundColor: this.state.selected === item ? "green" : "white" }]} onPress={() => this.handlePress(item)}>
<Text style={styles.BtnText}>Button {item + 1}</Text>
</TouchableOpacity>
)
})}
</View>
);
}
}
I created an Themed component(OK I did not create it. It is there when I create the app with Expo).
import { useState } from 'react';
import { TouchableOpacity as DefaultTouchableOpacity } from 'react-native';
export type TouchableProps = DefaultTouchableOpacity['props'] & { activeBgColor?: string };
export function TouchableOpacity(props: TouchableProps) {
const [active, setActive] = useState(false);
const { style, activeBgColor, ...otherProps } = props;
if (activeBgColor) {
return (
<DefaultTouchableOpacity
style={[style, active ? { backgroundColor: activeBgColor } : {}]}
activeOpacity={0.8}
onPressIn={() => setActive(true)}
onPressOut={() => setActive(false)}
{...otherProps}
/>
);
}
return <DefaultTouchableOpacity style={style} activeOpacity={0.8} {...otherProps} />;
}
Then I use this TouchableOpacity everywhere.
<TouchableOpacity
style={tw`rounded-sm h-10 px-2 justify-center items-center w-1/5 bg-sky-400`}
activeBgColor={tw.color('bg-sky-600')}
>
<Text style={tw`text-white font-bold`}>a Button</Text>
</TouchableOpacity>
Oh I am writing TailwindCSS with twrnc by the way. You will love it.
See the screenshot below.
I've a couple of buttons in my react native app, on pressing any button, color should change. Code is as given below
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
TextInput,
Button,
TouchableHighlight,
Image,
Alert
} from 'react-native';
export default class Home extends Component {
constructor(props) {
super(props);
this.state= {
clicks: 0,
show: true,
isbuttonpress: false
};
}
onButtonState = () => {
this.setState({isbuttonpress: true});
}
render() {
return (
<View style={ [styles.container] }>
<Text style= { [styles.header] }>How likely is it that you would recommend this company to a friend or colleague?</Text>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>0</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>1</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>2</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>3</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>4</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>5</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.nextContainer, styles.goback] } onPress = { () => this.onNextButtonState() }>
<Text style = { styles.gobacktext }>Next</Text>
</TouchableHighlight>
</View>
);
}
}
const styles = StyleSheet.create({
header: {
fontSize: 20,
alignItems: 'center',
padding: 20,
},
container: {
alignItems: 'center',
justifyContent: 'center'
},
buttonContainer: {
height:45,
justifyContent: 'center',
alignItems: 'center',
marginBottom:20,
width:45,
}, gobackred: {
backgroundColor: "#00a2b2",
},
goback: {
backgroundColor: "#00b5ec",
},
gobacktext: {
color: 'white',
},
nextContainer: {
height:45,
marginTop: 15,
justifyContent: 'center',
alignItems: 'center',
marginBottom:20,
width:250,
borderRadius:30,
}
})
Layout is as given below:
Whenever any option is selected, color of that button should change. But in my code, color of all buttons changes when clicked on any button. How can I fix it?
This happens because you used one state this.state.isbuttonpress for all button try to make the component of this code.
your TouchableHighlight Component
DemoButton.js
import React, { Component } from "react";
import { View, Text, TouchableHighlight } from "react-native";
export default class DemoButton extends Component {
constructor(props) {
super(props);
this.state = { isbuttonpress: false };
}
onButtonState = () => {
this.setState({ isbuttonpress: !this.state.isbuttonpress });
};
render() {
return (
<View>
<TouchableHighlight
style={{ backgroundColor: this.state.isbuttonpress ? "red" : "blue" }}
onPress={this.onButtonState}
>
<Text>{this.props.buttonTitle}</Text>
</TouchableHighlight>
</View>
);
}
}
App.js
import DemoButton from "./DemoButton";
export default class Heal extends Component {
constructor(props) {
super(props);
this.state = { isbuttonpress: false };
}
render() {
return (
<View>
<Header />
<DemoButton buttonTitle="0"/>
<DemoButton buttonTitle="1"/>
<DemoButton buttonTitle="2"/>
<DemoButton buttonTitle="3"/>
<DemoButton buttonTitle="4"/>
<DemoButton buttonTitle="5"/>
</View>
)
}
}
Please, look at the debugger and the screen for what could be a problem. However, the code is highly displayed below for your perusal.
More also, I aimed at navigating to another page based on the id of the selected content.
App.js
The App.js is where I defined my stackNavigator
import React, {Component} from 'react';
import { StyleSheet, Text, View} from 'react-native';
import Post from './components/Post';
import PostSingle from './components/PostSingle';
import { createStackNavigator, createAppContainer } from 'react-navigation';
const RootStack = createStackNavigator(
{
PostScreen: { screen: Post},
PostSingleScreen:{screen: PostSingle},
},
{
initialRouteName: "PostScreen"
}
);
const AppNavigator = createAppContainer(RootStack);
export default class App extends Component {
constructor(props) {
super(props);
};
render() {
return (
<View style={styles.container}>
<AppNavigator/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#F3F3F3',
}
});
Post.js
I have tried to delete alignItem = center. In fact I deleted my style to see if there is any blocking the screen from coming up.
import React, { Component } from 'react';
import {
ScrollView,
StyleSheet,
View,
Text,
InputText,
TouchableOpacity
} from 'react-native';
import axios from 'axios';
export default class Post extends Component{
constructor(props){
super(props);
this.state = {
posts: []
}
}
readMore = () => {
()=> this.props.navigation.navigate('PostSingleScreen');
debugger;
}
componentDidMount(){
axios.get(`http://localhost/rest_api_myblog/api/post/read.php`)
//.then(json => console.log(json.data.data[0].id))
.then(json => json.data.data.map(mydata =>(
{
title: mydata.title,
body: mydata.body,
author: mydata.author,
category_name: mydata.category_name,
id: mydata.id
}
)))
//.then(newData => console.log(newData))
.then(newData => this.setState({posts: newData}))
.catch(error => alert(error))
}
render(){
return (
<View>
<ScrollView style={styles.scrollContent}>
<View style={styles.header}>
<Text style={styles.headerText}>Gist Monger</Text>
</View>
{
this.state.posts.map((post, index) =>(
<View key={index} style={styles.container}>
<Text style={styles.display}>
Author: {post.author}
</Text>
<Text style={styles.display}>
Category: {post.category_name}
</Text>
<Text style={styles.display}>
Title: {post.title}
</Text>
<Text style={{overflow:'hidden'}}>
Id: {post.id}
</Text>
<TouchableOpacity style={styles.buttonContainer}
onPress = {() => this.readMore()}
>
<Text style={styles.buttonText}>
Read More
</Text>
</TouchableOpacity>
</View>
))
}
</ScrollView>
<View style={styles.footer}></View>
</View>
);
}
}
const styles = StyleSheet.create({
header: {
flex: 1,
height:40,
marginTop:50,
marginBottom:10,
flexDirection: 'row',
justifyContent:'center',
},
display: {
margin: 3,
fontSize: 16
},
headerText: {
fontWeight: 'bold',
fontSize: 40,
color: '#6200EE'
},
container: {
backgroundColor:'#efefef',
padding: 20,
margin: 5,
borderRadius:20,
justifyContent: 'center',
alignItems: 'center'
},
footer: {
flex: 1,
backgroundColor:'#000',
marginBottom:50
},
buttonContainer:{
height: 30,
width: 200,
marginTop: 15,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 15,
backgroundColor:'#6200EE'
},
buttonText: {
alignContent: 'center',
color: 'white'
}
});
PostSingle.js
import React, { Component } from 'react';
import {
StyleSheet,
View,
Text
} from 'react-native';
import axios from 'axios';
export default class Post extends Component{
constructor(props){
super(props);
}
render(){
return (
<View>
<Text>My text</Text>
</View>
);
}
}
const styles = StyleSheet.create({
});
I did not test this code, but try to add flex: 1 to your container style. the main containers/components don't stretch if you don't tell them to
const styles = StyleSheet.create({
container: {
backgroundColor: '#F3F3F3',
flex: 1,
}
});
also, to check if the components render (helps debugging where the problem is), write a console log in every component's componentDidMount. if they mount, but nothing is visible, it's most likely a CSS issue. If it wasn't, it would throw errors instead of blank screen
second issue is, when you navigate you need to have params with react-navigation. the syntax for it is like this:
this.props.navigation.navigate('PostSingleScreen', { params })
so, if you have { id: someId } in your params, in the component you navigated to you will have {this.props.navigation.state.params.id}. so basically those params are inside navigation.state.params where you navigate
Let me help you with your second question. First, it is easier as said by just specifying params in your navigation.
For instance,
readMore = (id) => {
this.props.navigation.navigate('PostSingleScreen', {id:id})
}
However, in your TouchableOpacity, onPress method i.e. onPress = {() => this.readMore(post.id)}
In your PostSingle.js
import React, { Component } from 'react';
import {
StyleSheet,
View,
Text,
Button
} from 'react-native';
import axios from 'axios';
class PostSingle extends Component{
constructor(props){
super(props);
this.state = {
posts: []
}
}
componentDidMount() {
const id = this.props.navigation.state.params.id;
axios.get(`http://localhost/rest_api_myblog/api/post/read_single.php?id=${id}`)
.then(json => json.data)
.then(newData => this.setState({posts: newData}))
.catch(error => alert(error))
}
render(){
return (
<View style={styles.container}>
<Text style={styles.display}>
{this.state.posts.title}
</Text>
<Text style={styles.display}>
{this.state.posts.author}
</Text>
<Text style={styles.display}>
{this.state.posts.category_name}
</Text>
<Text style={styles.display}>
{this.state.posts.body}
</Text>
</View>
);
}
}
I hope it helps
i would suggest using flaltist for this, and not this.state.map. this should give you the same outcome
readMore(id){
//do whatever you want with the id
this.props.navigation.navigate('PostSingleScreen',{id:id}); //or pass it as a prop
debugger;
}
renderItem = ({ item, index }) => {
return (
<View key={index} style={styles.container}>
<Text style={styles.display}>
Author: {item.author}
</Text>
<Text style={styles.display}>
Category: {item.category_name}
</Text>
<Text style={styles.display}>
Title: {item.title}
</Text>
<Text style={{overflow:'hidden'}}>
Id: {item.id}
</Text>
<TouchableOpacity style={styles.buttonContainer}
onPress = {() => this.readMore(item.id)}
>
<Text style={styles.buttonText}>
Read More
</Text>
</TouchableOpacity>
</View>
);
};
render(){
return (
<View style={{flex:1}}>
<FlatList
style={{flex:1}}
data={this.state.posts}
renderItem={this.renderItem}
numColumns={1}
keyExtractor={(item, index) => item.id} //this needs to be a unique id
ListHeaderComponent = {
<View style={styles.header}>
<Text style={styles.headerText}>Gist Monger</Text>
</View>}
/>
<View style={styles.footer}/>
</View>
);
}
If you are using react-navigation 5.x then it might be possible that you are adding CSS
alignItems: "center"
to your root file i.e. App.js or index.js I just removed it and it starts working for me.
I'm trying to render a Flatlist that contains a store name. Upon clicking the store name, more information about the store should be displayed.
I attempted to change state and then use
{this.renderMoreDetails(item) && this.state.moreDetailsShown}
to get the additional information to appear. However, it was clear via console.log that state was only changed after a second button press.
From what I read in this article 1 and this article 2 it seemed as though I needed a callback function as an additional parameter to my setState().
I tried adding a callback function(probably incorrect, but I'm extremely lost at this point) and only got more errors.
I know that I have access to all of the data from renderItem inside FlatList. How do I make it appear upon click?
import React, { Component } from 'react';
import {
View,
FlatList,
Text,
TouchableWithoutFeedback,
ListView,
ScrollView
} from 'react-native'
import { NavigationActions } from 'react-navigation';
import { Header, Card, CardSection } from '../common';
import Config from '../Config';
export default class Costco extends Component<Props> {
constructor(props) {
super(props);
this.state = {
stores: [],
selectedItem: {id: null},
};
}
componentWillMount() {
const obj = Config.costcoThree;
const costcoArr = Object.values(obj);
this.setState({
stores: costcoArr,
})
}
renderListOfStores() {
return <FlatList
data={this.state.stores}
renderItem={ ({item}) => this.singleStore(item)}
keyExtractor={(item) => item.id}
extraData={this.state.selectedItem} />
}
singleStore(item) {
console.log(this.state.selectedItem.id)
return item.id === this.state.selectedItem.id ?
<TouchableWithoutFeedback
onPress={() => this.selectStore(item)}
>
<View>
<Text>{item.branchName}</Text>
<Text>Opening Time {item.openingTime}</Text>
<Text>Closing Time {item.closingTime}</Text>
<Text>Address {item.dongAddKor}</Text>
</View>
</TouchableWithoutFeedback>
:
<TouchableWithoutFeedback>
<View>
<Text>{item.branchName}</Text>
<Text>Only showing second part</Text>
</View>
</TouchableWithoutFeedback>
}
selectStore(item) {
this.setState({selectedItem: item});
console.log('repssed');
}
render() {
return(
<ScrollView>
<Header headerText="Costco"/>
<Text>hello</Text>
{this.renderListOfStores()}
</ScrollView>
)
}
}
as a workaround you can have a state that maintain your selected item to expand(or something like another view) and then you can use some conditions to work for render your flat list data.
Consider the below code and let me know if you need some more clarification.
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,FlatList,TouchableNativeFeedback,
View
} from 'react-native';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
response: [],
selectedItem: {id: null},
};
}
userListLayout() {
const {response} = this.state;
return <FlatList data={response} renderItem={ ({item}) => this.singleUserLayout(item)} keyExtractor={(item) => item.email} extraData={this.state.selectedItem} />
}
singleUserLayout(item) {
return item.id == this.state.selectedItem.id ?
<TouchableNativeFeedback onPress={() => this.userSelected(item)}>
<View style={{flex:1, borderColor: 'black', borderWidth: 1}}>
<Text style={{padding: 10, fontSize: 25}}>{item.email}</Text>
<Text style={{padding: 10,fontSize: 20}}>{item.name}</Text>
</View>
</TouchableNativeFeedback>
: <TouchableNativeFeedback onPress={() => this.userSelected(item)}><Text style={{padding: 10,fontSize: 20}}>{item.email}</Text></TouchableNativeFeedback>
}
userSelected(item) {
console.log(item)
this.setState({selectedItem: item})
}
componentDidMount() {
fetch("https://jsonplaceholder.typicode.com/users")
.then(data => data.json())
.then(response => this.setState({response}))
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>HI THERE</Text>
{this.userListLayout()}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
Using react-navigation with react-native.
How can jeg run a function at the end of animation?
So I want callback like
navigate('RoadObject', () => { at the end of animationto do something... });
My tab navigator:
const MainNavigator = TabNavigator({
Map: {
screen: MapScreen
},
RoadObject: {
screen: RoadObjectScreen
}
},
{
animationEnabled: true
});
Have you tried Transitioner | React Navigation?
Seems like this might be relevant for your case...
Transitioner is a React component that helps manage transitions for complex animated components. It manages the timing of animations and keeps track of various screens as they enter and leave, but it doesn't know what anything looks like, because rendering is entirely deferred to the developer.
Under the covers, Transitioner is used to implement CardStack, and hence the StackNavigator.
The most useful thing Transitioner does is to take in a prop of the current navigation state. When routes are removed from that navigation state, Transitioner will coordinate the transition away from those routes, keeping them on screen even though they are gone from the navigation state.
Example
class MyNavView extends Component {
...
render() {
return (
<Transitioner
configureTransition={this._configureTransition}
navigation={this.props.navigation}
render={this._render}
onTransitionStart={this.onTransitionStart}
onTransitionEnd={this.onTransitionEnd}
/>
);
}
I had some problem with transictions too. I was entering on a Screen with Camera and it got too slow. So I decided to keep the Camera off before finishing the transition animation and after that I turned it on.
Example:
import React, {Component, createRef, RefObject} from 'react';
import {SafeAreaView, StyleSheet, Text, TouchableOpacity, View} from 'react-native';
import {Camera} from 'expo-camera';
import {CameraType} from 'expo-camera/build/Camera.types';
import {FontAwesome} from '#expo/vector-icons';
import {StackNavigationProp} from "#react-navigation/stack/lib/typescript/src/types";
export interface State {
type: CameraType,
hasPermission: boolean | null,
takenPictures: Array<string>,
isNavigating: boolean,
}
export interface Props {
navigation: StackNavigationProp<any>,
}
export class CameraMan extends Component<Props, State> {
private readonly camera: RefObject<Camera>;
constructor(props: Props) {
super(props);
this.camera = createRef();
this.state = {
type: Camera.Constants.Type.back,
hasPermission: null,
takenPictures: [],
isNavigating: true
}
}
async componentDidMount() {
this.props.navigation.addListener('transitionEnd', e => {
this.setState({
isNavigating: false
});
});
let permissionResponse = await Camera.requestPermissionsAsync();
this.setState({
hasPermission: permissionResponse.granted
});
}
render() {
if (this.state.hasPermission == null) {
return <SafeAreaView style={styles.container}>
</SafeAreaView>;
}
if (!this.state.hasPermission) {
return <SafeAreaView style={styles.container}>
<Text>Acesso negado!</Text>
</SafeAreaView>;
}
return (
<SafeAreaView style={styles.container}>
{
(this.state.isNavigating) ?
<View style={{flex: 1, backgroundColor: '#000'}} />
:
<Camera style={{flex: 1}}
type={this.state.type}
ref={this.camera}>
<View style={{flex: 1, backgroundColor: 'transparent', flexDirection: 'row'}}>
<TouchableOpacity style={{
position: 'absolute',
bottom: 20,
left: 20,
}}
onPress={() => this.switchCameraType()}>
<Text style={{fontSize: 20, marginBottom: 13, color: '#FFF'}}>Trocar</Text>
</TouchableOpacity>
</View>
</Camera>
}
<TouchableOpacity style={{
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#121212',
margin: 20,
borderRadius: 10,
height: 50,
}} onPress={() => this.takePicture()}>
<FontAwesome name="camera" size={23} color="#FFF" />
</TouchableOpacity>
</SafeAreaView>
);
}
private async takePicture() {
const capturedPicture = await this.camera.current?.takePictureAsync();
console.log(capturedPicture?.uri);
}
private switchCameraType() {
const selectedCameraType = this.state.type == Camera.Constants.Type.back ? Camera.Constants.Type.front : Camera.Constants.Type.back;
this.setState({type: selectedCameraType});
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
}
});
Based on this: https://reactnavigation.org/docs/navigation-events/