Reputation: 714
I just installed react-native-file-viewer in my react native app and tested its open method to read the document that I generated using the app. However, I always encounter an error message saying "Cannot read property open of null"
I checked the URI that generated from StorageAccessFramework and there seem no issue and the file is successfully created using expo-file-system.
the only problem is with opening the file using react-native-file-viewer.
is there something I need to make that error gone?
I followed the instruction on installing the fileviewer in react native with expo..
EDIT:
This is my code in react native
common.helper.ts
import * as Print from 'expo-print'
import * as FileSystem from 'expo-file-system';
import { StorageAccessFramework } from 'expo-file-system';
import RNFileViewer from 'react-native-file-viewer';
export const exportBarcodeAndSaveAsPdf = async (barcodes: string[] = []) => {
const html = createHtml()
.build(
createHtml()
.createTable(
barcodes.map(o =>
createHtml()
.createImage(o, ['style="width: 115px; height: 115px;"'])
),
5
)
);
try
{
const {base64} = await Print.printToFileAsync({
html,
base64: true
});
switch(Platform.OS)
{
case 'android':
{
const permission = await StorageAccessFramework.requestDirectoryPermissionsAsync();
if(!permission.granted)
{
return;
}
const dateText = dateTimeText();
const fileName = `My-Barcode-${dateText}.pdf`;
const uri = await StorageAccessFramework.createFileAsync(permission.directoryUri, fileName , 'application/pdf');
console.log(uri);
await FileSystem.writeAsStringAsync(uri, base64, {encoding: FileSystem.EncodingType.Base64});
await RNFileViewer.open(uri, {showOpenWithDialog: true, showAppsSuggestions: true});
}
break;
}
}
catch (ex)
{
console.log(ex);
}
}
package.json
{
"name": "myfirst-mobile",
"version": "1.0.1",
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web"
},
"dependencies": {
"@kichiyaki/react-native-barcode-generator": "^0.6.7",
"@react-navigation/native": "^6.1.9",
"@react-navigation/native-stack": "^6.9.17",
"@realm/react": "^0.6.2",
"@reduxjs/toolkit": "^2.1.0",
"expo": "~50.0.7",
"expo-asset": "~9.0.2",
"expo-camera": "14.0.5",
"expo-file-system": "~16.0.5",
"expo-image-picker": "~14.7.1",
"expo-media-library": "~15.9.1",
"expo-print": "~12.8.1",
"expo-sqlite": "~13.2.2",
"expo-status-bar": "~1.11.1",
"expo-system-ui": "^2.9.3",
"react": "18.2.0",
"react-hook-form": "^7.49.3",
"react-native": "0.73.4",
"react-native-document-picker": "^9.1.0",
"react-native-file-viewer": "^2.1.5",
"react-native-html-to-pdf": "^0.12.0",
"react-native-paper": "^5.12.2",
"react-native-qrcode-svg": "^6.3.0",
"react-native-safe-area-context": "4.8.2",
"react-native-screens": "~3.29.0",
"react-native-svg": "^14.1.0",
"react-native-view-shot": "3.8.0",
"react-redux": "^9.1.0",
"realm": "^12.5.1"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@tsconfig/react-native": "^3.0.3",
"@types/jest": "^29.5.11",
"@types/react": "^18.2.48",
"@types/react-test-renderer": "^18.0.7",
"typescript": "^5.3.3"
},
"private": true
}
settings.gradle
rootProject.name = 'MyFirst Mobile'
dependencyResolutionManagement {
versionCatalogs {
reactAndroidLibs {
from(files(new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../gradle/libs.versions.toml")))
}
}
}
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle");
useExpoModules()
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
applyNativeModulesSettingsGradle(settings)
include ':app'
includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile())
include ':react-native-file-viewer'
project(':react-native-file-viewer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-file-viewer/android')
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<queries>
<intent>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https"/>
</intent>
<intent>
<action android:name="android.intent.action.VIEW"/>
<data android:mimeType="application/pdf" />
</intent>
</queries>
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme">
<meta-data android:name="expo.modules.updates.ENABLED" android:value="false"/>
<meta-data android:name="expo.modules.updates.EXPO_RUNTIME_VERSION" android:value="@string/expo_runtime_version"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="com.ajp.ajpmobile"/>
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false"/>
<provider android:name="com.vinzscam.reactnativefileviewer.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_viewer_provider_paths" />
</provider>
</application>
</manifest>
build.gradle
...
dependencies {
// The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true";
if (isGifEnabled) {
// For animated gif support
implementation("com.facebook.fresco:animated-gif:${reactAndroidLibs.versions.fresco.get()}")
}
if (isWebpEnabled) {
// For webp support
implementation("com.facebook.fresco:webpsupport:${reactAndroidLibs.versions.fresco.get()}")
if (isWebpAnimatedEnabled) {
// Animated webp support
implementation("com.facebook.fresco:animated-webp:${reactAndroidLibs.versions.fresco.get()}")
}
}
implementation("com.facebook.react:flipper-integration")
if (hermesEnabled.toBoolean()) {
implementation("com.facebook.react:hermes-android")
} else {
implementation jscFlavor
}
implementation project(':react-native-file-viewer')
}
...
MainApplication.kt
package com.myfirst.myfirstmobile
import android.app.Application
import android.content.res.Configuration
import androidx.annotation.NonNull
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.ReactHost
import com.facebook.react.config.ReactFeatureFlags
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.flipper.ReactNativeFlipper
import com.facebook.soloader.SoLoader
import expo.modules.ApplicationLifecycleDispatcher
import expo.modules.ReactNativeHostWrapper
import com.vinzscam.reactnativefileviewer.RNFileViewerPackage;
class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper(
this,
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> = PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
add(RNFileViewerPackage())
}
override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry"
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
}
)
override val reactHost: ReactHost
get() = getDefaultReactHost(this.applicationContext, reactNativeHost)
override fun onCreate() {
super.onCreate()
SoLoader.init(this, false)
if (!BuildConfig.REACT_NATIVE_UNSTABLE_USE_RUNTIME_SCHEDULER_ALWAYS) {
ReactFeatureFlags.unstable_useRuntimeSchedulerAlways = false
}
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
load()
}
if (BuildConfig.DEBUG) {
ReactNativeFlipper.initializeFlipper(this, reactNativeHost.reactInstanceManager)
}
ApplicationLifecycleDispatcher.onApplicationCreate(this)
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig)
}
}
result:
Upvotes: 0
Views: 579
Reputation: 714
I just figured out why its not working.
its because I am running the application with the expo commands
expo is not taking manual changes in native codes so I drop the idea of using react-native-file-viewer and luckily I found expo-intent-launcher which is the best solution for me as it is an expo package
here is the SO reference I got with usage:
View or open PDF files stored locally Expo React Native
Upvotes: 0