Skip to content

Commit 2d2aefb

Browse files
authored
Merge pull request #144 from KevinnZou/feature/supoort_viewstate
Feature/supoort rememberSaveableWebViewState for Android and iOS15+
2 parents 17855b3 + 25ac504 commit 2d2aefb

File tree

23 files changed

+675
-60
lines changed

23 files changed

+675
-60
lines changed

docs/state.md

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# State
2-
This library provides a [WebViewState](https://github.com/KevinnZou/compose-webview-multiplatform/blob/main/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewState.kt) class as a state holder to hold the state for the WebView.
2+
3+
This library provides
4+
a [WebViewState](https://github.com/KevinnZou/compose-webview-multiplatform/blob/main/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewState.kt)
5+
class as a state holder to hold the state for the WebView.
36

47
## WebViewState
58

@@ -52,7 +55,9 @@ class WebViewState(webContent: WebContent) {
5255

5356
## rememberWebViewState
5457

55-
It can be created using the [rememberWebViewState](https://github.com/KevinnZou/compose-webview-multiplatform/blob/c1104c4458277423ec0ee3386140e06950483cb4/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewState.kt#L87) function, which can be remembered across Compositions.
58+
It can be created using
59+
the [rememberWebViewState](https://github.com/KevinnZou/compose-webview-multiplatform/blob/c1104c4458277423ec0ee3386140e06950483cb4/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewState.kt#L87)
60+
function, which can be remembered across Compositions.
5661

5762
```kotlin
5863
val state = rememberWebViewState("https://github.com/KevinnZou/compose-webview-multiplatform")
@@ -81,11 +86,11 @@ fun rememberWebViewState(
8186
*/
8287
@Composable
8388
fun rememberWebViewStateWithHTMLData(
84-
data: String,
85-
baseUrl: String? = null,
86-
encoding: String = "utf-8",
87-
mimeType: String? = null,
88-
historyUrl: String? = null
89+
data: String,
90+
baseUrl: String? = null,
91+
encoding: String = "utf-8",
92+
mimeType: String? = null,
93+
historyUrl: String? = null
8994
)
9095

9196
/**
@@ -95,12 +100,56 @@ fun rememberWebViewStateWithHTMLData(
95100
*/
96101
@Composable
97102
fun rememberWebViewStateWithHTMLFile(
98-
fileName: String,
103+
fileName: String,
99104
)
100105
```
101106

107+
## rememberSaveableWebViewState
108+
109+
This library also provides a `rememberSaveableWebViewState` function that can be used to create a
110+
`WebViewState` that is remembered across recompositions.
111+
112+
It can be used in tab layouts, where the state of the WebView should be saved when the user switches
113+
between tabs.
114+
115+
```kotlin
116+
@Composable
117+
fun Home() {
118+
val url = "https://www.jetbrains.com/lp/compose-multiplatform/"
119+
val webViewState =
120+
rememberSaveableWebViewState(url).apply {
121+
webSettings.logSeverity = KLogSeverity.Debug
122+
}
123+
124+
val navigator = rememberWebViewNavigator()
125+
126+
LaunchedEffect(navigator) {
127+
val bundle = webViewState.viewState
128+
if (bundle == null) {
129+
// This is the first time load, so load the home page.
130+
navigator.loadUrl(url)
131+
}
132+
}
133+
134+
WebView(
135+
state = webViewState,
136+
modifier = Modifier.fillMaxSize().padding(bottom = 45.dp),
137+
navigator = navigator,
138+
)
139+
}
140+
```
141+
142+
Please refer to
143+
the [VoyagerNavigationSample](https://github.com/KevinnZou/compose-webview-multiplatform/blob/main/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/VoyagerNavigationSample.kt)
144+
for whole example.
145+
146+
```kotlin
147+
102148
## Usage
103-
Developers can use the `WebViewState` to get the loading information of the WebView, such as the loading progress, the loading status, and the URL of the current page.
149+
150+
Developers can use the `WebViewState` to get the loading information of the WebView, such as the
151+
loading progress, the loading status, and the URL of the current page.
152+
104153
```kotlin
105154
Column {
106155
val state = rememberWebViewState("https://github.com/KevinnZou/compose-webview-multiplatform")

sample/shared/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ kotlin {
3232

3333
sourceSets {
3434
val coroutinesVersion = extra["coroutines.version"] as String
35+
val voyagerVersion = "1.0.0-rc10"
3536

3637
val commonMain by getting {
3738
dependencies {
@@ -41,10 +42,13 @@ kotlin {
4142
@OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
4243
implementation(compose.components.resources)
4344
implementation("co.touchlab:kermit:2.0.3")
45+
implementation("cafe.adriel.voyager:voyager-navigator:$voyagerVersion")
46+
implementation("cafe.adriel.voyager:voyager-tab-navigator:$voyagerVersion")
4447
api(project(":webview"))
4548
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
4649
implementation("org.jetbrains.kotlinx:atomicfu:0.23.2")
4750
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
51+
implementation("org.jetbrains.androidx.navigation:navigation-compose:2.7.0-alpha03")
4852
}
4953
}
5054
val androidMain by getting {

sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/BasicWebViewSample.kt

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import androidx.compose.ui.Modifier
3131
import androidx.compose.ui.graphics.Color
3232
import androidx.compose.ui.graphics.ColorFilter
3333
import androidx.compose.ui.unit.dp
34+
import androidx.navigation.NavHostController
3435
import co.touchlab.kermit.Logger
3536
import com.multiplatform.webview.cookie.Cookie
3637
import com.multiplatform.webview.util.KLogSeverity
@@ -51,7 +52,7 @@ import kotlinx.coroutines.flow.filter
5152
* for setup instructions first.
5253
*/
5354
@Composable
54-
internal fun BasicWebViewSample() {
55+
internal fun BasicWebViewSample(navHostController: NavHostController? = null) {
5556
val initialUrl = "https://github.com/KevinnZou/compose-webview-multiplatform"
5657
val state = rememberWebViewState(url = initialUrl)
5758
DisposableEffect(Unit) {
@@ -72,13 +73,17 @@ internal fun BasicWebViewSample() {
7273
TopAppBar(
7374
title = { Text(text = "WebView Sample") },
7475
navigationIcon = {
75-
if (navigator.canGoBack) {
76-
IconButton(onClick = { navigator.navigateBack() }) {
77-
Icon(
78-
imageVector = Icons.Default.ArrowBack,
79-
contentDescription = "Back",
80-
)
76+
IconButton(onClick = {
77+
if (navigator.canGoBack) {
78+
navigator.navigateBack()
79+
} else {
80+
navHostController?.popBackStack()
8181
}
82+
}) {
83+
Icon(
84+
imageVector = Icons.Default.ArrowBack,
85+
contentDescription = "Back",
86+
)
8287
}
8388
},
8489
)

sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
package com.kevinnzou.sample
22

33
import androidx.compose.foundation.layout.Box
4+
import androidx.compose.foundation.layout.Column
45
import androidx.compose.foundation.layout.fillMaxSize
56
import androidx.compose.foundation.layout.padding
67
import androidx.compose.material.Button
8+
import androidx.compose.material.Icon
9+
import androidx.compose.material.IconButton
710
import androidx.compose.material.MaterialTheme
811
import androidx.compose.material.Text
9-
import androidx.compose.runtime.*
12+
import androidx.compose.material.TopAppBar
13+
import androidx.compose.material.icons.Icons
14+
import androidx.compose.material.icons.filled.ArrowBack
15+
import androidx.compose.runtime.Composable
16+
import androidx.compose.runtime.LaunchedEffect
17+
import androidx.compose.runtime.getValue
18+
import androidx.compose.runtime.mutableStateOf
19+
import androidx.compose.runtime.setValue
1020
import androidx.compose.ui.Alignment
1121
import androidx.compose.ui.Modifier
1222
import androidx.compose.ui.unit.dp
23+
import androidx.navigation.NavHostController
1324
import co.touchlab.kermit.Logger
1425
import com.kevinnzou.sample.eventbus.FlowEventBus
1526
import com.kevinnzou.sample.eventbus.NavigationEvent
@@ -34,7 +45,7 @@ import kotlinx.coroutines.flow.filter
3445
* for setup instructions first.
3546
*/
3647
@Composable
37-
internal fun BasicWebViewWithHTMLSample() {
48+
internal fun BasicWebViewWithHTMLSample(navHostController: NavHostController? = null) {
3849
val html = HtmlRes.html
3950
val webViewState =
4051
rememberWebViewStateWithHTMLFile(
@@ -49,34 +60,50 @@ internal fun BasicWebViewWithHTMLSample() {
4960
initJsBridge(jsBridge)
5061
}
5162
MaterialTheme {
52-
Box(Modifier.fillMaxSize()) {
53-
WebView(
54-
state = webViewState,
55-
modifier = Modifier.fillMaxSize(),
56-
captureBackPresses = false,
57-
navigator = webViewNavigator,
58-
webViewJsBridge = jsBridge,
59-
)
60-
Button(
61-
onClick = {
62-
webViewNavigator.evaluateJavaScript(
63-
"""
64-
document.getElementById("subtitle").innerText = "Hello from KMM!";
65-
window.kmpJsBridge.callNative("Greet",JSON.stringify({message: "Hello"}),
66-
function (data) {
67-
document.getElementById("subtitle").innerText = data;
68-
console.log("Greet from Native: " + data);
69-
}
70-
);
71-
callJS();
72-
""".trimIndent(),
73-
) {
74-
jsRes = it
63+
Column {
64+
TopAppBar(
65+
title = { Text(text = "WebView Sample") },
66+
navigationIcon = {
67+
IconButton(onClick = {
68+
navHostController?.popBackStack()
69+
}) {
70+
Icon(
71+
imageVector = Icons.Default.ArrowBack,
72+
contentDescription = "Back",
73+
)
7574
}
7675
},
77-
modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = 50.dp),
78-
) {
79-
Text(jsRes)
76+
)
77+
78+
Box(Modifier.fillMaxSize()) {
79+
WebView(
80+
state = webViewState,
81+
modifier = Modifier.fillMaxSize(),
82+
captureBackPresses = false,
83+
navigator = webViewNavigator,
84+
webViewJsBridge = jsBridge,
85+
)
86+
Button(
87+
onClick = {
88+
webViewNavigator.evaluateJavaScript(
89+
"""
90+
document.getElementById("subtitle").innerText = "Hello from KMM!";
91+
window.kmpJsBridge.callNative("Greet",JSON.stringify({message: "Hello"}),
92+
function (data) {
93+
document.getElementById("subtitle").innerText = data;
94+
console.log("Greet from Native: " + data);
95+
}
96+
);
97+
callJS();
98+
""".trimIndent(),
99+
) {
100+
jsRes = it
101+
}
102+
},
103+
modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = 50.dp),
104+
) {
105+
Text(jsRes)
106+
}
80107
}
81108
}
82109
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.kevinnzou.sample
2+
3+
import androidx.compose.foundation.layout.RowScope
4+
import androidx.compose.material.BottomNavigation
5+
import androidx.compose.material.BottomNavigationItem
6+
import androidx.compose.material.Icon
7+
import androidx.compose.material.IconButton
8+
import androidx.compose.material.Scaffold
9+
import androidx.compose.material.Text
10+
import androidx.compose.material.TopAppBar
11+
import androidx.compose.material.icons.Icons
12+
import androidx.compose.material.icons.filled.ArrowBack
13+
import androidx.compose.runtime.Composable
14+
import androidx.navigation.NavHostController
15+
import cafe.adriel.voyager.navigator.tab.CurrentTab
16+
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
17+
import cafe.adriel.voyager.navigator.tab.Tab
18+
import cafe.adriel.voyager.navigator.tab.TabNavigator
19+
import com.kevinnzou.sample.navigation.HomeTab
20+
import com.kevinnzou.sample.navigation.PersonalTab
21+
22+
/**
23+
* Created By Kevin Zou On 2023/12/8
24+
*/
25+
@Composable
26+
fun VoyagerNavigationSample(navHostController: NavHostController? = null) {
27+
TabNavigator(HomeTab) {
28+
Scaffold(
29+
topBar = {
30+
TopAppBar(
31+
title = { Text(text = "WebView Sample") },
32+
navigationIcon = {
33+
IconButton(onClick = {
34+
navHostController?.popBackStack()
35+
}) {
36+
Icon(
37+
imageVector = Icons.Default.ArrowBack,
38+
contentDescription = "Back",
39+
)
40+
}
41+
},
42+
)
43+
},
44+
content = {
45+
CurrentTab()
46+
},
47+
bottomBar = {
48+
BottomNavigation {
49+
TabNavigationItem(HomeTab)
50+
TabNavigationItem(PersonalTab)
51+
}
52+
},
53+
)
54+
}
55+
}
56+
57+
@Composable
58+
private fun RowScope.TabNavigationItem(tab: Tab) {
59+
val tabNavigator = LocalTabNavigator.current
60+
61+
BottomNavigationItem(
62+
selected = tabNavigator.current.key == tab.key,
63+
onClick = { tabNavigator.current = tab },
64+
icon = { Icon(painter = tab.options.icon!!, contentDescription = tab.options.title) },
65+
)
66+
}

0 commit comments

Comments
 (0)