Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update custom webview documentation with kotlin code examples #3060

Merged
merged 7 commits into from
May 3, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions docs/custom-webview-android.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ id: custom-webview-android
title: Custom WebView
---

import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import constants from '@site/core/TabsConstants';

While the built-in web view has a lot of features, it is not possible to handle every use-case in React Native. You can, however, extend the web view with native code without forking React Native or duplicating all the existing web view code.

:::info
Expand All @@ -24,6 +26,9 @@ To get started, you'll need to create a subclass of `RNCWebViewManager`, `RNCWeb
- `getName`
- `addEventEmitters`

<Tabs groupId="android-language" defaultValue={constants.defaultAndroidLanguage} values={constants.androidLanguages}>
<TabItem value="java">

```java
@ReactModule(name = CustomWebViewManager.REACT_CLASS)
public class CustomWebViewManager extends RNCWebViewManager {
Expand Down Expand Up @@ -55,12 +60,43 @@ public class CustomWebViewManager extends RNCWebViewManager {
}
```

</TabItem>
<TabItem value="kotlin">

```kotlin
@ReactModule(name = CustomWebViewManager.REACT_CLASS)
class CustomWebViewManager : RNCWebViewManager() {
protected class CustomWebViewClient : RNCWebViewClient()
protected inner class CustomWebView(reactContext: ThemedReactContext?) :
RNCWebView(reactContext)

override fun createRNCWebViewInstance(reactContext: ThemedReactContext?): RNCWebView {
return CustomWebView(reactContext)
}

override fun addEventEmitters(reactContext: ThemedReactContext, view: WebView) {
view.webViewClient = CustomWebViewClient()
}

companion object {
/* This name must match what we're referring to in JS */
const val REACT_CLASS = "RCTCustomWebView"
}
}
```

</TabItem>
</Tabs>

You'll need to follow the usual steps to [register the module](native-modules-android.md#register-the-module).

### Adding New Properties

To add a new property, you'll need to add it to `CustomWebView`, and then expose it in `CustomWebViewManager`.

<Tabs groupId="android-language" defaultValue={constants.defaultAndroidLanguage} values={constants.androidLanguages}>
<TabItem value="java">

```java
public class CustomWebViewManager extends RNCWebViewManager {
...
Expand Down Expand Up @@ -89,10 +125,33 @@ public class CustomWebViewManager extends RNCWebViewManager {
}
```

</TabItem>
<TabItem value="kotlin">

```kotlin
class CustomWebViewManager : RNCWebViewManager() {
protected inner class CustomWebView(
reactContext: ThemedReactContext?,
var finalUrl: String? = null
) : RNCWebView(reactContext)

@ReactProp(name = "finalUrl")
fun setFinalUrl(view: WebView, url: String?) {
(view as CustomWebView).finalUrl = url
}
}
```

</TabItem>
</Tabs>

### Adding New Events

For events, you'll first need to make create event subclass.

<Tabs groupId="android-language" defaultValue={constants.defaultAndroidLanguage} values={constants.androidLanguages}>
<TabItem value="java">

```java
// NavigationCompletedEvent.java
public class NavigationCompletedEvent extends Event<NavigationCompletedEvent> {
Expand All @@ -116,10 +175,32 @@ public class NavigationCompletedEvent extends Event<NavigationCompletedEvent> {
}
```

</TabItem>
<TabItem value="kotlin">

```kotlin
// NavigationCompletedEvent.kt
class NavigationCompletedEvent(viewTag: Int, val params: WritableMap) :
Event<NavigationCompletedEvent>(viewTag) {
override fun getEventName(): String = "navigationCompleted"

override fun dispatch(rctEventEmitter: RCTEventEmitter) {
init(viewTag)
rctEventEmitter.receiveEvent(viewTag, eventName, params)
}
}
```

</TabItem>
</Tabs>

You can trigger the event in your web view client. You can hook existing handlers if your events are based on them.

You should refer to [RNCWebViewManager.java](https://github.com/react-native-webview/react-native-webview/blob/master/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java) in the React Native WebView codebase to see what handlers are available and how they are implemented. You can extend any methods here to provide extra functionality.

<Tabs groupId="android-language" defaultValue={constants.defaultAndroidLanguage} values={constants.androidLanguages}>
<TabItem value="java">

```java
public class NavigationCompletedEvent extends Event<NavigationCompletedEvent> {
private WritableMap mParams;
Expand Down Expand Up @@ -158,8 +239,44 @@ protected static class CustomWebViewClient extends RNCWebViewClient {
}
```

</TabItem>
<TabItem value="kotlin">

```kotlin
class NavigationCompletedEvent(viewTag: Int, val params: WritableMap) :
Event<NavigationCompletedEvent>(viewTag) {

override fun getEventName(): String = "navigationCompleted"

override fun dispatch(rctEventEmitter: RCTEventEmitter) {
init(viewTag)
rctEventEmitter.receiveEvent(viewTag, eventName, params)
}
}

// CustomWebViewManager.kt

protected class CustomWebViewClient : RNCWebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, url: String?): Boolean {
val shouldOverride: Boolean = super.shouldOverrideUrlLoading(view, url)
val finalUrl: String? = (view as CustomWebView).finalUrl
if (!shouldOverride && url != null && finalUrl != null && url == finalUrl) {
val params: WritableMap = Arguments.createMap()
dispatchEvent(view, NavigationCompletedEvent(view.getId(), params))
}
return shouldOverride
}
}
```

</TabItem>
</Tabs>

Finally, you'll need to expose the events in `CustomWebViewManager` through `getExportedCustomDirectEventTypeConstants`. Note that currently, the default implementation returns `null`, but this may change in the future.

<Tabs groupId="android-language" defaultValue={constants.defaultAndroidLanguage} values={constants.androidLanguages}>
<TabItem value="java">

```java
public class CustomWebViewManager extends RNCWebViewManager {
...
Expand All @@ -177,6 +294,23 @@ public class CustomWebViewManager extends RNCWebViewManager {
}
```

</TabItem>
<TabItem value="kotlin">

```kotlin
class CustomWebViewManager : RNCWebViewManager() {
override fun getExportedCustomDirectEventTypeConstants(): MutableMap<Any?, Any?>? {
val superTypeConstants = super.getExportedCustomDirectEventTypeConstants()
val export = superTypeConstants ?: MapBuilder.newHashMap<Any, Any?>()
export["navigationCompleted"] = MapBuilder.of("registrationName", "onNavigationCompleted")
Comment on lines +303 to +305
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

return export
}
}
```

</TabItem>
</Tabs>

## JavaScript Interface

To use your custom web view, you'll need to create a class for it. Your class must:
Expand Down