Native Android apps don't have a DOM to embed a <script> or <iframe> into, so a form is embedded with a WebView that loads the form's URL, or a small HTML wrapper that carries the script embed.
class CakeOrderFormActivity : AppCompatActivity() {
private lateinit var webView: WebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
webView = WebView(this)
setContentView(webView)
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
webView.webChromeClient = WebChromeClient() // required for camera / file-upload fields
webView.webViewClient = WebViewClient()
webView.loadUrl("https://eu.forms.app/form/69d4bd130b443bda40c8f65a")
}
}javaScriptEnabledanddomStorageEnabledare both required; forms.app forms use JavaScript and local storage.WebChromeClientis needed for file-upload and camera-based fields to open a native picker inside theWebView.- Request the runtime permissions your form's field types need (
CAMERA,RECORD_AUDIO,ACCESS_FINE_LOCATION), matching theallowattribute used on the web iframe embed, and grantWebViewaccess to them inonPermissionRequeston yourWebChromeClient.
Only declare the ones your form's field types actually need:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />The form's src URL doesn't accept prefill query parameters directly; prefilling goes through the answers option of the script embed (see Passing data to your form). To use it from a native app, load a small HTML wrapper containing the script embed instead of loading the form URL directly:
@Composable
fun CakeOrderFormScreen(referralCode: String) {
val safeReferral = remember(referralCode) {
JSONObject.quote(referralCode) // escape for safe interpolation into the HTML string below
}
AndroidView(factory = { context ->
WebView(context).apply {
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
webChromeClient = WebChromeClient()
val html = """
<html>
<body style="margin:0;">
<div data-formsapp-src="https://eu.forms.app/form/69d4bd130b443bda40c8f65a"></div>
<script src="https://cdn.formsapp.io/embed.js" async defer onload="
new formsapp('69d4bd130b443bda40c8f65a', 'standard',
{'width':'100vw','height':'formHeight','answers':{'63ebad419442ad0448b9e9b6':$safeReferral}},
'https://eu.forms.app');
"></script>
</body>
</html>
""".trimIndent()
loadDataWithBaseURL("https://eu.forms.app", html, "text/html", "UTF-8", null)
}
})
}referralCode is being written directly into an HTML string that a WebView will execute as a page. Always escape or JSON-encode any value that comes from outside your app (a deep link, a previous screen, and so on) before interpolating it, to avoid injecting unintended HTML or script into the page.
loadDataWithBaseURL's baseUrl argument is set to https://eu.forms.app, your account's data-region domain, so the loaded page resolves the embed script correctly. Match it to the domain shown on your form's Share page.
- Embed options for every layout's settings.
- Passing data to your form for the
answersoption in full.