React native scroll to on Modal - react-native

I work actually on a picker component which needs to be at center on the middle value by default.
I have created a modal with a ScrollView with ref and use the scrollTo function view on this exemple: https://snack.expo.io/SkileGr9X
Tried all but got this error :
Cannot read property 'scrollTo' of undefined
Does someone have any tip?
import React from 'react';
import { Modal,TouchableHighlight,View, StyleSheet,ScrollView,TextInput} from 'react-native';
import { Container, Header, Content, Icon,Picker, Form, Text,Left, Body, Right, Title,Button,List, ListItem} from "native-base";
const Mystyles = StyleSheet.create({
container:{
},
btnSelect:{
borderColor:'#a1a1a1',
borderWidth:1,
padding:5,
margin:10,
height:33
},
placeholderSelect:{
color:'#a1a1a1',
},
valueSelect:{
color:'black',
}
});
let scrollYPos = 0;
var itemsArray = [];
for(let i=0; i < 90;i++){
itemsArray.push(i);
}
export default class Selects extends React.Component {
constructor(props){
super(props);
this.state = {
items:itemsArray,
modalVisible: false,
isLoading:false,
selectValue:'',
placeholder:'placeholder',
type:'int'
}
}
setModalVisible(visible) {
this.setState({modalVisible: visible});
scrollYPos = this.state.screenHeight * 1;
this.scroller.scrollTo({x: 0, y: scrollYPos});
}
selectItem = (item) =>{
this.setState({
selectValue:item,
modalVisible:false
});
this.props.returnSelect(item);
}
render(){
const { selectValue,items,placeholder } = this.state;
return (
<View style={Mystyles.container}>
<Modal
animationType="slide"
presentationStyle='formSheet'
visible={this.state.modalVisible}>
<Header>
<Left>
<Button transparent onPress={() => {this.setModalVisible(false);}}>
<Icon name='arrow-back' />
</Button>
</Left>
<Body>
<Title>Header</Title>
</Body>
<Right />
</Header>
<ScrollView ref={(scroller) => {this.scroller = scroller}}>
<List dataArray={items}
renderRow={(item) =>
<ListItem onPress={() => {this.selectItem(item);}} selected={selectValue == item}>
<Left>
<Text>{item}</Text>
</Left>
<Right>
<Icon name="arrow-forward" />
</Right>
</ListItem>
}>
</List>
</ScrollView>
</Modal>
<TouchableHighlight
style={Mystyles.btnSelect}
underlayColor="rgba(0, 0, 0, 0.1)"
onPress={() => {
this.setModalVisible(true);
}}>
<Text style={selectValue ? Mystyles.valueSelect : Mystyles.placeholderSelect}>{selectValue ? selectValue : placeholder}</Text>
</TouchableHighlight>
</View>
);
}
}

<Button transparent onPress={() => {this.setModalVisible(false);}}>
When you press this button, Modal gone so you can scroll when view gone. You have to check:
if(this.state.modalVisible){
scrollYPos = this.state.screenHeight * 1;
this.scroller.scrollTo({x: 0, y: scrollYPos});
}
BTW screenHeight is a const, don't use state.

Related

How to use the modal in the list in react native (a specific Modal for each list item)?

I made a customized list component (in React Native) which shows touchable images with some description texts.
I need each images open a specific Modal; but I don't know how!! where & how I should code the Modal??
... here is my photo list component:
export class CustomGallery extends Component {
render() {
let {list} = this.props;
return (
<View style={styles.container}>
<FlatList
numColumns={4}
data={list}
renderItem={({ item}) => (
<View style={styles.views}>
<TouchableOpacity style={styles.touch} >
<ImageBackground
style={styles.img}
source={{ uri: item.photo }}
>
<Text style={styles.txt}>{item.name}</Text>
<Text style={styles.txt}>{item.key}</Text>
<Text style={styles.txt}>{item.describtion}</Text>
</ImageBackground>
</TouchableOpacity>
</View>
)}
/>
</View>
);
}
}
For Modal you can use modal from material-ui - https://material-ui.com/components/modal/
The Modal component renders its children node infront of a backdrop component. Simple and basic example would be like a confirmation message that pops up asking whether you surely want to delete particular information or not.
From your code I am guessing you want to display information regarding the image using modal when you click on the image.
Here I have added Modal component:
import React from 'react';
import Modal from '#material-ui/core/Modal';
export class CustomGallery extends Component {
constructor() {
super();
this.state = {
modalOpen: false,
snackOpen: false,
modalDeleteOpen: false,
};
}
handleModalOpen = () => {
this.setState({ modalOpen: true });
}
handleModalClose = () => {
this.setState({ modalOpen: false });
}
render() {
let {list} = this.props;
return (
<View style={styles.container}>
<FlatList
numColumns={4}
data={list}
renderItem={({ item}) => (
<View style={styles.views}>
<TouchableOpacity style={styles.touch} >
<ImageBackground
style={styles.img}
onClick={() => this.handleModalOpen()}
>
{ item.photo }
</ImageBackground>
<Modal
open={this.state.modalOpen}
onClose={this.handleModalClose}
closeAfterTransition
>
<Text style={styles.txt}>{item.name}</Text>
<Text style={styles.txt}>{item.key}</Text>
<Text style={styles.txt}>{item.describtion}</Text>
</Modal>
</TouchableOpacity>
</View>
)}
/>
</View>
);
}
}
I am not sure about how you set the image. But anyways below method is an example of opening modal with dynamic data.
import React, {useState} from "react";
import { Button, TouchableOpacity, FlatList, Modal, Text } from "react-native";
function App() {
const [value, setValue] = useState("");
const DATA = [
{
id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
title: 'First Item',
},
{
id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
title: 'Second Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'Third Item',
},
];
return (
<>
<FlatList
data={DATA}
renderItem={({item}) => (
<TouchableOpacity onPress={() => setValue(item.title)}>
<Text>{item.title}</Text>
</TouchableOpacity>
)}
/>
<Modal visible={value}>
<Text>{value}</Text>
<Button title="close" onPress={() => setValue("")} />
</Modal>
</>
)
}
export default App;

How to stick the comment input box on top of keyboard [duplicate]

So I need to align a button Which is not at bottom om screen by design should be at middle of screen but it should align to be on top of the keyboard for all devices.
If you check this screenshot :
for Some devices I mange to do it, but in some others is not really aligned :
how can I manage this to work in all?
this is what I did so far :
<Padding paddingVertical={isKeyboardOpen ? Spacing.unit : Spacing.small}>
<Button
variant="solid"
label='Next'
style={styles.submitBtn}
/>
</Padding>
And isKeyboardOpen is just a method which will create a listner based on the platform return true if keyboard is open :
Keyboard.addListener(
Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow',
true
);
And submitBrn css class is :
submitBtn: {
margin: Spacing.base,
},
First import this packages
import {
Button,
ScrollView,
KeyboardAvoidingView,
TextInput,
} from 'react-native';
Render method
<KeyboardAvoidingView
{...(Platform.OS === 'ios' ? { behavior: 'padding' } : {})}
style={styles.container}>
<ScrollView style={styles.scrollView}>
<TextInput style={styles.input} placeholder="Tap here" />
</ScrollView>
<Button title="Next" />
</KeyboardAvoidingView>
This are the styles
const styles = StyleSheet.create({
container: {
flex: 1,
},
scrollView: {
paddingHorizontal: 20,
},
input: {
marginBottom: 20,
borderBottomWidth: 2,
borderColor: '#dbdbdb',
padding: 10,
},
});
Make sure the button is outside the scrollview.
NOTE: You may need to adjust the offset prop of KeyboardAvoidingView if the keyboard has got autocomplete enabled.
Stick button at the bottom of the screen demo
In case anyone is still looking for a solution to this I am posting a working example from October 2021 along with the react native documentation. This example is for a text input that when focused has a button labeled 'Scanner' above the keyboard. React native documentation refers to what we are creating here as a 'toolbar'.
Please see this for further details https://reactnative.dev/docs/next/inputaccessoryview
import {
Button,
ScrollView,
TextInput,
StyleSheet,
InputAccessoryView,
Text,
View,
} from "react-native";
import { MaterialCommunityIcons } from "#expo/vector-icons";
import { BarCodeScanner } from "expo-barcode-scanner";
import { useFocusEffect } from "#react-navigation/native";
function CreateSearchBar() {
const inputAccessoryViewID = "uniqueID";
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
const [showScanner, setShowScanner] = useState(false);
useEffect(() => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === "granted");
})();
}, []);
useFocusEffect(
React.useCallback(() => {
// Do something when the screen is focused
return () => {
// Do something when the screen is unfocused
// Useful for cleanup functions
setShowScanner(false);
};
}, [])
);
onChangeSearch = (search) => {};
setScannerShow = (show) => {
setShowScanner(show);
};
const handleBarCodeScanned = ({ type, data }) => {
setScanned(true);
setShowScanner(false);
alert(`Bar code with type ${type} and data ${data} has been scanned!`);
};
if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
if (showScanner) {
return (
<View style={styles.scanner}>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={StyleSheet.absoluteFillObject}
/>
{scanned && (
<Button
title={"Tap to Scan Again"}
onPress={() => setScanned(false)}
/>
)}
</View>
);
}
return (
<>
<ScrollView keyboardDismissMode="interactive">
<View style={styles.input}>
<MaterialCommunityIcons name="magnify" size={24} color="black" />
<TextInput
onChangeText={onChangeSearch}
inputAccessoryViewID={inputAccessoryViewID}
placeholder="Find items or offers"
/>
<MaterialCommunityIcons
name="barcode"
size={30}
color="black"
onPress={() => setScannerShow(true)}
/>
</View>
</ScrollView>
<InputAccessoryView nativeID={inputAccessoryViewID}>
<Button onPress={() => setScannerShow(true)} title="Scanner" />
</InputAccessoryView>
</>
);
}
const styles = StyleSheet.create({
input: {
height: 40,
margin: 12,
borderWidth: 1,
padding: 10,
flexDirection: "row",
justifyContent: "space-between",
},
scanner: {
height: "50%",
},
});
export default CreateSearchBar;
YOu can use react native modal
<KeyboardAvoidingView
keyboardVerticalOffset={Platform.OS == "ios" ? 10 : 0}
behavior={Platform.OS == "ios" ? "padding" : "height"} style={{ flex: 1 }} >
<Modal>
<ScrollView>
<Content><-------Your content------></Content>
</ScrollView>
<BottomButton />
</Modal>
</KeyboardAvoidingView>

Why Header component in NativeBase is not on top?

i got a little problem with Header component in NativeBase (UI Component for React Native). Its position should be on top. But, mine is below a white block. I don't know why its there.
Here my code
import React, { Component } from 'react';
import { Image } from 'react-native';
import {
Container,
Header,
Left,
Body,
Right,
Title,
Content,
Footer,
FooterTab,
Button,
Icon,
Text,
List,
ListItem,
Switch,
Item,
Input,
Form,
DeckSwiper,
Card,
CardItem,
Thumbnail,
View
} from 'native-base';
import { Col, Row, Grid } from "react-native-easy-grid";
import { StackNavigator } from 'react-navigation';
import Expo from "expo";
import { cards, groups_category } from '../data/dummies';
class HomeScreen extends Component {
constructor (props) {
super(props);
this.state = {
loading: true,
isUserLogin: false,
searchStatus: false
}
this.showSearch = this.showSearch.bind(this);
this.checkLoginStatus = this.checkLoginStatus.bind(this);
}
async componentWillMount() {
await Expo.Font.loadAsync({
'Roboto': require('native-base/Fonts/Roboto.ttf'),
'Roboto_medium': require('native-base/Fonts/Roboto_medium.ttf'),
'Ionicons': require("#expo/vector-icons/fonts/Ionicons.ttf")
});
this.setState({ loading: false });
}
showSearch () {
this.setState({ searchStatus: !this.state.searchStatus });
}
checkLoginStatus () {
if (!this.state.isUserLogin) {
return this.props.navigation.navigate('Login');
} else {
return
}
}
render() {
if (this.state.loading) {
return <Expo.AppLoading />;
}
return (
<Container>
{ this.state.searchStatus &&
(<Header searchBar rounded>
<Item regular>
<Icon name='md-arrow-back' onPress={this.showSearch} />
<Input placeholder='Contoh: Jakarta Memancing'/>
<Icon name='search' />
</Item>
</Header>)
}
{ !this.state.searchStatus &&
(<Header>
<Left>
<Icon name='add' style={{color: '#FFFFFF'}} />
</Left>
<Body style={{ alignItems: 'center' }}>
<Title>Komunitas</Title>
</Body>
<Right>
<Icon name='search' style={{color: '#FFFFFF'}} onPress={this.showSearch} />
</Right>
</Header>)
}
{/* Content */}
<Content padder={true}>
<View style={{height: 470}}>
<DeckSwiper
dataSource={cards}
renderItem={item =>
<Card style={{ elevation: 3 }}>
<CardItem>
<Left>
<Thumbnail source={item.image} />
<Body>
<Text>{item.text}</Text>
<Text note>NativeBase</Text>
</Body>
</Left>
</CardItem>
<CardItem cardBody>
<Image style={{ height: 300, flex: 1 }} source={item.image} />
</CardItem>
<CardItem>
<Icon name="heart" style={{ color: '#ED4A6A' }} />
<Text>{item.name}</Text>
</CardItem>
</Card>
}
/>
</View>
<List>
<ListItem itemHeader first>
<Text>Kategori Grup</Text>
</ListItem>
{groups_category.map(group => {
return (<ListItem key={group.id}>
<Left>
<Icon name={group.icon}/>
</Left>
<Body>
<Text>{group.name}</Text>
</Body>
<Right />
</ListItem>)
}
)}
</List>
</Content>
{/* Content */}
<Footer>
<FooterTab>
<Button vertical active>
<Icon active name="home" />
<Text style={{fontSize: 9.5}}>Home</Text>
</Button>
<Button vertical>
<Icon name="megaphone" />
<Text style={{fontSize: 9.5}}>Baru</Text>
</Button>
<Button vertical>
<Icon name="notifications" />
<Text style={{fontSize: 9.5}}>Notifikasi</Text>
</Button>
<Button onPress={this.checkLoginStatus} vertical>
<Icon name="person" />
<Text style={{fontSize: 9.5}}>Profil</Text>
</Button>
</FooterTab>
</Footer>
</Container>
);
}
}
export default HomeScreen;
It results like this
As you can see the Header which has search icon is located below an empty space / white color. Why this happen ? Maybe there are a lot of friends here had experienced it before when using NativeBase UI Component.
It's because you are using StackNavigator as well. You can disable the header as below.
static navigationOptions = {
headerMode: 'none'
}
UPDATE for React Navigation 5.x
If you want to use Nativebase's header with react navigation 5, you can do it like this:
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import {
Header,
Left,
Body,
Title,
Button,
Icon,
View,
Text
} from 'native-base';
const Stack = createStackNavigator();
const Home = () => {
return (
<View>
<Text>Hello World</Text>
</View>
)
}
const CustomHeader = ({scene, previous, navigation}) => {
const {options} = scene.descriptor;
const title =
options.headerTitle !== undefined
? options.headerTitle
: options.title !== undefined
? options.title
: scene.route.name;
return (
<Header>
<Left>
{previous ? (
<Button transparent onPress={navigation.goBack}>
<Icon name="arrow-back" />
</Button>
) : (
undefined
)}
</Left>
<Body>
<Title>{title}</Title>
</Body>
</Header>
);
};
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"
component={Home}
options={{
header: (props) => <CustomHeader {...props} />,
}}
/>
</Stack.Navigator>
</NavigationContainer>
)
}
More of the options here https://reactnavigation.org/docs/en/stack-navigator.html
Set header null in navigation option to remove top blank space, like below
export default class Home extends React.Component {
static navigationOptions = {
title: 'Home',
header: null //used for removing blank space from top
};
}

how can i toggle multiple <View> data react native

I want to toggle multiple data. i tried it but not succeeded .
render() {
return(
<ScrollView style={styles.drawer}>
<View style={styles.content} key={1}>
<ListView
dataSource={this.state.dataSource}
renderRow={(data) =>
<View>
<Text style={styles.navMenuTop} onPress={this.handlePressTopCat.bind(this)}>
{'› '+data.Name}
</Text>
{data.SubItems.map((b,Index) =>
<View style={{height:this.state.SubHeight}}>
<Text style={styles.navMenu} onPress={this.handlePressCatid.bind(this,b.cPath)}> {'» '+b.Name}</Text>
</View>
)}
</View>
}
/>
</View>
</ScrollView>
);
}
}
how can i toggle multiple div. SubItems can be toggle when clicked on navMenuTop. it should be working like menu.
import React, { Component } from 'react';
import { Container, Header, Content, Card, CardItem, Body, Text,Button } from 'native-base';
import _isEqual from 'lodash/isEqual';
import _map from 'lodash/map';
export default class CardExample extends Component {
constructor(props) {
super();
this.state = {
previousState: '',
cardData: [1,2,3,4,5],
};
}
componentWillUpdate(nextProps, nextState) {
if (!_isEqual(this.state.previousState, nextState.previousState)) {
this.setState({ [this.state.previousState]: false});
}
}
open(key){
this.setState({ [key]: !this.state[key],previousState:key});
}
render() {
return (
<Container>
<Header />
<Content>
{
_map(this.state.cardData,(data,key)=>{
return (
<Card>
<CardItem header>
<Text>Card {key} header</Text>
</CardItem>
{
this.state[`open_${key}`] ?
<CardItem>
<Body>
<Text>
This is cardItem {key} Body element
</Text>
</Body>
</CardItem>
: null
}
<CardItem footer>
<Button onPress={()=>this.open(`open_${key}`)}><Text>open {key}</Text></Button>
</CardItem>
</Card>
)
})
}
</Content>
</Container>
);
}
}

React Native - How to re-render Navigator component on icon press

How would I re-render/ change the colour of a Navigator component icon when it is pressed. I have TouchableHighlight on it but I need the state to stay permanently. For example someone presses a 'favourite' heart icon, and it turns from grey to red. I've been trying to figure out a solution for some time now.
This is the section of my code where the change should happen. I need my if statement and Navigator icon to re-render based on the true/false value which changes when the TouchableHighlight onPress triggers.
var NavigationBarRouteMapper = {
RightButton: function(route, navigator, index, navState) {
// * The button displayed in the top right of the navigator
switch(route.id) {
case('businessProfile'):
if (route.isFavourited) {
return (
<View style={[ styles.multipleIcons ]}>
<TouchableOpacity
onPress={() => {
var favouriteBusiness = new FavouriteBusiness();
favouriteBusiness.updateBusinessAsFavourite(route.business.id)
}}
style={[ styles.navBarButton, styles.iconRightPaddingLarge ]}
>
<IconFA
name='heart'
size={ 25 }
color='#de6262'
style={ styles.icon }
/>
</TouchableOpacity>
</View>
);
} else {
return (
<View style={[ styles.multipleIcons ]}>
<TouchableOpacity
onPress={() => {
var favouriteBusiness = new FavouriteBusiness();
favouriteBusiness.updateBusinessAsFavourite(route.business.id)
}}
style={[ styles.navBarButton, styles.iconRightPaddingLarge ]}
>
<IconFA
name='heart'
size={ 25 }
color='#ddd'
style={ styles.icon }
/>
</TouchableOpacity>
</View>
);
}
default:
return null;
}
},
};
<Navigator
ref='navigator'
sceneStyle={ [styles.container, styles.inner] }
initialRoute={{
id: this.props.scene,
title:this.props.title
}}
renderScene={ this.renderScene }
configureScene={(route) => {
if (route.sceneConfig) {
return route.sceneConfig;
}
return Navigator.SceneConfigs.FloatFromRight;
}}
navigationBar={
<Navigator.NavigationBar
routeMapper={ NavigationBarRouteMapper }
style={ styles.navigationBar }
/>
}
/>
if you still did not get the solution perhaps these could help.
you can define some Properties in construktor:
constructor(props) {
super(props);
this.state = {
color: 'lightgrey',
};
}
You can define the Icon in the RooteMapper of the Navigaton bar:
return (<Icon
name="star"
style={[styles.rightHadderButton, {color: this.state.color}]}
onPress={RightNavigatorButton.favoriteStar.bind(this)}/>);
and then change the collor in the onpress defined Function:
exports.favoriteStar = function(event) {
if (this.state.color === 'lightgrey') {
this.setState({ color: '#990000' });
AlertIOS.alert( "favoriteStar","Favorite");
} else {
this.setState({ color: 'lightgrey' });
AlertIOS.alert( "favoriteStar","notFavorite");
}
}
Hope these works for you too and it helps you.