React Native Expo CLI Import .ttf Fonts - react-native

Trying to import .ttf for font in expo cli.
I also have splash screen. I wanna show the splash screen until the font loads.
Font: Josefin Sans.
"expo": "~45.0.0"
I took reference from following links but nothing works:
Using Custom Fonts: https://docs.expo.dev/guides/using-custom-fonts/
Splash Screen: https://docs.expo.dev/versions/latest/sdk/splash-screen/
Code (App.js)
import { useState, useEffect, useCallback } from "react";
import { Text } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { StatusBar } from "expo-status-bar";
import Header from "./components/Header.component";
import styles from "./styles/appStyle";
import * as Font from "expo-font";
import * as SplashScreen from "expo-splash-screen";
const App = () => {
const [appIsReady, setAppIsReady] = useState(false);
useEffect(() => {
async function prepare() {
try {
// Pre-load fonts
await Font.loadAsync({
"JosefinSans-Regular": require("./assets/fonts/JosefinSans-Regular.ttf"),
});
// 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) {
} 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;
}
return (
<>
<SafeAreaView style={styles.container} onLayout={onLayoutRootView}>
<Header />
<Text>Hello World!</Text>
<StatusBar style="light" backgroundColor="#05060B" />
</SafeAreaView>
</>
);
};
export default App;
Error
Android Bundling failed 12ms
Unable to resolve module ./assets/fonts/JosefinSans-Regular.ttf from C:\Users\user\Desktop\app\App.js:
None of these files exist:
* JosefinSans-Regular.ttf
* assets\fonts\JosefinSans-Regular.ttf\index(.native|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json)
18 | // Pre-load fonts
19 | await Font.loadAsync({
> 20 | "JosefinSans-Regular": require("./assets/fonts/JosefinSans-Regular.ttf"),
| ^
21 | });
22 | // Artificially delay for two seconds to simulate a slow loading
23 | // experience. Please remove this if you copy and paste the code!
File Structure:
Snap.png

Answer
expo install #expo-google-fonts/josefin-sans expo-font
And the code looks like this.
import { useState, useEffect, useCallback } from "react";
import { Text } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { StatusBar } from "expo-status-bar";
import Header from "./components/Header.component";
import styles from "./styles/appStyle";
import * as Font from "expo-font";
import * as SplashScreen from "expo-splash-screen";
import {
useFonts,
JosefinSans_100Thin,
JosefinSans_200ExtraLight,
JosefinSans_300Light,
JosefinSans_400Regular,
JosefinSans_500Medium,
JosefinSans_600SemiBold,
JosefinSans_700Bold,
JosefinSans_100Thin_Italic,
JosefinSans_200ExtraLight_Italic,
JosefinSans_300Light_Italic,
JosefinSans_400Regular_Italic,
JosefinSans_500Medium_Italic,
JosefinSans_600SemiBold_Italic,
JosefinSans_700Bold_Italic,
} from "#expo-google-fonts/josefin-sans";
const App = () => {
const [appIsReady, setAppIsReady] = useState(false);
let [fontsLoaded] = useFonts({
JosefinSans_100Thin,
JosefinSans_200ExtraLight,
JosefinSans_300Light,
JosefinSans_400Regular,
JosefinSans_500Medium,
JosefinSans_600SemiBold,
JosefinSans_700Bold,
JosefinSans_100Thin_Italic,
JosefinSans_200ExtraLight_Italic,
JosefinSans_300Light_Italic,
JosefinSans_400Regular_Italic,
JosefinSans_500Medium_Italic,
JosefinSans_600SemiBold_Italic,
JosefinSans_700Bold_Italic,
});
const prepare = async () => {
try {
// Pre-load fonts
await Font.loadAsync(fontsLoaded)
.then(() => {
setAppIsReady(true);
})
.catch((err) => {});
// 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) {}
};
useEffect(() => {
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;
}
return (
<>
<SafeAreaView style={styles.container} onLayout={onLayoutRootView}>
<Header />
<Text>Hello World!</Text>
<StatusBar style="light" backgroundColor="#05060B" />
</SafeAreaView>
</>
);
};
export default App;

Related

Font.loadAsync with expo SplashScreen won't work in react native

I updated to expo SDK 45. I used to load open-sans like so:
const fetchFonts = () => {
return Font.loadAsync({
"open-sans": require("./assets/fonts/OpenSans-Regular.ttf"),
"open-sans-bold": require("./assets/fonts/OpenSans-Bold.ttf"),
});
};
export default function App() {
const [dataLoaded, setDataLoaded] = useState(false);
if (!dataLoaded) {
return (
<AppLoading
startAsync={fetchFonts}
onFinish={() => setDataLoaded(true)}
onError={(err) => console.log(err)}
/>
);
}
Problem is that AppLoading is no longer supported. Instead one has to use SplashScreen now. I followed the example here. This is my code:
import * as Font from "expo-font";
import * as SplashScreen from 'expo-splash-screen';
SplashScreen.preventAutoHideAsync();
export default function App() {
/* Preload stuff */
const [appIsReady, setAppIsReady] = useState(false);
useEffect(() => {
async function prepare() {
try {
await Font.loadAsync({
"open-sans": require("./assets/fonts/OpenSans-Regular.ttf"),
"open-sans-bold": require("./assets/fonts/OpenSans-Bold.ttf"),
});
} catch (e) {
console.warn(e);
} finally {
}
}
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;
}
Now my app won't load - I'll only see the the splash screen. I can make the app work if I comment out SplashScreen.preventAutoHideAsync(); and onLayoutRootView. I then get the message that "fontfamily opensans is not a system font". Sometimes it also actually does load the font and it works (every other try)
Any thoughts?

How to use `expo-splash-screen` with `expo-google-fonts`?

The splash screen is using async operations to wait, while the fonts package is using a "custom hook" useFonts (I guess).
How to make the splash screen wait for the google fonts to load?
You can load fonts with loadAsync from expo-fonts, and manage splash screen with expo-splash-screen
import * as SplashScreen from 'expo-splash-screen';
import * as Font from 'expo-font';
import { Inter_900Black } from '#expo-google-fonts/inter';
export default function App() {
const [appIsReady, setAppIsReady] = useState(false);
useEffect(() => {
(async () => {
try {
await SplashScreen.preventAutoHideAsync();
await Font.loadAsync({ Inter_900Black });
}
catch {
// handle error
}
finally {
setAppIsReady(true);
}
})();
}, []);
const onLayout = useCallback(() => {
if (appIsReady) {
SplashScreen.hideAsync();
}
}, [appIsReady]);
if (!appIsReady) {
return null;
}
return (
<View style={styles.container} onLayout={onLayout}>
<Text style={{fontFamily: 'Inter_900Black'}}>
Example text
</Text>
</View>
);
}
This is compete!
import React, { useCallback, useEffect, useState } from 'react';
import * as SplashScreen from 'expo-splash-screen';
import * as Font from 'expo-font';
import { Montserrat_400Regular, Montserrat_500Medium, Montserrat_700Bold,
Montserrat_900Black } from '#expo-google-fonts/montserrat';
export default function App() {
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({ Montserrat_900Black });
// 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;
}
return (
<View
style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}
onLayout={onLayoutRootView}>
<Text style={{ fontFamily: 'Montserrat_900Black', fontSize: 18 }}>SplashScreen
Demo! 👋</Text>
</View>
);
}'

React Native App - swipe right means all componenets unmount....can this be stopped?

With Android...when you swipe right on an app, it will unmount all components and cease all app operations. First component to be unmounted appears to be the parent componenet (normally named app.js).....please correct me if Im wrong on this.
When this happens..I know the componentWillUnmount event fires as I added the code below that logs to the console.
componentWillUnmount() {
console.log('app.js....componentWillUnmount');
}
My question is whether I can add some extra code within componentWillUnmount that could Alert the user "are you sure you want to quit app?"....and give them an option so say "no" and keep the app live
The BackHandler API description on the React-Native official docs has the example for the same use case. You can take a look at the given example code snippet on the official docs and pick the example with functional or class based component as per your choice.
You can place that code at the top level component like App.js or Routes.js.
Another approach with the autohide toast can be:
Using functional component
import React, {useEffect, useRef} from 'react';
import {Text, SafeAreaView, BackHandler, ToastAndroid} from 'react-native';
export default function App() {
const doubleBackToExitPressedOnce = useRef(false);
useEffect(() => {
const backAction = () => {
if (doubleBackToExitPressedOnce.current) {
BackHandler.exitApp();
return true;
}
ToastAndroid.show('Press back again to exit', ToastAndroid.SHORT);
doubleBackToExitPressedOnce.current = true;
setTimeout(() => {
doubleBackToExitPressedOnce.current = false;
}, 2000);
return true;
};
const backHandler = BackHandler.addEventListener(
'hardwareBackPress',
backAction,
);
return () => backHandler.remove();
}, []);
return (
<SafeAreaView>
<Text>Hello world!</Text>
</SafeAreaView>
);
}
Using class component:
import React, {Component} from 'react';
import {SafeAreaView, Text, BackHandler, ToastAndroid} from 'react-native';
export default class App extends Component {
backAction = () => {
if (this.doubleBackToExitPressedOnce) {
BackHandler.exitApp();
}
ToastAndroid.show('Press back again to exit', ToastAndroid.SHORT);
this.doubleBackToExitPressedOnce = true;
setTimeout(() => {
this.doubleBackToExitPressedOnce = false;
}, 2000);
return true;
};
componentDidMount() {
this.doubleBackToExitPressedOnce = false;
this.backHandler = BackHandler.addEventListener(
'hardwareBackPress',
this.backAction,
);
}
componentWillUnmount() {
this.backHandler.remove();
}
render() {
return (
<SafeAreaView>
<Text>Hello world!</Text>
</SafeAreaView>
);
}
}

console.error: "fontFamily "Roboto_medium" is not a system font and has not been loaded through Font.loadAsync

When using import Native Base (as it comes) I have encountered trouble because of a Font error shown in screen. If you click dismiss it will disappear but the user can't be seeing that every time a Text gets loaded. ¿Is there a correct way to solve the font problem?
This official documentation says to do this:
// At the top of your file
import { Font } from 'expo';
import { Ionicons } from '#expo/vector-icons';
// Later on in your component
async componentDidMount() {
await Font.loadAsync({
'Roboto': require('native-base/Fonts/Roboto.ttf'),
'Roboto_medium': require('native-base/Fonts/Roboto_medium.ttf'),
...Ionicons.font,
});
}
but it didn't work. This is my code:
import React, { Component } from 'react';
import { View, } from "react-native";
import { Button, Text } from 'native-base';
export default class MyComponent extends Component {
render() {
return (
<View>
<Button>
<Text>Click me!</Text>
</Button>
</View>
)
}
}
I expect the code to run smoothly but every time it loads the same error:
console.error: "fontFamily "Roboto_medium" is not a system font and has not been loaded through Font.loadAsync.
- If you intended to use a system font, make sure you typed the name correctly and that it is supported by your device operating system.
- If this is a custom font, be sure to load it with Font.loadAsync."
__expoConsoleLog
AppEntry.bundle?platform=android&dev=true&minify=false&hot=false:95014:31
...................
...................
...................
Native Base uses Roboto_Medium as a font for Title and some objects. Roboto_Medium is not a system font.
You can do either two things
Install and Load Roboto_Medium font in your codebase.
Edit existing Native Base core files
1) Install and Load Roboto_Medium font in your codebase
After installing Native Base, run these in terminal expo install expo-font.
After that Open your App.js file, add this two lines,
import * as Font from 'expo-font';
import { Ionicons } from '#expo/vector-icons';
After that include function componentDidMount()
async componentDidMount() {
await Font.loadAsync({
Roboto: require('native-base/Fonts/Roboto.ttf'),
Roboto_medium: require('native-base/Fonts/Roboto_medium.ttf'),
...Ionicons.font,
});
this.setState({ isReady: true });
}
You must call componentDidMount() function. If you are using Class Component, then this can be called using constuctor method
constructor(){
componentDidMount();
}
But, if you are using Functional Method, then you have manually call the componentDidMount() function.
2) Edit existing Native Base core files (Alternative)
You have to edit core Native Base files.
Location of File:
commonColor.js
node_modules\native-base\dist\src\theme\variables \ commonColor.js
material.js
node_modules\native-base\dist\src\theme\variables \ material.js
platform.js
node_modules\native-base\dist\src\theme\variables \ platform.js
In this files, find "Roboto_Medium" and replace it with "Roboto" or any other system default fonts.
But, as we have hardcoded the node_modules, with each update of Native Base, you have to again hard code the values again.
If anyone still has this problem and is using functional components, i solved it like this:
import * as Font from 'expo-font';
useEffect(() => {
(async () => await Font.loadAsync({
Roboto: require('native-base/Fonts/Roboto.ttf'),
Roboto_medium: require('native-base/Fonts/Roboto_medium.ttf'),
}))();
}, [])
For newer Functional Components solved it like this
import { View } from 'react-native';
import { NativeBaseProvider, Text } from 'native-base';
const MyComponent = () => {
return (
<NativeBaseProvider>
<View>
<Text>Example Text</Text>
</View>
</NativeBaseProvider>
)
}
export default MyComponent;
For older Functional Components solved it like this
import { View } from 'react-native';
import { Text } from 'native-base';
import * as Font from 'expo-font';
const MyComponent = () => {
useEffect(() => {
(async () => await Font.loadAsync({
Roboto: require('native-base/Fonts/Roboto.ttf'),
Roboto_medium: require('native-base/Fonts/Roboto_medium.ttf'),
}))();
}, [])
return (
<View>
<Text>Example Text</Text>
</View>
)
}
export default MyComponent;
For Class Components solved it like this
import React, { Component } from 'react';
import { View, } from "react-native";
import { Button, Text } from 'native-base';
import { Font } from 'expo';
import { Ionicons } from '#expo/vector-icons';
export default class MyComponent extends Component {
state = {
loading: true
}
async componentDidMount() {
await Font.loadAsync({
'Roboto': require('native-base/Fonts/Roboto.ttf'),
'Roboto_medium': require('native-base/Fonts/Roboto_medium.ttf'),
...Ionicons.font,
})
this.setState({ loading: false })
}
render() {
if (this.state.loading) {
return (
<View></View>
);
}
return (
<View>
<Button>
<Text>Click me!</Text>
</Button>
</View>
)
}
}

How do I go back in webview? I am using the react-navigation package in react-native

I installed the react-navigation package in react-native
I have implemented tab navigation and one of them is implemented in webview format.
My problem is that if I press the back physical button on Android, I go from the app itself to the previous tab, not back from the webview.
I've already applied the back button for the webview on the internet, but I have not done that.
I tried to display the onNavigationStateChange log when debugging, but it was not updated when url was moved after it was loaded at first startup. Here is the code I implemented:
import React from "react";
import {BackHandler} from "react-native";
import {WebView} from "react-native-webview";
class SermonScreen extends React.Component {
constructor(props) {
super(props);
}
static navigationOptions = {
header: null
};
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
}
_onNavigationStateChange(navState) {
console.log(navState);
this.setState({
canGoBack: navState.canGoBack
});
}
handleBackButton = () => {
console.log(this.state);
if (this.state.canGoBack === true) {
this.webView.goBack();
return true;
} else {
return false;
}
};
render() {
return (
<WebView
source={{uri: 'https://m.youtube.com/channel/UCw3kP3qCCF7ZpLUNzm_Q9Xw/videos' }}
ref={(webView) => this.webView = webView}
onNavigationStateChange={this._onNavigationStateChange.bind(this)}
/>
);
}
}
export default SermonScreen;
Following the official webview documnentation you could try to do this: https://github.com/react-native-community/react-native-webview/blob/master/docs/Guide.md#intercepting-hash-url-changes
In general you were almost there, however the way the YT navigation works made it impossible to be caught via the onNavigationStateChange, that's why we inject a JS code that intercepts these hash changes and posts a message to the parent component, we then catch it inside the onMessage handler and set the state variable properly. Copying the injectedJavaScript and onMessage properties to your example should solve your problem.
I prepared a component for you that seems to do what is needed:
* Sample React Native App
* https://github.com/facebook/react-native
*
* #format
* #flow
*/
import React, { Fragment } from "react";
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
BackHandler,
StatusBar
} from "react-native";
import { WebView } from "react-native-webview";
import {
Header,
LearnMoreLinks,
Colors,
DebugInstructions,
ReloadInstructions
} from "react-native/Libraries/NewAppScreen";
class App extends React.Component {
constructor(props) {
super(props);
this.startingUrl =
"https://m.youtube.com/channel/UCw3kP3qCCF7ZpLUNzm_Q9Xw/videos";
this.handleBackButton = this.handleBackButton.bind(this);
}
componentDidMount() {
BackHandler.addEventListener("hardwareBackPress", this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress", this.handleBackButton);
}
handleBackButton = () => {
console.log(this.state);
const { canGoBack } = this.state;
if (canGoBack) {
this.webView.goBack();
return true;
} else {
return false;
}
};
render() {
return (
<Fragment>
<WebView
source={{ uri: this.startingUrl }}
style={{ marginTop: 20 }}
ref={webView => (this.webView = webView)}
injectedJavaScript={`
(function() {
function wrap(fn) {
return function wrapper() {
var res = fn.apply(this, arguments);
window.ReactNativeWebView.postMessage('navigationStateChange');
return res;
}
}
history.pushState = wrap(history.pushState);
history.replaceState = wrap(history.replaceState);
window.addEventListener('popstate', function() {
window.ReactNativeWebView.postMessage('navigationStateChange');
});
})();
true;
`}
onMessage={({ nativeEvent: state }) => {
if (state.data === "navigationStateChange") {
// Navigation state updated, can check state.canGoBack, etc.
this.setState({
canGoBack: state.canGoBack
});
}
}}
/>
</Fragment>
);
}
}
export default App;
The response above was perfect. I set the state true for canGoBack though; I was getting a null error, so:
constructor(props) {
super(props);
this.startingUrl = "https://app.vethorcardpag.com.br/GIF/login/0/";
this.state = {
canGoBack : true
}
this.handleBackButton = this.handleBackButton.bind(this);
}
Here is a simple solution using the magic of React's State.
Hope this helps.
import React, { useRef, useState } from 'react'
export default function Component () {
// This is used to save the reference of your webview, so you can control it
const webViewRef = useRef(null);
// This state saves whether your WebView can go back
const [webViewcanGoBack, setWebViewcanGoBack] = useState(false);
const goBack = () => {
// Getting the webview reference
const webView = webViewRef.current
if (webViewcanGoBack)
// Do stuff here if your webview can go back
else
// Do stuff here if your webview can't go back
}
return (
<WebView
source={{ uri: `Your URL` }}
ref={webViewRef}
javaScriptEnabled={true}
onLoadProgress={({ nativeEvent }) => {
// This function is called everytime your web view loads a page
// and here we change the state of can go back
setWebViewcanGoBack(nativeEvent.canGoBack)
}}
/>
)
}
Original answer
https://stackoverflow.com/a/74500469/7823800