I’m trying to use the signature canvas from react-native-signature-canvas. I’ve imported it as stated in the npm docs and placed it in my code perfectly. I can’t figure out why it doesn’t show up on my screen. Here’s my code:
import React, { useRef, useState } from "react";
import {
Text,
StyleSheet,
Pressable,
TextInput,
View,
SafeAreaView,
Image,
} from "react-native";
import SignatureScreen from "react-native-signature-canvas";
export default function SignatureNav() {
const [colorText, setPenColor] = useState("");
const ref = useRef();
const [signature, setSign] = useState(null);
//onOk Function update
const handleOK = (signature) => {
setSign(signature);
};
const handleColorChange = () => {
ref.current.changePenColor(colorText);
};
// Function to handle Undo
const handleUndo = () => {
ref.current.undo();
};
const handleEmpty = () => {
console.log("Empty");
};
// Function to handle Redo
const handleRedo = () => {
ref.current.redo();
};
const handleClear = () => {
console.log("clear success!");
};
return (
<View style={styles.container}>
<Text style={styles.textSign}>Sign Below</Text>
<View style={styles.row}>
<Pressable
style={[
styles.setButton,
{ marginRight: 30, backgroundColor: "red" },
]}
onPress={handleUndo}
>
<Text style={styles.text}>Undo</Text>
</Pressable>
<TextInput
placeholder="Specify Pen Color"
style={styles.textInput}
autoCapitalize="none"
value={colorText}
onChangeText={setPenColor}
/>
<Pressable style={styles.setButton} onPress={handleColorChange}>
<Text style={styles.text}>Set</Text>
</Pressable>
<Pressable
style={[styles.setButton, { marginLeft: 30, backgroundColor: "red" }]}
onPress={handleRedo}
>
<Text style={styles.text}>Redo</Text>
</Pressable>
</View>
<SignatureScreen
ref={ref}
onOK={(signature) => handleOK(signature)}
onEmpty={handleEmpty}
penColor={colorText}
onClear={handleClear}
confirmText="Preview"
/>
<Text style={styles.textSign}>Preview Signature</Text>
<Image
resizeMode={"cover"}
style={{ width: 300, height: 180, paddingBottom: 20 }}
source={{ uri: signature }}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
height: 250,
padding: 10,
},
row: {
flexDirection: "row",
marginTop: 10,
borderBottomWidth: 1,
borderBottomColor: "#f2f2f2",
paddingBottom: 5,
},
textSign: {
color: "deepskyblue",
fontWeight: "bold",
paddingVertical: 5,
},
text: {
color: "#fff",
fontWeight: "900",
},
textInput: {
paddingVertical: 10,
textAlign: "center",
},
setButton: {
backgroundColor: "deepskyblue",
textAlign: "center",
fontWeight: "900",
color: "#fff",
marginHorizontal: 10,
paddingVertical: 15,
paddingHorizontal: 10,
borderRadius: 5,
},
preview: {
width: 335,
height: 114,
backgroundColor: "#F8F8F8",
justifyContent: "center",
alignItems: "center",
marginTop: 15,
},
});
and here’s my package.json
{
"name": "data-sign-mobile-app",
"version": "1.0.0",
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web"
},
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0",
"@expo/metro-runtime": "~3.1.3",
"@expo/ngrok": "^2.5.0",
"@expo/vector-icons": "^14.0.0",
"@gluestack-style/react": "^1.0.49",
"@gluestack-ui/themed": "^1.1.9",
"@mui/icons-material": "^5.15.11",
"@mui/material": "^5.15.11",
"@react-native-async-storage/async-storage": "1.21.0",
"@react-navigation/drawer": "^6.6.9",
"@react-navigation/native": "^6.1.12",
"@react-navigation/stack": "^6.3.23",
"@reduxjs/toolkit": "^2.2.1",
"@rneui/themed": "^4.0.0-rc.8",
"@types/react-native": "^0.73.0",
"base-64": "^1.0.0",
"expo": "~50.0.8",
"expo-image-picker": "~14.7.1",
"expo-status-bar": "~1.11.1",
"react": "18.2.0",
"react-native": "0.73.4",
"react-native-canvas": "^0.1.39",
"react-native-dropdown-select-list": "^2.0.5",
"react-native-gesture-handler": "~2.14.0",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-reanimated": "~3.6.2",
"react-native-screens": "~3.29.0",
"react-native-signature-canvas": "^4.7.2",
"react-native-signature-capture": "^0.4.12",
"react-native-svg": "13.4.0",
"react-native-web": "~0.19.6",
"react-native-webview": "13.6.4",
"react-redux": "^9.1.0",
"@shopify/react-native-skia": "0.1.221"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@expo/ngrok": "^4.1.0"
},
"private": true
}
and here’s my App.js
import "react-native-gesture-handler";
import React, { useEffect, useState } from "react";
import {
ActivityIndicator,
View,
Image,
Text,
SafeAreaView,
} from "react-native";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import {
DrawerItemList,
createDrawerNavigator,
} from "@react-navigation/drawer";
import {
LoginScreen,
HomeScreen,
RegistrationScreen,
EditProfileScreen,
SignatureScreen,
} from "./src/screens";
import {
SimpleLineIcons,
MaterialIcons,
MaterialCommunityIcons,
FontAwesome,
} from "@expo/vector-icons";
import { decode, encode } from "base-64";
import { store } from "./src/features/common/store";
import { Provider } from "react-redux";
if (!global.btoa) {
global.btoa = encode;
}
if (!global.atob) {
global.atob = decode;
}
const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
export default function App() {
const [user, setUser] = useState({
username: "",
password: "",
hasPaid: true,
}); //change code for when backend stores if user has paid
const [waitingForUser, setWaitingForUser] = useState(false);
return (
<Provider store={store}>
<NavigationContainer>
{waitingForUser ? (
<ActivityIndicator
style={{ flex: 1, justifyContent: "center", alignItems: "center" }}
size="large"
/>
) : (
<>
{user ? (
<Drawer.Navigator
drawerContent={(props) => {
return (
<SafeAreaView>
<View
style={{
height: 200,
width: "100%",
justifyContent: "center",
alignItems: "center",
borderBottomColor: "#f4f4f4",
borderBottomWidth: 1,
}}
>
<Image
source={require("./assets/profile_logo.png")}
style={{
height: 130,
width: 130,
borderRadius: 65,
}}
/>
<Text
style={{
fontSize: 22,
marginVertical: 6,
fontWeight: "bold",
color: "#111",
}}
>
John Doe
</Text>
<Text
style={{
fontSize: 16,
color: "#111",
}}
>
Introduction
</Text>
</View>
<DrawerItemList {...props} />
</SafeAreaView>
);
}}
screenOptions={{
drawerStyle: {
backgroundColor: "#fff",
width: 250,
},
headerStyle: {
backgroundColor: "#788eec",
},
headerTintColor: "#fff",
headerTitleStyle: {
fontWeight: "bold",
},
drawerActiveTintColor: "#788eec", // use blue if it doesn't look good
drawerLabelStyle: {
color: "#111",
},
}}
>
{user.hasPaid ? (
<>
<Drawer.Screen
name="Signature"
options={{
drawerLabel: "Signature",
title: "Signature",
drawerIcon: () => (
<MaterialCommunityIcons
name="signature"
size={20}
color="#808080"
/>
),
}}
component={SignatureScreen}
/>
</>
) : (
<>
<Drawer.Screen
name="Home"
options={{
drawerLabel: "Home",
title: "Home",
drawerIcon: () => (
<SimpleLineIcons
name="home"
size={20}
color="#808080"
/>
),
}}
>
{(props) => <HomeScreen {...props} extraData={user} />}
</Drawer.Screen>
</>
)}
<Drawer.Screen
name="Edit Profile"
options={{
drawerLabel: "Edit Profile",
title: "Edit Profile",
drawerIcon: () => (
<FontAwesome name="edit" size={20} color="#808080" />
),
}}
component={EditProfileScreen}
/>
</Drawer.Navigator>
) : (
<Stack.Navigator>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen
name="Registration"
component={RegistrationScreen}
/>
</Stack.Navigator>
)}
</>
)}
</NavigationContainer>
</Provider>
);
}
and here’s related files (SignatureNav is called in Signature, which is called in SignatureScreen)
SignatureScreen:
import React from 'react'
import Signature from "../../features/signature"
import { ScrollView } from "react-native-gesture-handler";
export default function SignatureScreen() {
return (
<ScrollView>
<Signature />
</ScrollView>
)
}
Signature:
import React, { useEffect, useState } from "react";
import { useAppDispatch } from "../common/exports";
import { setPageTitle } from "../common/headerSlice";
import SignatureContent from "./SignatureContent";
import SignatureNav from "./SignatureNav";
import { Pressable, View, Text, StyleSheet } from "react-native";
export default function Signature() {
const [activeTab, setActiveTab] = useState("tab1");
const handleTabClick = (tab) => {
setActiveTab(tab);
};
return (
<View
style={{ display: "flex", flexDirection: "column", backgroundColor: "#fff" }}
>
{/* Sidebar */}
<View style={{ backgroundColor: "#bbdefb", padding: "2rem" }}>
<Pressable
style={styles.button}
onPress={() => handleTabClick("tab1")}
>
<Text style={styles.buttonTitle}>Generate Signature</Text>
</Pressable>
<Pressable
style={styles.button}
onPress={() => handleTabClick("tab2")}
>
<Text style={styles.buttonTitle}>Draw Signature</Text>
</Pressable>
</View>
{/* Main Content */}
<View style={{
flexGrow: 1,
overflow: 'hidden',
borderRadius: 8
}}
>
<View style={{
paddingTop: 32,
}}
>
{activeTab === 'tab1' && <SignatureContent />}
{activeTab === 'tab2' && <SignatureNav />}
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
button: {
backgroundColor: "#3f51b5",
marginLeft: 30,
marginRight: 30,
marginVertical: 20,
height: 48,
borderRadius: 5,
alignItems: "center",
justifyContent: "center",
paddingLeft: 20,
paddingRight: 20,
},
buttonTitle: {
color: "white",
fontSize: 16,
fontWeight: "bold",
},
});
I tried adding webStyles thinking that the problem was that there was no height, reinstalling the module, and using curly brackets {} to import the module. Can’t think of anything else. I’m new to React Native!
2
Answers
You are on Expo v50.0.8 which came out 16 days ago, yet you are trying to use react-native-signature-canvas v4.7.2 which came out 13 days ago.
I recommend you try v4.7.1 to see if that changes the behaviour.
The code you provided is incomplete. I can’t see where you are importing the ‘react-native-signature-canvas’ module. Anyways, this is working for me:
Container Style:
Although this works in Expo-Go, I have had issues in the development build using eas – it crashes on launch in iOS.