How to open a webview on button press in react-native? - react-native

I want to open a webview in a react-native app on button click.
I am new to react native and getting error which are difficult to understand.
const url = 'https://google.com/';
export default class MyWebComponent extends Component {
renderIcon(){
console.log("came here")
return(
return <WebView source={{ uri: url }} />;
)
}
render() {
return (
<View style={styles.container}>
<Button
onPress={{} => () this._renderIcon()}
title= {this.state.Title}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#EF5350'
},
instructions: {
textAlign: 'center',
color: '#ffffff',
marginBottom: 5
}
});

You could add a state that opens the webview like this.
export default class MyWebComponent extends Component {
state = { shouldOpenWebview: false }
render() {
if (this.state.shouldOpenWebview) return <WebView source={{ uri: url }} />
return (
<View style={styles.container}>
<Button
onPress={() => this.setState({ shouldOpenWebview: true})}
title= {this.state.Title}
/>
</View>
);
}
}

Use a flag to management if the Webview is showed or not.

Related

how to use the state hook for dynamically created components in react native

I am trying to do is use useState for dynamically created components in React. In the code below I am, rendering the new components, but I cannot find a way to keep track of their state like I did for the hardcoded components. The new components are rendered when the user creates a new "post", causing new "posts" to be generated.
return (
{
results.map( (eachPost) =>
{ const [status, setStatus] = React.useState(true); },
<TouchableOpacity>
<Card>
<Text>
{JSON.parse(eachPost).title}
</Text>
<Collapsible collapsed = {status}>
{JSON.parse(eachPost).content}
</Collapsible>
</Card>
</TouchableOpacity>
)
}
)
UPDATE: I need to know how I can re-render the page
import * as React from 'react';
import { StyleSheet, Button, TextInput, Image, TouchableOpacity } from 'react-native';
import EditScreenInfo from '../components/EditScreenInfo';
import { Text, View } from '../components/Themed';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { Card } from 'react-native-elements';
import Collapsible from 'react-native-collapsible';
import Accordion from 'react-native-collapsible/Accordion';
import { CardStyleInterpolators } from '#react-navigation/stack';
import AsyncStorage from '#react-native-community/async-storage';
import { useState, useCallback } from 'react';
import Post from './Post'
import RenderToLayer from 'material-ui/internal/RenderToLayer';
import { useForceUpdate } from './useForceUpdate'
async function createNewPost (titleAndContent : string){
const post = {titleAndContent_: titleAndContent}
try {
await AsyncStorage.setItem('post' + Math.floor(100000 + Math.random() * 900000), JSON.stringify(post))
} catch (e) {
console.log(e)
}
//console.log(newPosts)
}
export default function NewsScreen({ navigation }: { navigation: any }) {
const [status, setStatus] = React.useState(true);
const [status2, setStatus2] = React.useState(true);
const [status3, setStatus3] = React.useState(true);
const [renderedData, setRenderedData] = React.useState(new Array<string>());
const [input, setInput] = React.useState('Please enter the details of the post in the format of title,content');
const [, forceUpdate] = useState();
const getData = async () => {
try {
const keys = await AsyncStorage.getAllKeys();
const result = await AsyncStorage.multiGet(keys);
console.log(result)
return result;
} catch (e) {
console.log(e);
}
};
async function parseData() {
const payments = await getData();
if (payments) {
const res = payments
//.filter((e: any[]) => e[1] && e.includes('{'))
//.map((e: any[]) => e[1]);
setRenderedData(res);
console.log(res)
}
return renderedData;
}
const results: string[] = []
renderedData.forEach( (eachPost) => {
if ((/\{"title.*}/g.test(eachPost[1]))) results.push(eachPost[1])
})
console.log(results)
React.useEffect(() => {
if (renderedData.length === 0) {
parseData();
}
forceUpdate
},[]);
return (
<View style={styles.container}>
<Text style={{ fontSize: 25}}>News</Text>
<View style = {{flexDirection: "row"}}>
<TextInput
style={{ height: 40, width: 700, borderColor: 'gray', borderWidth: 1 }}
value = {input}
onChangeText={text=>setInput(text)}
/>
<Button
onPress={()=> createNewPost(input)}
title="Add post"
color="blue"
accessibilityLabel="A button to allow the admin to create a new post"
/>
<Button
onPress = {useForceUpdate()}
title="submit"
/>
</View>
<View>
{
results.map( (eachPost) =>
<Post titleAndContent={JSON.parse(eachPost)}/>
)
}
<TouchableOpacity onPress={() => setStatus(!status)}>
<Card>
<Text>
Moore Park Lift Outage
</Text>
<Image
source={{
uri: 'https://i.imgur.com/Etr5xBn.png'
}}
style={{width: 20, height: 20, marginLeft: 10}}
>
</Image>
<Collapsible collapsed = {status}>
<Text style={{fontWeight: 'bold'}}>
UPDATED 6 Aug 09:30 {'\n'}
Details: Ongoing {'\n'}
Lift 2 at Moore Park Light Rail is temporarily out of service. For assistance, ask staff.. {'\n'}
</Text>
<Image
source={{
uri: 'https://svg-clipart.com/svg/blue/PGaAIh0-blue-wheelchair-vector.svg',
}}
style={{width: 70, height: 90, marginTop: 30}}
>
</Image>
</Collapsible>
</Card>
</TouchableOpacity>
<TouchableOpacity onPress={() => setStatus2(!status2)}>
<Card>
<Text>Dulwich Hill Line running slow</Text>
<Image
source={{
uri: 'https://i.imgur.com/iNjXQMW.png'
}}
style={{width: 20, height: 20, marginLeft: 10}}
>
</Image>
<Collapsible collapsed = {status2}>
<Text>
The Dulwich Hill Line is running slow due to track-work being conducted between Lilyfield and Rozelle Bay
</Text>
</Collapsible>
</Card>
</TouchableOpacity>
<TouchableOpacity onPress={() => setStatus3(!status3)}>
<Card >
<Text>Extra services running</Text>
<Image
source={{
uri: 'https://imgur.com/Etr5xBn'
}}
style={{width: 20, height: 20, marginLeft: 10}}
>
</Image>
<Image
source={{
uri: 'https://i.imgur.com/Etr5xBn.png'
}}
style={{width: 20, height: 20, marginLeft: 10}}
>
</Image>
<Collapsible collapsed = {status3}>
<Text>
Extra services will be running this week to help students get back to school
</Text>
</Collapsible>
</Card>
</TouchableOpacity>
</View>
<View style={styles.button}>
<Button
onPress={() => navigation.navigate('HomeScreen')}
title="Go back"
color="blue"
accessibilityLabel="A button to click so that you can go back to the home page"
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'flex-start',
justifyContent: 'flex-start',
flexDirection: 'column',
},
title: {
fontSize: 20,
fontWeight: 'bold',
alignItems: 'center',
justifyContent: 'center',
},
separator: {
marginVertical: 30,
height: 1,
width: '80%',
},
button: {
margin: 10,
flexDirection: 'row',
},
news:{
flexDirection:'column',
backgroundColor: 'white',
},
eachnews:{
margin: 500,
fontWeight: 'bold',
alignItems: 'flex-start',
justifyContent: 'flex-start',
},
textnews:{
fontSize: 20,
},
});
I think you need more knowledge related to basics of react.
You need a Post component
Post.js
export default (props) => {
const [status, setStatus] = useState();
return (<TouchableOpacity>
<Card>
<Text>
{props.title}
</Text>
<Collapsible collapsed = {status}>
{props.content}
</Collapsible>
</Card>
</TouchableOpacity>);
};
When u call Post
App.js
import Post from './Post.js'
export default App = ()
{
const result = [];
return (
results.map( (eachPost) =>{
const eachPost = JSON.parse(eachPost);
return (<Post title={eachPost.title} content={eachPost.content})/>)
}
}
)

Modal and FlatList

i work on my first react-native project and its also my firts javascript work. I want at least a news-app with own database informations. The backend is already finish. Now im in struggle with the app- i want an Popup with Modal with informations from my api like news_image, news_content and news_title. The News are in the FlatList and now i want to click on a item to show the content in an modal popup. so, here is my code where im struggle. i get always an error. so how can i fix this problem? tanks a lot!
import React from "react";
import {
AppRegistry,
FlatList,
Image,
Platform,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
ActivityIndicator,
ListView,
YellowBox,
Alert,
TextInput
} from "react-native";
import { WebBrowser } from "expo";
import Button from "react-native-button";
import Modal from "react-native-modalbox";
import Slider from "react-native-slider";
import { MonoText } from "../components/StyledText";
export default class NewsFeed extends React.Component {
static navigationOptions = {
title: "HomeScreen"
};
constructor(props) {
super(props);
this.state = {
isLoading: true
};
YellowBox.ignoreWarnings([
"Warning: componentWillMount is deprecated",
"Warning: componentWillReceiveProps is deprecated"
]);
}
FlatListItemSeparator = () => {
return (
<View
style={{
height: 0.5,
width: "100%",
backgroundColor: "#000"
}}
/>
);
};
webCall = () => {
return fetch("http://XXXXXXXXXXXX.com/connection.php")
.then(response => response.json())
.then(responseJson => {
this.setState(
{
isLoading: false,
dataSource: responseJson
},
function() {
// In this block you can do something with new state.
}
);
})
.catch(error => {
console.error(error);
});
};
onClose() {
console.log("Modal just closed");
}
onOpen() {
console.log("Modal just opened");
}
onClosingState(state) {
console.log("the open/close of the swipeToClose just changed");
}
componentDidMount() {
this.webCall();
}
render() {
if (this.state.isLoading) {
return (
<View
style={{ flex: 1, justifyContent: "center", alignItems: "center" }}
>
<ActivityIndicator size="large" />
</View>
);
}
return (
<View style={styles.MainContainer}>
<FlatList
data={this.state.dataSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={({ item }) => (
<View style={{ flex: 1, flexDirection: "row" }}>
<Image
source={{ uri: item.news_image }}
style={styles.imageView}
/>
<Text
onPress={() => this.refs.modal.open()}
style={styles.textView}
>
{item.news_title}
{"\n"}
<Text style={styles.textCategory}>{item.author}</Text>
</Text>
<Text style={styles.textViewDate}>{item.created_at}</Text>
<Modal
style={[styles.modal]}
position={"bottom"}
ref={"modal"}
swipeArea={20}
>
<ScrollView>
<View style={{ width: "100%", paddingLeft: 10 }}>
{item.news_content}
</View>
</ScrollView>
</Modal>
</View>
)}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
}
const styles = StyleSheet.create({
MainContainer: {
justifyContent: "center",
flex: 1,
margin: 5
},
imageView: {
width: "25%",
height: 100,
margin: 7,
borderRadius: 7
},
textView: {
width: "100%",
height: "100%",
textAlignVertical: "center",
padding: 10,
fontSize: 20,
color: "#000"
},
textViewDate: {
width: "30%",
textAlignVertical: "center",
padding: 15,
color: "#afafaf"
},
textCategory: {
color: "#d3d3d3",
fontSize: 12
},
modal: {
justifyContent: "center",
alignItems: "center",
height: "90%"
}
});
Check the code below and compare it with your code.
I am not sure where your error is located or what your exact error is, but you can check the example code below, which is similar to yours, for comparison.
I am using 'Axios' over fetch() because of the automatic transformation to JSON and some other beneficial stuff.
npm install --save axios
Code:
import React, { Component } from 'react'
import {
ActivityIndicator,
FlatList,
Image,
ScrollView,
Text,
TouchableOpacity,
View
} from 'react-native';
import Axios from 'axios';
import Modal from "react-native-modalbox";
export default class NewsFeed extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: [],
selectedIndex : -1
}
}
componentDidMount = () => {
Axios.get('<URL>')
.then(response => {
const { data } = response;
this.setState({dataSource : data});
}).catch(error => {
const { data } = error;
console.log(data);
});
}
_openModal = index => {
this.setState({ selectedIndex : index });
this.modalReference.open();
}
_renderSeparator = () => {
return <View style={{ flex: 1, borderBottomWidth: 0.5, borderBottomColor: '#000000' }} />
}
_renderItem = ({item, index}) => {
const {news_image, news_title, news_content, author, created_at} = item;
return <TouchableOpacity onPress={() => this._openModal(index)} >
<View style={{ flex: 1, flexDirection: 'row' }}>
<Image style={{ flex: 1, width: null, height: 200 }} source={{ uri: news_image }} />
<Text>{news_title}</Text>
<Text>{author}</Text>
<Text>{created_at}</Text>
</View>
</TouchableOpacity>;
}
render = () => {
const { dataSource, selectedIndex } = this.state;
const { news_content } = dataSource[selectedIndex];
return dataSource.length === 0 ?
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<ActivityIndicator size="large" />
</View> :
<View style={styles.MainContainer}>
<FlatList
data={dataSource}
keyExtractor={(item, index) => index.toString()}
ItemSeparatorComponent={this._renderSeparator}
renderItem={this._renderItem}
/>
<Modal ref={reference => modalReference = reference}>
<ScrollView style={{ flex: 1, padding: 20 }}>
<Text>{news_content}</Text>
</ScrollView>
</Modal>
</View>
}
}
I think the issue is in modal,
Can you re-write the code like below?
return (
<View style={styles.MainContainer}>
<FlatList
data={this.state.dataSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={({ item }) => (
<View style={{ flex: 1, flexDirection: "row" }}>
<Image source={{ uri: item.news_image }} style={styles.imageView} />
<Text onPress={() => { this.setState({ item: item.news_content }, () => this.refs.modal.open()); }} style={styles.textView}>
{item.news_title}
<Text style={styles.textCategory}>{item.author}</Text>
</Text>
<Text style={styles.textViewDate}>{item.created_at}</Text>
</View>
)}
keyExtractor={(item, index) => index.toString()}
/>
<Modal
style={[styles.modal]}
position={"bottom"}
ref={"modal"}
swipeArea={20}
>
<ScrollView>
<View style={{ width: "100%", paddingLeft: 10 }}>
{this.state.item}
</View>
</ScrollView>
</Modal>
</View>
);
Only single modal is enough for popup screen.
And you can try this also,
Change your reference to
ref={ref => this.modalRef = ref}
And use like this,
this.modalRef.open()

How to snap pictures using expo react native camera?

I have just started using React Native with Expo so I am kind of confused. So, I have made a camera component which I imported in the main screen. Everything looks good. But I can't take pictures. I cannot click the snap icon and save the image. Is there a component that I missed?
I have only posted the CameraComponent class below.
Camera.js
class CameraComponent extends Component {
state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back
}
async componentWillMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === 'granted' })
}
render() {
const { hasCameraPermission } = this.state
if (hasCameraPermission === null) {
return <View />
}
else if (hasCameraPermission === false) {
return <Text> No access to camera</Text>
}
else {
return (
<View style={{ flex: 1 }}>
<Camera
style={{ flex: 1, justifyContent: 'space-between' }}
type={this.state.type}
>
<Header
searchBar
rounded
style={{
position: 'absolute',
backgroundColor: 'transparent',
left: 0,
top: 0,
right: 0,
zIndex: 100,
alignItems: 'center'
}}
>
<View style={{ flexDirection: 'row', flex: 4 }}>
<Ionicons name="md-camera" style={{ color: 'white' }} />
<Item style={{ backgroundColor: 'transparent' }}>
<Icon name="ios-search" style={{ color: 'white', fontSize: 24, fontWeight: 'bold' }}></Icon>
</Item>
</View>
<View style={{ flexDirection: 'row', flex: 2, justifyContent: 'space-around' }}>
<Icon name="ios-flash" style={{ color: 'white', fontWeight: 'bold' }} />
<Icon
onPress={() => {
this.setState({
type: this.state.type === Camera.Constants.Type.back ?
Camera.Constants.Type.front :
Camera.Constants.Type.back
})
}}
name="ios-reverse-camera"
style={{ color: 'white', fontWeight: 'bold' }}
/>
</View>
</Header>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', paddingHorizontal: 30, marginBottom: 15, alignItems: 'flex-end' }}>
<Ionicons name="ios-map" style={{ color: 'white', fontSize: 36 }}></Ionicons>
<View></View>
<View style={{ alignItems: 'center' }}>
<MaterialCommunityIcons name="circle-outline" // This is the icon which should take and save image
style={{ color: 'white', fontSize: 100 }}
></MaterialCommunityIcons>
<Icon name="ios-images" style={{ color: 'white', fontSize: 36 }} />
</View>
</View>
</Camera>
</View>
)
}
}
}
export default CameraComponent;
The icon in the center i;e circle icon should automatically take and save image.
You can use "onPictureSaved" when the asynchronous takePictureAsync function returns so that you can grab the photo object:
takePicture = () => {
if (this.camera) {
this.camera.takePictureAsync({ onPictureSaved: this.onPictureSaved });
}
};
onPictureSaved = photo => {
console.log(photo);
}
In the view you would have a Camera component that has a ref:
<Camera style={styles.camera} type={this.state.type} ref={(ref) => { this.camera = ref }} >
As well as a button that will call the takePicture function on press:
<TouchableOpacity style={styles.captureButton} onPress={this.takePicture} />
So you need to tell your 'circle icon' to take the picture. First I would add a reference to your camera like so
<Camera style={{ flex: 1 }}
ref={ (ref) => {this.camera = ref} }
type={this.state.type}>
then create a function that actually tells your app to take the photo:
async snapPhoto() {
console.log('Button Pressed');
if (this.camera) {
console.log('Taking photo');
const options = { quality: 1, base64: true, fixOrientation: true,
exif: true};
await this.camera.takePictureAsync(options).then(photo => {
photo.exif.Orientation = 1;
console.log(photo);
});
}
}
Now make your icon have an onPress() to take the photo. I did something like this.
<TouchableOpacity style={styles.captureButton} onPress={this.snapPhoto.bind(this)}>
<Image style={{width: 100, height: 100}} source={require('../assets/capture.png')}
/>
</TouchableOpacity>
You may also want to create a view that renders an image preview or something similar. The Expo documentation has a fairly good example on getting started. Note that Expo creates a cached folder called 'Camera' and that's where the image initially is.
You can do this in a functional component as well using a ref created via a React Hook. Here is an example based on the expo SDK 38 Camera component https://docs.expo.io/versions/v38.0.0/sdk/camera/
import React, { useState, useEffect, useRef } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { Camera } from 'expo-camera';
export default function App() {
const [hasPermission, setHasPermission] = useState(null);
const [type, setType] = useState(Camera.Constants.Type.back);
const ref = useRef(null)
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
_takePhoto = async () => {
const photo = await ref.current.takePictureAsync()
console.debug(photo)
}
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View style={{ flex: 1 }}>
<Camera style={{ flex: 1 }} type={type} ref={ref}>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
}}>
<TouchableOpacity
style={{
flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={() => {
setType(
type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
);
}}>
<Text style={{ fontSize: 18, marginBottom: 10, color: 'white' }}> Flip </Text>
</TouchableOpacity>
<TouchableOpacity
onPress={_takePhoto}
>
<Text>Snap Photo</Text>
</TouchableOpacity>
</View>
</Camera>
</View>
);
}
I did not check what the UI for that looks like but the main point is to utilize React.useRef and attach the ref to your <Camera/> component. Then you may call ref.current.takePictureAsync to capture and access a new image. Look below to see the important relevant snippets for capturing a photo.
import React from 'react'
/* ... other imports
*/
export default CameraScene = () => {
/* ... other state and permission logic
*/
const ref = useRef(null)
const _takePhoto = async () => {
const photo = await ref.current.takePictureAsync()
console.debug(photo)
}
return (
<Camera style={{flex: 1}} ref={ref}> /* ...
... other ui logic
*/
</Camera>
)
}
Learn more about useRef here https://reactjs.org/docs/hooks-reference.html#useref)
You'll need to add a ref to the Camera class to be able to call it's takePictureAsync function within your own 'handle' method.
cameraRef = React.createRef();
<Camera ref={this.cameraRef}>...</Camera>
Don't forget ".current" when calling the method of the referenced camera.
handlePhoto = async () => {
if(this.cameraRef){
let photo = await this.cameraRef.current.takePictureAsync();
console.log(photo);
}
}
Then simply call your 'handle' method on a touchable element acting as the photo-snap button.
<TouchableOpacity
style={{width:60, height:60, borderRadius:30, backgroundColor:"#fff"}}
onPress={this.handlePhoto} />
You should be able to see the photo logged in your console.
Are you trying to do this on an actual physical device? You can't shoot pictures with an emulator.

wants to capture image in by using expo camera

i'm trying to capture image from expo camera but when i alert my photo nothing shown up here is complete code of my component
import React from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { Camera, Permissions } from 'expo';
import {
Container,
Title,
Content,
Header,
Button,
Switch,
Left,
Body,
Right,
List,ListItem,Thumbnail,Footer,FooterTab
} from "native-base";
import {Icon} from 'react-native-elements';
export default class CameraEx extends React.Component {
static navigationOptions = {
header: null
}
state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back,
};
async componentDidMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === 'granted' });
}
takePicture = async function() {
if (this.camera) {
let photo = await this.camera.takePictureAsync();
alert(photo);
}
}
render() {
const { hasCameraPermission } = this.state;
if (hasCameraPermission === null) {
return <View />;
} else if (hasCameraPermission === false) {
return <Text>No access to camera</Text>;
} else {
return (
<Container style={{ flex: 1 }}>
<Header transparent>
<Left>
<Button transparent onPress={() =>this.props.navigation.navigate('Home') }>
<Icon name='arrow-back' />
</Button>
</Left>
</Header>
<Camera style={{ flex: 1 }} type={this.state.type}>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
justifyContent:'space-between',
width:"50%"
}}>
<TouchableOpacity
style={{
//flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={() => {
this.setState({
type: this.state.type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back,
});
}}>
<Text
style={{ fontSize: 18, marginBottom: 10, color: 'white' }}>
{' '}Flip{' '}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={{
// flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={()=>this.takePicture()}
>
<Text
style={{ fontSize: 18, marginBottom: 10, color: 'white' }}>
{' '}Take Picture{' '}
</Text>
</TouchableOpacity>
</View>
</Camera>
</Container>
);
}
}
}
here is a complete code my component please let me know how i can capture image and stored into state i'm beginner ............................................................................................................................................. ....
you cannot show a picture in alert box. you need to pass the source or base64 of image to react native Image component.
import {ImagePicker} from 'expo';
const options = {
base64: true,
allowsEditing: true
};
const data = await ImagePicker.launchCameraAsync(options);
if (data.cancelled !== true) {
this.setState({imageBase64: data.base64});
}
and then you can use like this:
<Image source={`data:image/png;base64, ${this.state.imageBase64}`} />

Implement #mention in TextInput

How can I implement #mention in react native's TextInput?
I've tried this react-native-mention but it is not being maintained anymore. There are so many styling issues and callback issues.
What I want is to display custom view inside TextInput. Something like this.
And after tapping on the list I want to display like this:
So far I am able to achieve:
When I type '#' in TextInput user list appear.
And when I tap on user I get username in TextInput
Code:
renderSuggestionsRow() {
return this.props.stackUsers.map((item, index) => {
return (
<TouchableOpacity key={`index-${index}`} onPress={() => this.onSuggestionTap(item.label)}>
<View style={styles.suggestionsRowContainer}>
<View style={styles.userIconBox}>
<Text style={styles.usernameInitials}>{!!item.label && item.label.substring(0, 2).toUpperCase()}</Text>
</View>
<View style={styles.userDetailsBox}>
<Text style={styles.displayNameText}>{item.label}</Text>
<Text style={styles.usernameText}>#{item.label}</Text>
</View>
</View>
</TouchableOpacity>
)
});
}
onSuggestionTap(username) {
this.setState({
comment: this.state.comment.slice(0, this.state.comment.indexOf('#')) + '#'+username,
active: false
});
}
handleChatText(value) {
if(value.includes('#')) {
if(value.match(/#/g).length > 0) {
this.setState({active: true});
}
} else {
this.setState({active: false});
}
this.setState({comment: value});
}
render() {
const {comments} = this.state;
return (
<View style={styles.container}>
{
this.state.active ?
<View style={{ marginLeft: 20}}>
{this.renderSuggestionsRow()}
</View> : null
}
<View style={{ height: 55}}/>
<View style={styles.inputContainer}>
<TextInput
style={styles.inputChat}
onChangeText={(value) => this.handleChatText(value)}
>
{comment}
</TextInput>
<TouchableOpacity style={styles.inputIcon} onPress={() => this.addComment()}>
<Icon type='FontAwesome' name='send-o' style={{fontSize: 16, color: '#FFF'}}/>
</TouchableOpacity>
</View>
</View>
);
}
One simple solution would be to use react-native-parsed-text.
Here is an example:
import * as React from "react";
import { Text, View, StyleSheet } from 'react-native';
import ParsedText from 'react-native-parsed-text';
const userNameRegEx = new RegExp(/#([\w\d.\-_]+)?/g);
export default class Example extends React.Component {
handleNamePress = (name) => {
alert("Pressed username " + name);
}
render() {
return (
<View style={styles.container}>
<ParsedText
style={styles.text}
parse={
[
{pattern: userNameRegEx, style: styles.username, onPress: this.handleNamePress},
]
}
childrenProps={{allowFontScaling: false}}
>
This is a text with #someone mentioned!
</ParsedText>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
text: {
color: 'black',
fontSize: 15,
},
username: {
color: 'white',
fontWeight: 'bold',
backgroundColor: "purple",
paddingHorizontal: 4,
paddingBottom: 2,
borderRadius: 4,
},
});
However, this library doesn't support rendering custom views. The example above is achieved by just pure styling. If you need a custom view you need to implement something yourself. For a long time, it wasn't possible to render arbitrary components embedded inside a text-components. However, this has changed now afaik and we can do stuff like this:
<Text>Hello I am an example <View style={{ height: 25, width: 25, backgroundColor: "blue"}}></View> with an arbitrary view!</Text>
Check both code examples here: https://snack.expo.io/#hannojg/restless-salsa
One important note: You can render the output of the ParsedText or your own custom component inside the TextInput, like this:
<TextInput
...
>
<ParsedText
...
>
{inputValue}
</ParsedText>
</TextInput>