How do I make the expo image picker dark themed - react-native

This is my current image picker function
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
})
console.log(result)
if (!result.cancelled) {
setImage(result.uri)
}
}
I am trying to make the pop-up gallery dark-themed. How do I do that?

React Navigation has a Theme component that would be perfect for this! You can use their default theme, or customize it however you want! Check out details here.
Adding a theme to your project is actually super easy. Assuming you're already using React-Navigation, then you don't even need to download an additional dependency.
Create a new .js file called whatever you want, typical name would be theme.js. Inside paste this:
import * as React from 'react';
import { NavigationContainer, DefaultTheme } from '#react-navigation/native';
const MyTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
primary: 'rgb(255, 45, 85)',
},
};
This theme component has a whole bunch of props to choose from so make sure to check it out.
Here is an example from the docs:
const MyTheme = {
dark: false,
colors: {
primary: 'rgb(255, 45, 85)',
background: 'rgb(242, 242, 242)',
card: 'rgb(255, 255, 255)',
text: 'rgb(28, 28, 30)',
border: 'rgb(199, 199, 204)',
notification: 'rgb(255, 69, 58)',
},
};
Now for the dark mode portion, React-Navigation has built in themes, default and dark: import { DefaultTheme, DarkTheme } from '#react-navigation/native';
Inside your navigation container, now all you have to do is call the theme you'd like to use:
import { useColorScheme } from 'react-native';
import {
NavigationContainer,
DefaultTheme,
DarkTheme,
} from '#react-navigation/native';
export default () => {
const scheme = useColorScheme();
return (
<NavigationContainer theme={scheme === 'dark' ? DarkTheme : DefaultTheme}>
{/* content */}
</NavigationContainer>
);
};
This is a great rudimentary way to get start with light and dark themes. Once you get the hang of it, you can implement State as well to make your app toggle between default and dark mode!

Related

React Native Paper - Redux ToolKit : Redux doesn't track change in fonts between different themes

I am building an application using React Native and Expo.
I'm using Redux ToolKit to store the state of different themes in React Native Paper. In this example, I switch between two themes (one light and one dark) with a simple switch. I can change the colors without any problem and I can see in the debugger that Redux follows perfectly the state changes between the two themes.
However, if I change the font size for only the light theme with the configureFonts function recommended by Paper, Redux (and so my application) does not follow the font size changes between the two themes.
Here is my redux slice (themeSlice.js):
import { createSlice, current } from '#reduxjs/toolkit'
import { MD3LightTheme, MD3DarkTheme, configureFonts } from 'react-native-paper';
const fontConfigLight = {
"labelLarge": {
"fontSize": 20,
"fontWeight": "500",
"letterSpacing": 0.1,
"lineHeight": 25,
}
};
const lightTheme = {
...MD3LightTheme,
fonts: configureFonts({config: fontConfigLight}),
};
const darkTheme = {
...MD3DarkTheme,
};
export const themeSlice = createSlice({
name: 'counter',
initialState: lightTheme,
reducers: {
switchTheme: (state, action) => {
if (action.payload === 'light'){
// console.log('light theme : labelLarge', current(state.fonts.labelLarge))
return lightTheme
} else {
// console.log('dark theme : labelLarge', current(state.fonts.labelLarge))
return darkTheme
}
},
}
})
export const { switchTheme } = themeSlice.actions
export default themeSlice.reducer
and I use the reducer in the SwitchPaper.js file :
import { useState } from 'react';
import { Switch } from 'react-native-paper';
import { useDispatch } from 'react-redux'
import { switchTheme } from './themeSlice'
const ThemeSwitch = () => {
const [isSwitchOn, setIsSwitchOn] = useState(false);
const dispatch = useDispatch()
const onToggleSwitch = () => {
setIsSwitchOn(!isSwitchOn)
if (isSwitchOn) {
dispatch(switchTheme('light'))
} else {
dispatch(switchTheme('dark'))
}
}
return (
<Switch value={isSwitchOn} onValueChange={onToggleSwitch} />
);
};
export default ThemeSwitch;
I suspect that the configureFonts function causes a conflict with the immutability of the Redux store as I sometimes get the error : TypeError: Cannot assign to read only property 'labelLarge' of object '#'.
I am looking for a solution to change fonts between themes while keeping my themes in the global Redux state.
EDIT:
a non-elegant way to solve the problem would be to change darkTheme to :
const darkTheme = {
...MD3DarkTheme,
myOwnProperty: true,
fonts: {
...MD3DarkTheme.fonts,
"labelLarge": {
fontFamily: "System",
letterSpacing: 0.5,
fontWeight: "500",
lineHeight: 16,
fontSize: 11
}
}
};
wishing that the function configureFonts was not there for a particular (and good) reason ...

Warning: React has detected a change in the order of Hooks

I have run into this error in my code, and don't really know how to solve it, can anyone help me?
I get the following error message:
ERROR Warning: React has detected a change in the order of Hooks called by ScreenA. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks
import React, { useCallback, useEffect, useState } from "react";
import { View, Text, StyleSheet, Pressable } from "react-native";
import { useNavigation } from '#react-navigation/native';
import { DancingScript_400Regular } from "#expo-google-fonts/dancing-script";
import * as SplashScreen from 'expo-splash-screen';
import * as Font from 'expo-font';
export default function ScreenA({ route }) {
const [appIsReady, setAppIsReady] = useState(false);
useEffect(() => {
async function prepare() {
try {
// Keep the splash screen visible while we fetch resources
await SplashScreen.preventAutoHideAsync();
// Pre-load fonts, make any API calls you need to do here
await Font.loadAsync({ DancingScript_400Regular });
// Artificially delay for two seconds to simulate a slow loading
// experience. Please remove this if you copy and paste the code!
await new Promise(resolve => setTimeout(resolve, 2000));
} catch (e) {
console.warn(e);
} finally {
// Tell the application to render
setAppIsReady(true);
}
}
prepare();
}, []);
const onLayoutRootView = useCallback(async () => {
if (appIsReady) {
// This tells the splash screen to hide immediately! If we call this after
// `setAppIsReady`, then we may see a blank screen while the app is
// loading its initial state and rendering its first pixels. So instead,
// we hide the splash screen once we know the root view has already
// performed layout.
await SplashScreen.hideAsync();
}
}, [appIsReady]);
if (!appIsReady) {
return null;
}
const navigation = useNavigation();
const onPressHandler = () => {
// navigation.navigate('Screen_B', { itemName: 'Item from Screen A', itemID: 12 });
}
return (
<View style={styles.body} onLayout={onLayoutRootView}>
<Text style={styles.text}>
Screen A
</Text>
<Pressable
onPress={onPressHandler}
style={({ pressed }) => ({ backgroundColor: pressed ? '#ddd' : '#0f0' })}
>
<Text style={styles.text}>
Go To Screen B
</Text>
</Pressable>
<Text style={styles.text}>{route.params?.Message}</Text>
</View>
)
}
const styles = StyleSheet.create({
body: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontSize: 40,
margin: 10,
fontFamily: 'DancingScript_400Regular'
}
})
I have read the rules of hooks: https://reactjs.org/docs/hooks-rules.html
The output is correct, but i want to fix this error before i add more additions to the app
You need to move useNavigation use before early returns.
Instead, always use Hooks at the top level of your React function, before any early returns.
The key is you need to call all the hooks in the exact same order on every component lifecycle update, which means you can't use hooks with conditional operators or loop statements such as:
if (customValue) useHook();
// or
for (let i = 0; i< customValue; i++) useHook();
// or
if (customValue) return;
useHook();
So moving const navigation = useNavigation(); before if (!appIsReady) {return null;}, should solve your problem:
export default function ScreenA({ route }) {
const [appIsReady, setAppIsReady] = useState(false);
const navigation = useNavigation();
// ...
}

Moti animations not displayed on react native application

I am new to react native and was trying to play with Moti animations, I am not sure why the animations are not getting loaded, the code is :
import { MotiView, MotiText } from "moti"
import React, { useState, useEffect } from "react";
import { View } from 'react-native';
export default function App() {
return (
<View>
<Text>Hello world</Text>
<MotiView
from={{ opacity: 0, scale: 0.5 }}
animate={{ opacity: 1, scale: 1 }}
transition={{
// default settings for all style values
type: 'timing',
duration: 350,
// set a custom transition for scale
scale: {
type: 'spring',
delay: 100,
},
}}
/>
</View>
)
}
In above code the text "hello world" is displayed, but the motiview is not getting displayed.
babel.config.js file looks like this
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: ['react-native-reanimated/plugin']
};
};
package.json versions:
"react-native-reanimated": "~2.9.1",
"moti": "^0.21.0",
Need some help, can anyone tell where I am going wrong.
The style property was missing from MotiView properties, we need to set width and height of the view.

Is there a way to set a font globally in React Native?

I need to create a custom font that applies to every Text component in the whole application.
Is there is a way to set a font globally in React Native?
One way is to create a wrapper for RN Text say MyTextCustomFont:
const MyTextCustomFont = (props) => {
return (
<Text style={{fontFamily:'myFont'}} {...props} >{props.children}</Text>
)
}
import this MyTextCustomFont and use anywhere.
Another way is to define a style object and use it wherever you want.
To do this we have to implement a method in which we will override Text component creation in React Native. In this we will set default font family or size or any attribute we want to set by default.
// typography.js
import React from 'react'
import { Text, Platform, StyleSheet } from 'react-native'
export const typography = () => {
const oldTextRender = Text.render
Text.render = function(...args) {
const origin = oldTextRender.call(this, ...args)
return React.cloneElement(origin, {
style: [styles.defaultText, origin.props.style],
})
}
}
const styles = StyleSheet.create({
defaultText: {
fontFamily: 'NunitoSans-Regular',//Default font family
}
});
Then in index.js you have to do this:
import { typography } from './src/utils/typography'
typography()
Detailed answer here:
https://ospfolio.com/two-way-to-change-default-font-family-in-react-native/
I think your problem is add Custom Fonts in react native.
1. Add Your Custom Fonts to Assets
Add all the font files you want to an “assets/fonts” folder in the root of your react native project:
2. Edit Package.json
Adding rnpm to package.json providing the path to the font files:
"rnpm": {
"assets": [
"./assets/fonts/"
]
},
3. Link assest files
run this command in your react native project root folder
react-native link
This should add the font references in your Info.plist file for iOS and on Android copy the font files to android/app/src/main/assets/fonts.
4. Add in stylesheet
Add a fontFamily property with your font name:
const styles = StyleSheet.create({
title: {
fontSize: 16,
fontFamily: 'PlayfairDisplay-Bold',
color: '#fff',
paddingRight: 20,
},
});
So, I've made a component doing this quite easely some times ago. This is working with Expo, I don't know for vanilla react-native.
at the start of your app:
import { Font, Asset } from 'expo'
async initFont() {
try {
await Font.loadAsync({
'Bariol': require('src/assets/font/Bariol_Regular.otf'),
'Bariol Bold': require('src/assets/font/Bariol_Bold.otf'),
})
this.setState({ fontReady: true })
} catch (e) {
console.log(e)
}
}
Then, you have to create a component file like text.js containing this code:
export default function (props) {
let font = { fontFamily: 'Bariol' }
if (props.bold) {
font = { fontFamily: 'Bariol Bold' }
}
const { bold, style, children, ...newProps } = props
return (
<Text {...newProps} style={[Style.text, props.style, font]}>
{props.children}
</Text>
)
}
Finally, in any of you other component / page just import MyText:
import Text from 'path/to/text.js'
use it like a normal Text component:
<Text bold>Hello World!</Text>
Even if this solution looks a bit more complicated than the others, it is easier to use once the setup is ok, since you just have to import Text.
You can override Text behaviour by adding this in any of your component using Text:
Edit: Add this code in your App.js or main file
let oldRender = Text.render;
Text.render = function (...args) {
let origin = oldRender.call(this, ...args);
return React.cloneElement(origin, {
style: [{color: 'red', fontFamily: 'Arial'}, origin.props.style]
});
}
For react Native Version 0.56 or below, Add this code in your App.js or main file
let oldRender = Text.prototype.render;
Text.prototype.render = function (...args) {
let origin = oldRender.call(this, ...args);
return React.cloneElement(origin, {
style: [{color: 'red', fontFamily: 'Arial'}, origin.props.style]
});
};
Reference
Or create your own component, such as MyAppText.
MyAppText would be a simple component that renders a Text component using your universal style and can pass through other props, etc.
I use a wrapper with default props like this :
const CustomText = ({ fontFam = "regular", ...props }) => {
const typo = {
light: "Montserrat_300Light",
regular: "Montserrat_400Regular",
bold: "Montserrat_600SemiBold",
};
return (
<Text {...props} style={[{ fontFamily: typo[fontFam], ...props.style }]}>
{props.children}
</Text>
);
};
export default CustomText;
By default, if "fontFam" is not indicated it will be regular font.
An example with bold typo :
<CustomText fontFam="bold" style={{ marginTop: 30, color: "grey" }}>
Some Text
</CustomText>
You can replace all your <Text/> by <CustomText />.
If you don't have to create custom component, you could try react-native-global-font. It will be apply for your all Text and TextInput
yes
app.js
import styles from './styles';
{...}
<Text style={styles.text}>hello World </Text>
{...}
styles.js
import {StyleSheet} from 'react-native';
const styles = StyleSheet.create({
text: {
// define your font or size or whatever you want to style here
},
use style on every text and all changes will affect all text components

How to "lazy load" tab navigator screens now that lazy has been removed from react-navigation

The maintainers of react-navigation have removed 'lazy: true' from the library, causing all tabs to attempt to render at once (and fetches previously controlled by lazy now firing out of order).
In order to maintain similar functionality, how do you force a wait on a tab screen to not load or call fetch calls prior to being focused for the first time?
It seems they did remove it, but have decided to add it back in v 1.1.2
https://github.com/react-navigation/react-navigation/releases/tag/v1.1.2
Thus, you should be able to pass lazy={true} in your TabNavigatorConfig object, and then tabs will not be rendered before they are active. To further optimize memory usage, you can couple this with removeClippedSubviews to free memory from inactive tabs.
You can use LazyLoading from react-navigation-utils
React-navigation now suports withNavigationFocus wrapper.
You can use it to wrap the screen you want to prevent updating when it is not focused.
import React from 'react';
import { Text } from 'react-native';
import { withNavigationFocus } from 'react-navigation';
class LazyScreen extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return nextProps.isFocused;
}
render() {
return <Text>{this.props.isFocused ? 'Focused' : 'Not focused' </Text>;
}
}
export default withNavigationFocus(LazyScreen);
P.S. if you use Redux just do
export default connect(mapStateToProps, mapDispatchToProps)(withNavigationFocus(LazyScreen));
lazy={true}
optimizationsEnabled={true}
tabBarOptions={tabBarOptions}
the above code is important, try it
import { createMaterialTopTabNavigator } from "#react-navigation/material-top-tabs";
import { createStackNavigator } from "#react-navigation/stack";
const TabNavigator = createMaterialTopTabNavigator();
const Stack1 = createStackNavigator();
const Stack2 = createStackNavigator();
const ProductsScreen = (props) => {
//
return (
<TabNavigator.Navigator
lazy={true}
optimizationsEnabled={true}
tabBarOptions={tabBarOptions}
>
<TabNavigator.Screen name="HOME" component={StackScreen1} />
<TabNavigator.Screen name="SHOP" component={StackScreen2} />
</TabNavigator.Navigator>
);
};
const tabBarOptions = {
indicatorStyle: {
height: null,
top: 0,
backgroundColor: "#ccc",
borderBottomColor: "black",
borderBottomWidth: 3,
},
activeTintColor: "black",
style: {
backgroundColor: "red",
},
labelStyle: { fontSize: 13 },
};
How about this?
const MyTab = TabNavigator({
tab1:{screen:TabScreen1},
tab2:{screen:TabScreen2}
}
class MainScreen extends React.Component{
constructor(){
super();
this.state = {
loading:true
}
}
componentWillMount(){
//fetch login
//set loading:false when fetch is done
}
render(){
!this.state.loading && <MyTab/>
}
}
In the new versions of React Navigation the lazy prop is set to true by default.
See https://reactnavigation.org/docs/en/bottom-tab-navigator.html#lazy