I have a react native App which downloads images from external sources and saves them into dedicated local cache folder. Later they get displayed in different views. The images are initially not available in the app bundle.
<Image source={{uri: uri}} resizeMode="contain" style={style} />
I use the URI format for the source input parameter of Image component referring a local folder. I pass width and height as style as well, in fact the image size is always known before drawing. It works fine on iOS but not on Android.
As the documentation says, I should use require('') for local images on Android. This works for static images but not for downloaded on run time.
Has anybody idea how could I solve this issue?
Here is the code:
class CachedImage extends React.Component {
state = {
uri: null
}
async _handleCache(source) {
const path = `${RNFetchBlob.fs.dirs.CacheDir}${sha1(source.uri).toString()}.jpg`;
const exists = await RNFetchBlob.fs.exists(path);
if (exists) {
this.setState({uri: path});
} else {
// encoding the file to preserve proper URL transmition
await RNFetchBlob
.config({path})
.fetch("GET", source.uri, {});
this.setState({uri: path});
}
}
componentWillMount() {
this._handleCache(this.props.source).done();
}
render() {
const { style } = this.props;
if (this.state.uri != null) {
return (
<Image source={{uri: this.state.uri}} resizeMode="contain" style={style} />
);
}
return(
<View style={style}/> //todo: display activity while loading ...
);
}
}
Usage:
<CachedImage
style={{width: _width , height: _height}}
source={{uri: 'http://...'}} />
Import this packages
import { StyleSheet, Image, Dimensions } from 'react-native';
Render image like this
<Image source={{uri: uri}} style={styles.image} />
Add this styles at the bottom
const styles = StyleSheet.create({
image: {
width: Dimensions.get('window').width,
height: Dimensions.get('window').width / 2
}
});
This should work if any issue write that down below!
Related
SOLVED! : Check the reply below, many thanks to the people who helped! I also had to change the storage rules like this. It's in storage>Rules:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if true;
}
}
}
I am doing this only for test purposes though. Don't allow everyone to write when you write a real program!
PROBLEM:
This is my cloud storage, how to show this image in my react native app? I tried to do it like this:
<Image style={styles.stretch} source={{
uri: 'gs://mezuniyet2r.appspot.com/images/erkek.jpg',
}}
/>
What am I missing here? It doesn't work like this. Isn't it supposed to work like this? I can't find anything like this at official documentation. Very bad documentation indeed. It doesn't say much. Can someone please help me? And this is the full code:
import React, {Component} from 'react';
import {
Platform,
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
Image,
Button,
StatusBar,
} from 'react-native';
import firebase from '#react-native-firebase/app'
import firestore from '#react-native-firebase/firestore'
import { format } from "date-fns";
import storage from '#react-native-firebase/storage';
class App extends Component {
state = {
tablo: {
adSoyad: "",
yas: "",
dogumTarihi: "",
mezunDurumu: "",
}
}
constructor(props) {
super(props);
this.getUser();
this.subscriber=firestore().collection("tablo").doc
('J6mAav1kjkcjrMupeOqX').onSnapshot(doc => {
this.setState({
tablo: {
adSoyad: doc.data().adSoyad,
yas: doc.data().yas,
dogumTarihi: doc.data().dogumTarihi,
mezunDurumu:doc.data().mezunDurumu,
}
}
)
});
}
getParsedDate(date){
date = String(date).split(' ');
var days = String(date[0]).split('-');
var hours = String(date[1]).split(':');
return [parseInt(days[2]), parseInt(days[1])-1, parseInt(days[0]), parseInt(hours[0]), parseInt(hours[1]), parseInt(hours[2])];
}
getUser= async() => {
const userDocument= await firestore().collection("tablolar").doc
("J6mAav1kjkcjrMupeOqX").get()
console.log(userDocument)
}
render() {
var mezund;
var date = new String(this.state.tablo.dogumTarihi);
var date2=this.getParsedDate(date);
//const reference = storage().ref('erkek.jpg');
//var storageRef = firebase.storage().ref();
//var mountainsRef = storageRef.child('erkek.jpg');
//var mountainImagesRef = storageRef.child('images/erkek.jpg');
//mountainsRef.name === mountainImagesRef.name;
console.log(date2);
//this doesn't work though how do we do this??? it gives undefined.
if(this.state.tablo.mezunDurumu==false){mezund="Mezun Değil"}
else if(this.state.tablo.mezunDurumu=true){mezund="mezun"}
// var dtarih= new String(this.state.tablo.dogumTarihi);
return (
<View style={styles.body}>
<Text style={styles.row}>Ad Soyad: {this.state.tablo.adSoyad}</Text>
<Text style={styles.row}>Yaş: {this.state.tablo.yas}</Text>
<Text style={styles.row}>Doğum Tarihi: {date}</Text>
<Text style={styles.row}>Mezun Durumu: {mezund}</Text>
<Image style={styles.stretch} source={{
uri: 'gs://mezuniyet2r.appspot.com/erkek.jpg',
}}
/>
</View>
);
}
}
const styles=StyleSheet.create({
body:{
padding: 25,
margin:25,
backgroundColor:'orange',
flex: 1
},
stretch:{width:50,height:50,
resizeMode:'stretch',},
row:{
backgroundColor:'#fff',
borderBottomWidth:4,
}
})
export default App
The image path has to be converted into a URL that the <Image/> component can fetch and display. This asynchronous operation is best done once in the component lifecycle and stored in the state.
class App extends Component {
state = {
...,
imageUrl: null,
}
...
async componentDidMount() {
var imageRef = firebase.storage().ref('erkek.jpg');
var imageUrl = await imageRef.getDownloadURL();
this.setState({ imageUrl });
}
render() {
...
<Image style={styles.stretch} source={{ uri: this.state.imageUrl }}/>
...
}
}
You should also make sure that the user has read access to the image. You can set read and write permissions in Firebase Security Rules, see https://firebase.google.com/docs/storage/security/start#sample-rules. If you want the image to be readable by everyone set ".read": true in your rules but be careful not to set write public to prevent unwanted tampering with your project!
I can't seem to find this anywhere or I might be using the wrong keyword to search, but how do I change the barStyle on React Native (light-content/dark-content) based on the rendered background image's color
edit:
I have single screen which renders different kinds of images, what I want to achieve is how to change the barStyle based on what image currently showing on the screen. Is there even any way to do that?
case 1:
light-background image with dark-content barStyle
case 2
dark-background image with light-content barStyle
import statusbar and add in render method and set barStyle according to what you want.
try this
import { StatusBar, View } from "react-native";
render() {
return (<View>
<StatusBar barStyle="light-content" />
.....
</view>);
}
Setting StatusBar as transluent along with backgroundColor as transparent, you can achieve this which is as follows:
import React, { Component } from "react";
import { ImageBackground, StyleSheet, StatusBar } from "react-native";
const image1 = { uri: "https://htmlcolorcodes.com/assets/images/html-color-codes-color-tutorials-hero.jpg" };
const image2 = { uri: "https://reactjs.org/logo-og.png" };
export default class App extends Component {
componentDidMount() {
StatusBar.setTranslucent(true);
StatusBar.setBackgroundColor("transparent");
}
render() {
return (
<ImageBackground source={image1} style={styles.image}>
</ImageBackground>
);
}
}
const styles = StyleSheet.create({
image: {
flex: 1,
resizeMode: "cover"
}
});
If you use image1, your output will be like this picture.
If you use image2, your output will be like this picture.
I need to loop through my products array then pass the data as props but for my image urls I cant do that and console show me the following:
invalid call at line 21 require(this.props.img)
here is my code:
app.js
export default class App extends React.Component {
state = {
products: [
{
id: 1,
details: 'this is a macbook',
image: '../Images/macbook.jpg',
price: '1000$',
},
{
id: 2,
details: 'this is a PS4 pro',
image: '../Images/ps4pro.jpeg',
price: '500$',
},
{
id: 3,
details: 'this is a beats',
image: '../Images/beats.jpeg',
price: '200$',
},
]
}
showProds = () => {
let prods = [];
for (let i = 0; i <= this.state.products.length - 1; i++) {
prods.push(<Product details={this.state.products[i].details} img={this.state.products[i].image} />)
}
console.log(prods);
return prods;
}
render() {
return <ScrollView>
{this.showProds()}
</ScrollView>
}
}
product component
export default class Product extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<View style={styles.card}>
<View style={styles.prod}>
<Text style={styles.prod_details}>{this.props.details}</Text>
<Image style={styles.img} source={require(this.props.img)} />
</View>
<View style={styles.btn}>
<TouchableOpacity>
<Text style={styles.btn_text}>Delete</Text>
</TouchableOpacity>
</View>
</View >
)
}
}
I'm new to react-native. I also tried to use require in my loop but no luck.
any help would be appreciated.
Change your products.image so their will be in this format:
products: {
...
image: require('.../Images.image.jpeg'),
...
}
And then change image source to this:
<Image style={styles.img} source={this.props.img} />
You cant dynamically require images in react-native. It's a major flaw in react native.
You have two options,
1.Either host the images onany server like cloudinary and send the URI
2.You can require all images at start, and then just include those image components like :
const flower = () => (
<Image source={require('../../)} />
)
const rat = () => (
<Image source={require('../../)} />
)
and (this.props.img == 'flower') ? flower():rat()
Hope you get the gyst. Feel free for doubts
I have a requirement to open an App screen using user selections on a website, where when user select an item it should openup the relevant page in the Mobile App.
But the website should be loaded inside a webview within the same APP.
Here I used Firebase dynamic links to manage this navigation.
I can open up the website from chrome browser in the emulator and open up the App navigated to exact page as required.
But when I click the same component from the webview within the app it throws up the following error and App wont open up the page that the firebase dynamic link should open up as in the first case of the chrome browser.
Can't open url: intent://example.page.link/getapp#Intent;package=com.google.android.gms;action=com.google.firebase.dynamiclinks.VIEW_DYNAMIC_LINK;scheme=https;S.browser_fallback_url=https://play.google.com/store/apps/details%3Fid%3Dcom.ubercab;end;
My implementation is as follows,
WebApp
App.js
function App() {
return (
<div>
<Button variant="primary">
Open this in your App
</Button>
<Banner url={"https://testingapp.page.link/getapp"}/>
<Banner url={"https://testingapp.page.link/getapp"}/>
<Banner url={"https://testingapp.page.link/getapp"}/>
<Banner url={"https://testingapp.page.link/getapp"}/>
</div>
);
}
Banner.js
const Banner=(props)=>{
const classes = useStyles();
const [expanded, setExpanded] = React.useState(false);
const styles = {
cardAction: {
display: 'block',
textAlign: 'initial',
height: '100%'
}
}
return (
<div onClick={() => window.open(props.url, "_blank")}>
<Card className={classes.root}>
<CardMedia
className={classes.media}
image= {banner}
title="Offer"
/>
</Card>
</div>
);
};
export default Banner;
On the Mobile App side:
App.jsx
import React, { useEffect } from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import dynamicLinks from '#react-native-firebase/dynamic-links';
import { WebView } from 'react-native-webview';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
function DetailsScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
</View>
);
}
function HomeScreen({ navigation }) {
async function buildLink() {
console.log('building link');
const link = await dynamicLinks().buildLink({
link: 'https://invertase.io',
// domainUriPrefix is created in your firebase console
domainUriPrefix: 'https://testingapp.page.link',
// optional set up which updates firebase analytics campaign
// "banner". This also needs setting up before hand
analytics: {
campaign: 'banner',
},
});
return link;
}
const handleDynamicLink = link => {
// Handle dynamic link inside your own application
console.log('Received URfffL:' + link.url);
if (link.url === 'https://example.com/packageview') {
console.log('hit package view');
navigation.push('packageview');
}
};
useEffect(() => {
const unsubscribe = dynamicLinks().onLink(handleDynamicLink);
// When the is component unmounted, remove the listener
dynamicLinks()
.getInitialLink()
.then(link => {
console.log('Received URL:' + link.url);
if (link.url === 'https://example.com/packageview') {
console.log('hit package view');
navigation.push('packageview');
}
});
return () => unsubscribe();
}, []);
return (
<WebView source={{ uri: 'http://10.0.2.2:3000/' }}/>
);
}
export default function App() {
const Stack = createStackNavigator();
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="packageview" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Do anyone have any experience in anything related to this? On what's going wrong in here?
There is a solution, But more like a work around so it seems like webview blocking up requests with "intent://" head, as a solution "intent://" can be added to whitelist of the WebView. check the code below,
<WebView
originWhitelist={['intent://']}
source={{ uri: 'http://10.0.2.2:3000/' }}/>
But the user experience might not be that great as this makes the URL to be opened from the web browser and web browser will the one doing the naigation to the page inside the app.
Hey for me this worked .
add your dynamic link in webview orginWhitlelist without https/http
add intent:// too
<WebView
source={{ uri: redirect_url }}
originWhitelist={["intent://*", "applink.page.link/back"]}
/>
then follow https://rnfirebase.io/dynamic-links/usage documentication to Listen for Dynamic Links
make sure to select Open the deep link in your Android App for android and Open the deep link in your Apple app for ios while creating dynamic link in firbase
I need to render image tag in a loop based on array length,
My image tag looks similar to this
<Image
style={{width: 50, height: 50}}
source={{uri: 'https://facebook.github.io/react/img/logo_og1.png'}}
//if image fails to load from the uri then load from'./img/favicon.png'
onError={(error) => {console.log('loadinf',error)}}
/>
If error occurs while fetching from Uri i.e 404 error retry for sometime or show default local image.
How to do that in react native?
You put in a loading image in the place of real image using defaultSource property but it is only available to iOS as of this moment. We can achieve the other thing you wrote that it should display a local image in case it couldn't load the image from the internet by the following approach.
Store the original image URI in state.
Use this URI from state in source of the image.
When onError function is called change this state variable to your local image.
Example:
import React, {Component} from 'react';
import {
View,
Image
} from 'react-native';
export default class Demo extends Component {
constructor(props) {
super(props);
this.state = {
image: {
uri: 'something demo'
}
}
}
render() {
return <View>
<Image
source={ this.state.image }
onError={(a) => {
this.setState({
image: require('image.png')
});
}}
style={{ height: 100, width: 100 }}
/>
</View>
}
}
A dirty hack for retrying the image would be something like this:
let counter = 0;
export default class reactNativePange extends Component {
constructor(props) {
super(props);
this.state = {
image: {
uri: 'something demo'
},
failed: false
}
}
render() {
return (
<View style={styles.container}>
<Image
source={ this.state.failed ? require('./image.png') : { uri: 'something demo' } }
onError={(a) => {
if(counter >= 3) {
this.setState({
failed: true
});
} else {
counter++;
this.setState({
failed: true
});
setTimeout(() => {
this.setState({
failed: false
});
}, 1)
}
}}
style={{ height: 100, width: 100 }}
/>
</View>
);
}
}
As I mentioned that this is a dirty hack as the image might flicker during the retry.