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

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>

Related

React Native: Camera from "expo-camera" stop running when face is not ever detected

I am newer for using react-native, and wanna try to create a camera with filter. I'm blocked in step to recognize face. Have success to draw rectangle when face detected, but the problem is once it goes out of detection. The camera stop running as it fixes on the last real-time capture
Here is my code:
import { useState, useEffect, useRef } from 'react'
import { Camera } from 'expo-camera'
import * as MediaLibrary from 'expo-media-library'
import { Text, StyleSheet, View, TouchableOpacity } from 'react-native'
import Button from './Button'
import { Ionicons } from '#expo/vector-icons'
import * as FaceDetector from 'expo-face-detector'
export default function PCamera() {
const cameraRef = useRef(undefined)
const [faceDetected, setFaceDetected] = useState([])
const [lastImage, setImage] = useState(undefined)
const [hasUsePermssion, setUsePermission] = useState(false)
const [type, switchToType] = useState(Camera.Constants.Type.front)
const takePicture = async () => {
if (cameraRef) {
try {
const options = {
quality: 1,
base64: true,
exif: false,
}
const data = await cameraRef.current.takePictureAsync(options)
setImage(data.uri)
console.log(data)
} catch (err) {
console.error(err)
}
}
}
const swithMode = () => {
switchToType(
type === Camera.Constants.Type.front
? Camera.Constants.Type.back
: Camera.Constants.Type.front
)
}
const handleFacesDetected = ({ faces }) => {
setFaceDetected(faces)
}
useEffect(() => {
;(async () => {
const { status } = await Camera.requestCameraPermissionsAsync()
if (status === 'granted') {
setUsePermission(true)
}
})()
}, [])
if (hasUsePermssion === null) {
return <View />
}
if (hasUsePermssion === false) {
return <Text>No access to camera</Text>
}
return (
<View style={styles.cameraContainer}>
<View style={styles.overlay}>
<Camera
ref={cameraRef}
style={styles.camera}
type={type}
onFacesDetected={handleFacesDetected}
faceDetectorSettings={{
mode: FaceDetector.FaceDetectorMode.fast,
detectLandmarks: FaceDetector.FaceDetectorLandmarks.all,
runClassifications:
FaceDetector.FaceDetectorClassifications.none,
minDetectionInterval: 100,
tracking: true,
}}
>
{faceDetected.length > 0 &&
faceDetected.map((face) => (
<View
key={face.faceID}
style={{
position: 'absolute',
borderWidth: 2,
borderColor: 'red',
left: face.bounds.origin.x,
top: face.bounds.origin.y,
width: face.bounds.size.width,
height: face.bounds.size.height,
}}
/>
))}
</Camera>
</View>
<View style={styles.optionsContainer}>
<View>
<TouchableOpacity onPress={swithMode}>
<Text>
<Ionicons
name="camera-reverse-outline"
size={24}
color="black"
/>
</Text>
</TouchableOpacity>
</View>
<Button
icon="camera"
title="Take Photo"
onPress={takePicture}
style={styles.button}
/>
<View>
<Text>...</Text>
</View>
</View>
</View>
)}
const styles = StyleSheet.create({
cameraContainer: {flex: 1,
},
overlay: {
flex: 6,
borderBottomStartRadius: 75,
borderBottomEndRadius: 75,
overflow: 'hidden',
},
camera: {
flex: 1,
},
optionsContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
},
})
N.B: Don't take care of the Button, it's a custom component and works well

React Native: Is it possible to TextInput keep on focus when the Modal is opened?

I need to keep input text in TextInput while the Modal is opened. FYI, TextInput is not a child component of Modal
I know it doesn't normal but the situation is pushing me to do this.
Please help me if you have experience in solving this kind of problem.
Use can use Ref object to focus the text input every time modal will be visible
check this code,,,,
export default function App() {
const [visible, setVisible] = React.useState(false);
const input = React.createRef();
React.useEffect(() => {
if (visible) {
input.current.focus();
}
}, [visible]);
return (
<View style={styles.container}>
<Button
title="Click"
onPress={() => {
setVisible(true);
}}
/>
<Modal
visible={visible}
onRequestClose={() => {
setVisible(false);
}}>
<View style={styles.modal}>
<TextInput ref={input} />
</View>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
modal: {
width: '100%',
height: '100%',
},
});
Use this way
CustomModal.js
const CustomModal = React.forwardRef((props, ref) => (
React.useEffect(() => {
ref.current.focus();
}, []);
return (
<Modal isActive={props.isActive}>
<ModalClose onClick={props.handleClose} />
</Modal>
)
))
App.js
export default function App() {
const [visible, setVisible] = React.useState(false);
const input = React.createRef();
return (
<View >
<TextInput ref={input} /> // Create textinput outside here
<Button
title="Click"
onPress={() => {
setVisible(true);
}}
/>
<CustomModal
isActive={visible}
handleClose={()=>{}}
ref={input}
/>
</View>
);
}

create a customized scrollable top Tab bar using react-navigation-tab?

i need to create a customized scrollable top tab bar using react navigation tabs and tabBarComponent without using any other third party library.
const TopTabBar = createMaterialTopTabNavigator({
Home: HomePage,
Entertainment: EntertainmentNews,
Business: BusinessStack,
Music: MusicStack,
Politics: PoliticsStack,
Sports: SportsStack,
Technology: TechnologyStack,
WorldNews: WorldNewsStack
}, {
tabBarComponent: (props) => <TopTabBarComponent {...props}/>
})
In this with tab bar component i am able to create the top bar but it does not scroll when the screen are swiped ?
import React , { Component } from 'react'
import { Text, View, StyleSheet, FlatList, TouchableOpacity, Animated, Dimensions} from 'react-native'
interface Props {
navigation?: any
}
interface State {
//
}
export class TopTabBarComponent extends Component <Props, State>{
flatListRef
constructor(props: Props, state: State){
super(props, state)
}
onPressItem = (index) => {
const { navigation } = this.props
navigation.navigate( this.props.navigation.state.routes[index].routeName )
// this.onScrollIndex(index)
}
renderTopBar = (item, index) => {
const routes = this.props.navigation.state.routes
const activeIndex = this.props.navigation.state.index
return (
<TouchableOpacity style = {{
alignItems: 'center' ,
height: 50,
justifyContent: 'center',
borderBottomWidth: activeIndex === index ? 2 : 0,
borderColor: 'green',
paddingHorizontal: 5
}} onPress = {() => this.onPressItem(index)}>
<Text style = {{ fontSize: 20, color: 'blue'}}>{item.routeName}</Text>
</TouchableOpacity>
)
}
render() {
// reactotron.log('this.props', this.props.navigation)
console.warn('this.props.navigation.state.index', this.props.navigation.state.index)
return(
<View style = {{ marginHorizontal: 5}}>
<FlatList
initialScrollIndex = { this.props.navigation.state.index }
ref = {(ref) => { this.flatListRef = ref}}
// style = {{ paddingHorizontal: 20}}
horizontal
showsHorizontalScrollIndicator = {false}
data = {this.props.navigation.state.routes}
renderItem = {({item , index}) => this.renderTopBar(item, index) }
ItemSeparatorComponent = {() => <View style = {{ paddingRight: 40}}/>}
/>
</View>
)
}
}
This is top tab bar component code ? So how can i make the top tab scroll automatically when the screens are swiped ?
This is a sample to my tab bar that I quickly edited for your test case.
function MyTabBar({ state, descriptors, navigation, position }) {
const scrollViewRef = useRef(null)
useEffect(() => {
scrollViewRef.current.scrollTo({ x: state.index * 50, y: 0, animated: true })
}, [state.index])
return (
<View style={styles.tabContainer}>
<ScrollView ref={list => scrollViewRef.current = list} contentContainerStyle={{ flexDirection: 'row', alignItems: 'center'}} horizontal>
{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 onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
let focusStyle = { }; //style object to append to the focussed tab
let tintColor = { };
let fontColor = { };
isFocused ? focusStyle = { backgroundColor: 'darkgray' } : focusStyle = { };
isFocused ? tintColor = { tintColor: 'black' } : tintColor = { tintColor: 'lightgray' }; // TODO: change this with proper fontColor codes
isFocused ? fontColor = { color: 'black' } : fontColor = { color: 'lightgray' };
//below controls the rendered tab
return (
<TouchableWithoutFeedback
key={index}
onPress={onPress}
onLongPress={onLongPress}
style={[styles.tabItem, focusStyle]}
>
<View style={styles.tabIconContainer}>
<LearnTabIcon title={route.name} color={tintColor} />
</View>
<Text style={[styles.labelStyle, fontColor]}>
{label}
</Text>
</TouchableWithoutFeedback>
);
})}
</ScrollView>
<View style={styles.tabBarIndicator}>
{ state.routes.map((route, index) => {
const isFocused = state.index === index;
let selectedStyle = { };
isFocused ? selectedStyle = { backgroundColor: 'darkgray'} : { }; // TODO: change this coloring to actual color codes
return (
<View
style={[styles.tabIndicators, selectedStyle]}
key={index}
>
</View>
);
})}
</View>
</View>
);
}
I'm using functional components for everything so it will look a bit different but in your custom tab bar grab the state.index on each index change and then use scrollToOffset to control where the scroll view is centered on screen. I put in a sample 50px offset scale just for simplicity.
I also wasn't using TypeScript but you seem to have most the boiler plate already ready to go anyway.
import React from 'react'
import {StatusBar} from 'react-native'
import {NavigationContainer} from '#react-navigation/native'
import {GestureHandlerRootView} from 'react-native-gesture-handler'
import {createMaterialTopTabNavigator} from '#react-navigation/material-top-tabs'
import HomeScreen from './screens/HomeScreen'
import LivingRoom from './screens/LivingRoom'
import Fav from './screens/Fav'
import Settings from './screens/Settings'
const Tab = createMaterialTopTabNavigator()
const AppRoot = () => {
return (
<GestureHandlerRootView className="flex-1">
<StatusBar hidden />
<NavigationContainer>
<Tab.Navigator
screenOptions={{
tabBarScrollEnabled: true,
tabBarIndicator: () => null,
tabBarStyle: {
backgroundColor: '#000',
},
tabBarItemStyle: {
width: 'auto',
alignItems: 'flex-start',
},
tabBarLabelStyle: {
fontSize: 30,
fontFamily: 'Satoshi-Black',
color: '#fff',
textTransform: 'capitalize',
},
}}
sceneContainerStyle={{backgroundColor: '#000'}}>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Living Room" component={LivingRoom} />
<Tab.Screen name="Fav" component={Fav} />
<Tab.Screen name="Settings" component={Settings} />
</Tab.Navigator>
</NavigationContainer>
</GestureHandlerRootView>
)
}
export default AppRoot

Better solution to open the Menu when 3 dots are clicked in React Native

I am able to open menu when 3 dots icon is clicked for each item. But can the code be written in a better way..
Right now menu is getting created for each card item but ideally it would have been good to create single Menu View and dynamically associate it to some card where ever the 3 dots is clicked.
Expo Source Code Link
Code
export default class App extends React.Component {
constructor(props, ctx) {
super(props, ctx);
this.state = {
list: [
{ name: "Michael", mobile: "9292929292", ref: React.createRef() },
{ name: "Mason Laon Roah", mobile: "1232313233", ref: React.createRef() },
{ name: "Constructor", mobile: "4949494949", ref: React.createRef() },
{ name: "Rosling", mobile: "4874124584", ref: React.createRef() }
],
};
}
_menu = null;
hideMenu = () => {
this._menu.hide();
};
showMenu = (ref) => {
this._menu = ref;
this._menu.show();
};
render() {
const renderItem = ({ item, index }) => (
<ListItem
title={
<View>
<Text style={{ fontWeight: "bold" }}>{item.name}</Text>
<Text>{item.mobile}</Text>
</View>
}
subtitle={
<View>
<Text>445 Mount Eden Road, Mount Eden, Auckland. </Text>
<Text>Contact No: 134695584</Text>
</View>
}
leftAvatar={{ title: 'MD' }}
rightContentContainerStyle={{ alignSelf: 'flex-start'}}
rightTitle={this.getMenuView(item.ref)}
/>
);
return (
<View style={styles.container}>
<View style={{ flex: 1, marginTop: 30 }}>
<FlatList
showsVerticalScrollIndicator={false}
keyExtractor={(item, index) => index.toString()}
data={this.state.list || null}
renderItem={renderItem}
ItemSeparatorComponent={() => (
<View style={{ marginBottom: 5 }} />
)}
/>
</View>
</View>
);
}
getMenuView(ref) {
return (
<Menu
ref={ref}
button={<Icon onPress={() => this.showMenu(ref.current)} type="material" color="red" name="more-vert" />}
>
<MenuItem onPress={this.hideMenu}>Menu item 1</MenuItem>
<MenuItem onPress={this.hideMenu}>Menu item 2</MenuItem>
<MenuItem onPress={this.hideMenu} disabled>
Menu item 3
</MenuItem>
<MenuDivider />
<MenuItem onPress={this.hideMenu}>Menu item 4</MenuItem>
</Menu>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
});
Sample Output
As mentioned here, you can find an undocumented UIManager.java class that allows you to create Popups with its showPopupMenu method.
This currently works only for Android.
import React, { Component } from 'react'
import { View, UIManager, findNodeHandle, TouchableOpacity } from 'react-native'
import Icon from 'react-native-vector-icons/MaterialIcons'
const ICON_SIZE = 24
export default class PopupMenu extends Component {
constructor (props) {
super(props)
this.state = {
icon: null
}
}
onError () {
console.log('Popup Error')
}
onPress = () => {
if (this.state.icon) {
UIManager.showPopupMenu(
findNodeHandle(this.state.icon),
this.props.actions,
this.onError,
this.props.onPress
)
}
}
render () {
return (
<View>
<TouchableOpacity onPress={this.onPress}>
<Icon
name='more-vert'
size={ICON_SIZE}
color={'grey'}
ref={this.onRef} />
</TouchableOpacity>
</View>
)
}
onRef = icon => {
if (!this.state.icon) {
this.setState({icon})
}
}
}
Then use it as follows.
render () {
return (
<View>
<PopupMenu actions={['Edit', 'Remove']} onPress={this.onPopupEvent} />
</View>
)
}
onPopupEvent = (eventName, index) => {
if (eventName !== 'itemSelected') return
if (index === 0) this.onEdit()
else this.onRemove()
}
Source: https://cmichel.io/how-to-create-a-more-popup-menu-in-react-native
There is now a React Native plugin for this. I'm not sure it was around when the question was originally asked. But I'm leaving this here for anyone else looking for the answer.
https://www.npmjs.com/package/react-native-popup-menu
The example worked for me. I wanted to use the vertical ellipsis, so I did this modification to the MenuTrigger part of the example to an icon instead of text:
<MenuTrigger>
<Icon name="more-vert" size={25} color={colors.rustRed} />
</MenuTrigger>
As a side note, I had difficulty finding and using the ellipsis. I eventually went with using react-native-vector-icons by using 'npm -i react-native-vector-icons' and importing the Material Icons like this:
import Icon from 'react-native-vector-icons/MaterialIcons';
Use React Portals
https://reactjs.org/docs/portals.html
In short the receipts is:
You define your dynamic menu at sibling level only once in the parent i.e. in your case it would be adjacent to App.
Handle Click at each item level to open your component. You can pass some specific event days to achieve the dynamism.
Easier example https://codeburst.io/reacts-portals-in-3-minutes-9b2efb74e9a9
This achieves exactly what you are trying to do which is defer the creation of component untill clicked.

KeyboardAvoidingView works on EXPO but not on APK?

I bought this Theme which in Expo works flawlessly, but as soon as I build the APK, the Keyboard will cover the whole screen and wont work as supposed.
I'm using expo for testing and it works just fine.
return (
<SafeAreaView style={styles.container}>
<NavHeader title={thread.name} {...{navigation}} />
<FlatList
inverted
data={messages}
keyExtractor={message => `${message.date}`}
renderItem={({ item }) => (
<Msg message={item} name={item.me ? name : thread.name} picture={thread.picture} />
)}
/>
<KeyboardAvoidingView behavior={Platform.OS === "ios" ? "padding" : "height"} enabled>
<View style={styles.footer}>
<TextInput
style={styles.input}
placeholder="Write a message"
value={this.state.message}
onChangeText={message => this.setState({ message })}
autoFocus
blurOnSubmit={false}
returnKeyType="send"
onSubmitEditing={this.send}
underlineColorAndroid="transparent"
/>
<TouchableOpacity primary transparent onPress={this.send}>
<Text style={styles.btnText}>Send</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
</SafeAreaView>
);
And the Styles
const styles = StyleSheet.create({
container: {
flex: 1
},
footer: {
borderColor: Theme.palette.lightGray,
borderTopWidth: 1,
paddingLeft: Theme.spacing.small,
paddingRight: Theme.spacing.small,
flexDirection: "row",
alignItems: "center"
},
input: {
height: Theme.typography.regular.lineHeight + (Theme.spacing.base * 2),
flex: 1
},
btnText: {
color: Theme.palette.primary
}
});
I have tried the following plugin
using the enableOnAndroid prop
https://github.com/APSL/react-native-keyboard-aware-scroll-view
with no success.
I have posted here:
https://github.com/APSL/react-native-keyboard-aware-scroll-view/issues/305
and here:
https://github.com/expo/expo/issues/2172
Unfortunately this is a known issue
https://github.com/expo/expo/issues/2172
Depending on the complexity of your screen layout you could add a bottom margin or padding using Keyboard listeners provided by React Native.
import React, { Component } from 'react';
import { Keyboard, TextInput } from 'react-native';
class Example extends Component {
componentDidMount () {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
}
componentWillUnmount () {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
}
_keyboardDidShow () {
this.setState({
marginBottom: 400
})
}
_keyboardDidHide () {
this.setState({
marginBottom: 0
})
}
render() {
return (
<TextInput
style={{marginBottom: this.state.marginBottom}}
onSubmitEditing={Keyboard.dismiss}
/>
);
}
}