Jetpack Compose में WebView का इस्तेमाल करने के लिए, आपको इसे AndroidView में रैप करना होगा.
इस गाइड में, इस्तेमाल के सामान्य उदाहरण और Compose में इन्हें इस्तेमाल करने का तरीका बताया गया है.
AndroidView की मदद से, WebView को रैप करना
Compose में WebView का इस्तेमाल करने के लिए, इसे AndroidView में रैप करें:
@Composable fun SimpleWebView( initialUrl: String, modifier: Modifier = Modifier ) { AndroidView( modifier = modifier.fillMaxSize(), factory = { context -> WebView(context).apply { webViewClient = WebViewClient() settings.javaScriptEnabled = true loadUrl(initialUrl) } } ) }
इससे आपके ऐप्लिकेशन में, सामान्य यूआरएल दिखाया जा सकता है. हालांकि, WebView में स्टेट लाइफ़साइकल से जुड़ी जटिल समस्याएं हो सकती हैं. ये समस्याएं, Android View लाइफ़साइकल और Compose लाइफ़साइकल से अलग होती हैं. Compose को इंटिग्रेट करने से, WebView के जटिल उदाहरण सामने आ सकते हैं. इससे मुश्किल बग आ सकते हैं. यहां इस्तेमाल के कुछ उदाहरण दिए गए हैं. इन उदाहरणों में, सुविधाओं को इस्तेमाल करने के लिए, खास तरीके अपनाने पड़ सकते हैं.
WebView की स्थिति को बनाए रखना
Compose में कॉन्फ़िगरेशन में हुए बदलावों और नेविगेशन को मैनेज करना मुश्किल है. इसकी वजह यह है कि
WebView पुराने वर्शन का View है. यह अपने होस्ट Activity से जुड़ा होता है. इसलिए, हमारा सुझाव है कि इसका इंस्टेंस, Activity लाइफ़साइकल से ज़्यादा समय तक न चले.
इसलिए, WebView की स्थिति को बनाए रखने का सामान्य तरीका यह है कि WebView के इंस्टेंस को, Activity के साथ डिस्ट्रॉय और फिर से बनाया जाए. Bundle का इस्तेमाल करके, इसके इंटरनल नेविगेशन इतिहास और स्क्रोल की स्थिति को मैन्युअल तरीके से बनाए रखा जा सकता है.
@Composable fun PersistentWebView(url: String) { val webViewStateBundle = rememberSaveable { Bundle() } AndroidView( factory = { context -> WebView(context).apply { webViewClient = WebViewClient() settings.javaScriptEnabled = true // Restore the state and history if (webViewStateBundle.containsKey("WEBVIEW_STATE")) { restoreState(webViewStateBundle.getBundle("WEBVIEW_STATE")!!) } else { loadUrl(url) } } }, onRelease = { releasedWebView -> // Save navigation history before the instance is destroyed val bundle = Bundle() releasedWebView.saveState(bundle) webViewStateBundle.putBundle("WEBVIEW_STATE", bundle) }, modifier = Modifier.fillMaxSize() ) }
वापस जाने की प्रोसेस को मैनेज करना
जब WebView में नेविगेशन इतिहास मौजूद होता है, तो सिस्टम के बैक जेस्चर से, स्क्रीन से बाहर निकलने के बजाय, WebView में पीछे की ओर नेविगेट करना चाहिए.
सिस्टम के बैक इवेंट को इंटरसेप्ट करने के लिए, Compose BackHandler एपीआई का इस्तेमाल करें. साथ ही,
WebView goBack() फ़ंक्शन को कॉल करें:
// ... @Composable fun BackNavigationDemoScreen(onBack: () -> Unit) { // Hold a reference to the WebView to check its history state var webViewReference by remember { mutableStateOf<WebView?>(null) } // Intercept the system back press if the WebView has history BackHandler(enabled = true) { val webView = webViewReference if (webView != null && webView.canGoBack()) { webView.goBack() // Go back in history } else { onBack() // Exit screen } } Scaffold( topBar = { TopAppBar( title = { Text("Back Navigation Demo") }, navigationIcon = { IconButton(onClick = onBack) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") } } ) } ) { padding -> Column(modifier = Modifier.fillMaxSize().padding(padding)) { AndroidView( modifier = Modifier.fillMaxSize(), factory = { context -> WebView(context).apply { settings.javaScriptEnabled = true // Keeps link navigations internal to the WebView instead of opening Chrome webViewClient = WebViewClient() loadUrl("https://developer.android.com") webViewReference = this } }, onRelease = { webViewReference = null } ) } } }
इस तरीके को अपनाने से, ब्राउज़र की तरह नेविगेशन का अनुभव मिलता है.
नेस्ट की गई स्क्रोलिंग
Compose में WebView का इस्तेमाल करने पर, नेस्ट की गई स्क्रोलिंग को आसानी से इस्तेमाल नहीं किया जा सकता. WebView को स्क्रोल किए जा सकने वाले Compose कंटेनर में रखने पर, जैसे कि LazyColumn, WebView सभी स्क्रोल जेस्चर का इस्तेमाल कर सकता है.
WebView अपने इंटरनल रेंडरिंग इंजन पर काम करता है. इसलिए, फ़िलहाल इसे LazyColumn के साथ नेस्ट करने पर, यह ठीक से काम नहीं करता.
WebView के लिए, नेस्ट की गई स्क्रोलिंग की आधिकारिक सहायता की प्रोग्रेस ट्रैक करने के लिए, यह समस्या देखें.
एज-टू-एज लेआउट और विंडो इनसेट
एज-टू-एज लेआउट का इस्तेमाल करने पर, WebView का कॉन्टेंट, स्टेटस बार जैसे सिस्टम बार के नीचे दिख सकता है. windowInsetsPadding मॉडिफ़ायर का इस्तेमाल करके, पूरे WebView को सुरक्षित इलाके में पुश किया जा सकता है:
@Composable fun EdgeToEdgeDemo(url: String) { AndroidView( modifier = Modifier .fillMaxSize() .windowInsetsPadding(WindowInsets.systemBars), factory = { context -> WebView(context).apply { loadUrl(url) } } ) }
इनसेट के बारे में ज़्यादा जानकारी के लिए, WebView में विंडो इनसेट को समझना लेख पढ़ें.
WebView के कॉन्टेंट के साथ, ऐप्लिकेशन की थीम को सिंक करना
जब ऐप्लिकेशन, लाइट और डार्क मोड के बीच स्विच करता है, तो WebView का कॉन्टेंट, पेज को फिर से लोड किए बिना अपने-आप अपडेट हो सकता है. हालांकि, इसके लिए इसे सही तरीके से मैनेज करना ज़रूरी है.
अगर आपके पास वेब पेज का कॉन्टेंट है, तो ऐप्लिकेशन की थीम के साथ रंगों को सिंक करने के लिए, मीडिया क्वेरी prefers-color-scheme को मैनेज करें. इससे यह पक्का किया जा सकेगा कि आपका वेब पेज, चुनी गई थीम के हिसाब से दिखे.
नेटिव एलिमेंट, जैसे कि ड्रॉपडाउन और पॉप-अप को अपने ऐप्लिकेशन की थीम का पता लगाने और उससे मैच करने के लिए, अपने Activity. पर DayNight स्टाइल थीम लागू करें.
<resources> <!-- ... <!-- Use a DayNight theme in your manifest to handle both modes automatically --> <style name="Theme.Webviewdemo.DayNight" parent="Theme.AppCompat.DayNight.NoActionBar" /> </resources>
@Composable fun ThemeSyncDemo(onBack: () -> Unit) { val context = LocalContext.current AndroidView( modifier = Modifier.fillMaxSize(), factory = { _ -> WebView(context).apply { settings.javaScriptEnabled = true webViewClient = WebViewClient() val html = """ <html> <head> // ... @media (prefers-color-scheme: dark) { body { background-color: #212121; color: #ffffff; } select { border-color: #BB86FC; background: #212121; color: #ffffff; } } </style> </head> // ... </html> """.trimIndent() loadDataWithBaseURL(null, html, "text/html", "UTF-8", null) } } ) }
अगर वेब पेज में डार्क थीम नहीं है या आपके पास वेब कॉन्टेंट नहीं है, एल्गोरिदम के हिसाब से डार्कनिंग की सुविधा का इस्तेमाल करके, डार्क थीम को लागू किया जा सकता है. आधुनिक वेबसाइटों में पहले से ही डार्क मोड होता है. इसलिए, वे इस एल्गोरिदम को अनदेखा करती हैं और इसके बजाय, अपनी बिल्ट-इन स्टाइल का इस्तेमाल करती हैं.
Compose में वेब की अनुमतियों को मैनेज करना
जब कोई वेब पेज, हार्डवेयर या डेटा का ऐक्सेस करने का अनुरोध करता है (उदाहरण के लिए, कैमरा, माइक्रोफ़ोन या जगह की जानकारी), तो WebView, अपने WebChromeClient में खास कॉलबैक ट्रिगर करता है. आपको इन कॉलबैक को मैनेज करना होगा. साथ ही, यह पक्का करना होगा कि Android के रनटाइम की ज़रूरी अनुमतियां दी गई हों.
कैमरा और माइक्रोफ़ोन की अनुमतियों को मैनेज करना
जब कोई वेब पेज, कैमरा या माइक्रोफ़ोन ऐक्सेस करने का अनुरोध करता है (उदाहरण के लिए, WebRTC या वीडियो रिकॉर्डिंग), तो WebView, WebChromeClient.onPermissionRequest को कॉल करता है.
हालांकि, grant() को कॉल करने से पहले, आपको Android के रनटाइम की ये अनुमतियां मांगनी होंगी:
Manifest.permission.CAMERAManifest.permission.RECORD_AUDIO
सबसे पहले, WebView के लिए अनुमति हैंडलर तय करें. यह हैंडलर, WebView से अनुरोध किए गए PermissionRequest को ट्रैक करता है:
class WebViewPermissionHandler( private val launcher: ManagedActivityResultLauncher<Array<String>, Map<String, Boolean>> ) { var pendingRequest by mutableStateOf<PermissionRequest?>(null) private set fun handleRequest(request: PermissionRequest) { val isTrustedOrigin = request.origin.host == "www.trusted-domain.com" || request.origin.host == "app.local" // Always verify the origin before granting request if (!isTrustedOrigin) { Log.w("WebViewPermission", "Blocked and denied permission request from untrusted origin: ${request.origin.host}") request.deny() return } val androidPermissions = mutableListOf<String>() request.resources.forEach { resource -> when (resource) { PermissionRequest.RESOURCE_VIDEO_CAPTURE -> androidPermissions.add(Manifest.permission.CAMERA) PermissionRequest.RESOURCE_AUDIO_CAPTURE -> androidPermissions.add(Manifest.permission.RECORD_AUDIO) } } // Save the request and launch the Android system dialog pendingRequest = request launcher.launch(androidPermissions.toTypedArray()) } fun onResult(results: Map<String, Boolean>) { val allGranted = results.values.all { it } Log.d("WebViewPermission", "Kotlin: All permissions granted? $allGranted") if (allGranted) { pendingRequest?.grant(arrayOf("/* list of permissions */")) } else { pendingRequest?.deny() } pendingRequest = null } }
इसके बाद, एक कंपोज़ेबल बनाएं, जो WebViewPermissionHandler को याद रखता है. अनुमतियों का अनुरोध करने के लिए, rememberLauncherForActivityResult का इस्तेमाल करें:
@Composable fun rememberWebViewPermissionHandler(): WebViewPermissionHandler { val handlerState = remember { mutableStateOf<WebViewPermissionHandler?>(null) } val launcher = rememberLauncherForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { results -> handlerState.value?.onResult(results) } return remember { WebViewPermissionHandler(launcher).also { handlerState.value = it } } }
onPermissionRequest कॉलबैक से अनुमति को मैनेज करें. इससे अनुमति लॉन्चर लॉन्च होता है:
@Composable fun WebViewPermissionScreen() { val permissionHandler = rememberWebViewPermissionHandler() AndroidView( factory = { context -> WebView(context).apply { settings.javaScriptEnabled = true webChromeClient = object : WebChromeClient() { override fun onPermissionRequest(request: PermissionRequest) { // Simply delegate to the handler permissionHandler.handleRequest(request) } } // load a web page that needs permissions } }, modifier = Modifier.fillMaxSize() ) }
एम्बेड किए गए WebView का विकल्प
अगर आपको WebView को एम्बेड नहीं करना है, तो Android, वेब कॉन्टेंट दिखाने के लिए
अन्य विकल्प उपलब्ध कराता है. जैसे, Chrome के कस्टम टैब. अपने इस्तेमाल के उदाहरणों (जैसे, ब्राउज़ करना या पुष्टि करना) के लिए सही तरीका चुनने का तरीका जानने के लिए, अपने Android ऐप्लिकेशन में वेब कॉन्टेंट
का इस्तेमाल करना लेख पढ़ें.
अन्य संसाधन
- WebView की मेमोरी को मैनेज करना और उसकी समस्याओं का पता लगाना
- WebView की स्थिति को बेहतर तरीके से मैनेज करना
- वेब ऐप्लिकेशन को डीबग करना