Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
788583c
[webview_flutter_android] Adds support for Web Authentication
creatorpiyush May 10, 2026
79f5f50
Formatting fix
creatorpiyush May 10, 2026
9bed47f
code review fixes
creatorpiyush May 10, 2026
5167264
code refactoring
creatorpiyush May 10, 2026
c6e72c9
version change and changelog update
creatorpiyush May 11, 2026
e7a1290
typo fix
creatorpiyush May 11, 2026
2fcb70a
review comment updates
creatorpiyush May 20, 2026
884b573
Merge branch 'main' into webview_flutter_android_set-Web-Authenticati…
creatorpiyush May 21, 2026
545ff3e
Merge branch 'main' into webview_flutter_android_set-Web-Authenticati…
creatorpiyush May 25, 2026
1eb4b80
Merge branch 'main' of github.com:flutter/packages into webview_flutt…
bparrishMines May 26, 2026
5119751
fix version bump
bparrishMines May 26, 2026
68cb49b
Merge branch 'main' into webview_flutter_android_set-Web-Authenticati…
creatorpiyush-nagarro Jun 10, 2026
16b0e23
Updated the version and changelog
creatorpiyush-nagarro Jun 10, 2026
5756505
fix
creatorpiyush-nagarro Jun 10, 2026
9409864
updated changelog
creatorpiyush-nagarro Jun 10, 2026
63b6294
Update CHANGELOG for version 4.14.0
creatorpiyush Jun 10, 2026
719a165
Merge branch 'main' into webview_flutter_android_set-Web-Authenticati…
creatorpiyush Jul 4, 2026
4cf3289
fix the issue
creatorpiyush-nagarro Jul 9, 2026
9023562
Merge branch 'main' into webview_flutter_android_set-Web-Authenticati…
creatorpiyush Jul 9, 2026
1d47090
Update packages/webview_flutter/webview_flutter_android/android/src/m…
creatorpiyush Jul 26, 2026
049b6a9
Merge branch 'main' into webview_flutter_android_set-Web-Authenticati…
creatorpiyush Jul 26, 2026
7077c5e
fix: CI fixes
creatorpiyush Jul 26, 2026
28ec527
Merge branch 'main' into webview_flutter_android_set-Web-Authenticati…
creatorpiyush Jul 26, 2026
80cffc0
Merge branch 'main' into webview_flutter_android_set-Web-Authenticati…
gmackall Aug 11, 2026
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
3 changes: 3 additions & 0 deletions packages/webview_flutter/webview_flutter_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 4.14.0
* Adds support for configuring Web Authentication in `AndroidWebViewController` with `setWebAuthenticationSupport` to enable Passkey and other related Authentication.

## 4.13.0

* Adds new method for accessing a native `WebView` from a `FlutterPluginBinding`.
Expand Down
28 changes: 28 additions & 0 deletions packages/webview_flutter/webview_flutter_android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,34 @@ Add intent filters to your AndroidManifest.xml to discover and invoke Android pa
</queries>
```

## Enable Web Authentication in WebView

WebAuthentication (WebAuthn) can be configured by calling
`AndroidWebViewController.setWebAuthenticationSupport` after checking
`AndroidWebViewController.isWebViewFeatureSupported`.

The WebAuthentication support level can be set to one of three values:
- **[WebAuthenticationSupport.none]**: Disables all WebAuthn requests
- **[WebAuthenticationSupport.forApp]**: Allows WebAuthn for the embedded application (default)
- **[WebAuthenticationSupport.forBrowser]**: Allows WebAuthn for any website (browser-like behavior)

<?code-excerpt "example/lib/readme_excerpts.dart (web_authentication_example)"?>
```dart
final bool webAuthenticationSupported = await androidController
.isWebViewFeatureSupported(WebViewFeatureType.webAuthentication);

if (webAuthenticationSupported) {
// Enable WebAuthn for the embedded app
await androidController.setWebAuthenticationSupport(
WebAuthenticationSupport.forApp,
);
// Or for browser-like behavior supporting any website:
// await androidController.setWebAuthenticationSupport(
// WebAuthenticationSupport.forBrowser,
// );
}
```

## Fullscreen Video

To display a video as fullscreen, an app must manually handle the notification that the current page
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6826,6 +6826,8 @@ abstract class PigeonApiWebSettingsCompat(
) {
abstract fun setPaymentRequestEnabled(webSettings: android.webkit.WebSettings, enabled: Boolean)

abstract fun setWebAuthenticationSupport(webSettings: android.webkit.WebSettings, support: Long)

companion object {
@Suppress("LocalVariableName")
fun setUpMessageHandlers(binaryMessenger: BinaryMessenger, api: PigeonApiWebSettingsCompat?) {
Expand Down Expand Up @@ -6854,6 +6856,30 @@ abstract class PigeonApiWebSettingsCompat(
channel.setMessageHandler(null)
}
}
run {
val channel =
BasicMessageChannel<Any?>(
binaryMessenger,
"dev.flutter.pigeon.webview_flutter_android.WebSettingsCompat.setWebAuthenticationSupport",
codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val webSettingsArg = args[0] as android.webkit.WebSettings
val supportArg = args[1] as Long
val wrapped: List<Any?> =
try {
api.setWebAuthenticationSupport(webSettingsArg, supportArg)
listOf(null)
} catch (exception: Throwable) {
AndroidWebkitLibraryPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,28 @@ public WebSettingsCompatProxyApi(@NonNull ProxyApiRegistrar pigeonRegistrar) {
public void setPaymentRequestEnabled(@NonNull WebSettings webSettings, boolean enabled) {
WebSettingsCompat.setPaymentRequestEnabled(webSettings, enabled);
}

/**
* This method should only be called if {@link WebViewFeatureProxyApi#isFeatureSupported(String)}
* with WEB_AUTHENTICATION returns true.
*
* <p>The {@code support} parameter is a {@code long} to accommodate Dart's integer type, but is
* safely converted to {@code int} for the underlying Android API call. {@link
* Math#toIntExact(long)} is used to verify the value fits in the {@code int} range and throw
* {@link ArithmeticException} if it overflows. This is safe because the valid support levels are
* constants (0, 1, 2) that well within the integer range.
Comment thread
creatorpiyush marked this conversation as resolved.
Outdated
*
* <p>Note: {@link Math#toIntExact(long)} requires API level 24 or higher. This is compatible with
* this plugin's minimum SDK version.
*
* @param webSettings the WebSettings instance
* @param support the WebAuthentication support level (0, 1, or 2)
* @throws ArithmeticException if {@code support} exceeds {@link Integer#MAX_VALUE}
*/
@SuppressLint("RequiresFeature")
@Override
public void setWebAuthenticationSupport(@NonNull WebSettings webSettings, long support) {
final int supportValue = Math.toIntExact(support);
Comment thread
creatorpiyush marked this conversation as resolved.
WebSettingsCompat.setWebAuthenticationSupport(webSettings, supportValue);
}
Comment thread
creatorpiyush marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package io.flutter.plugins.webviewflutter;

import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
Expand Down Expand Up @@ -36,4 +37,38 @@ public void setPaymentRequestEnabled() {
fail(e.toString());
}
}

@Test
public void setWebAuthenticationSupport() {
final PigeonApiWebSettingsCompat api =
new TestProxyApiRegistrar().getPigeonApiWebSettingsCompat();

final WebSettings webSettings = mock(WebSettings.class);

try (MockedStatic<WebSettingsCompat> mockedStatic = mockStatic(WebSettingsCompat.class);
MockedStatic<WebViewFeature> mockedWebViewFeature = mockStatic(WebViewFeature.class)) {
mockedWebViewFeature
.when(() -> WebViewFeature.isFeatureSupported(WebViewFeature.WEB_AUTHENTICATION))
.thenReturn(true);
api.setWebAuthenticationSupport(webSettings, 2L);
mockedStatic.verify(() -> WebSettingsCompat.setWebAuthenticationSupport(webSettings, 2));
}
}

@Test
public void setWebAuthenticationSupportWithLongOutsideIntRangeThrows() {
final PigeonApiWebSettingsCompat api =
new TestProxyApiRegistrar().getPigeonApiWebSettingsCompat();

final WebSettings webSettings = mock(WebSettings.class);

try (MockedStatic<WebViewFeature> mockedWebViewFeature = mockStatic(WebViewFeature.class)) {
mockedWebViewFeature
.when(() -> WebViewFeature.isFeatureSupported(WebViewFeature.WEB_AUTHENTICATION))
.thenReturn(true);
assertThrows(
ArithmeticException.class,
() -> api.setWebAuthenticationSupport(webSettings, Integer.MAX_VALUE + 1L));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,26 +101,28 @@ Future<void> main() async {
);
});

testWidgets('withWeakRefenceTo allows encapsulating class to be garbage collected', (
WidgetTester tester,
) async {
final gcCompleter = Completer<int>();
final instanceManager = android.PigeonInstanceManager(
onWeakReferenceRemoved: gcCompleter.complete,
);
testWidgets(
'withWeakRefenceTo allows encapsulating class to be garbage collected',
(WidgetTester tester) async {
final gcCompleter = Completer<int>();
final instanceManager = android.PigeonInstanceManager(
onWeakReferenceRemoved: gcCompleter.complete,
);

ClassWithCallbackClass? instance = ClassWithCallbackClass();
instanceManager.addHostCreatedInstance(instance.callbackClass, 0);
instance = null;
ClassWithCallbackClass? instance = ClassWithCallbackClass();
instanceManager.addHostCreatedInstance(instance.callbackClass, 0);
instance = null;

// Force garbage collection.
await IntegrationTestWidgetsFlutterBinding.instance.watchPerformance(() async {
await tester.pumpAndSettle();
});
// Force garbage collection.
await IntegrationTestWidgetsFlutterBinding.instance.watchPerformance(() async {
await tester.pumpAndSettle();
});

final int gcIdentifier = await gcCompleter.future;
expect(gcIdentifier, 0);
}, timeout: const Timeout(Duration(seconds: 10)));
final int gcIdentifier = await gcCompleter.future;
expect(gcIdentifier, 0);
},
timeout: const Timeout(Duration(seconds: 10)),
);

// TODO(bparrishMines): This test is skipped because of
// https://github.com/flutter/flutter/issues/123327
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ enum MenuOptions {
videoExample,
logExample,
basicAuthentication,
webAuthentication,
javaScriptAlert,
viewportMeta,
}
Expand Down Expand Up @@ -383,6 +384,8 @@ class SampleMenu extends StatelessWidget {
_onLogExample();
case MenuOptions.basicAuthentication:
_promptForUrl(context);
case MenuOptions.webAuthentication:
_onWebAuthenticationExample(context);
case MenuOptions.javaScriptAlert:
_onJavaScriptAlertExample(context);
case MenuOptions.viewportMeta:
Expand Down Expand Up @@ -443,6 +446,10 @@ class SampleMenu extends StatelessWidget {
value: MenuOptions.basicAuthentication,
child: Text('Basic Authentication Example'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.webAuthentication,
child: Text('Web Authentication Example'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.javaScriptAlert,
child: Text('JavaScript Alert Example'),
Expand Down Expand Up @@ -564,6 +571,30 @@ class SampleMenu extends StatelessWidget {
);
}

Future<void> _onWebAuthenticationExample(BuildContext context) async {
final androidController = webViewController as AndroidWebViewController;
final bool supported = await androidController.isWebViewFeatureSupported(
WebViewFeatureType.webAuthentication,
);

if (!supported) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Web Authentication is not supported on this device.')),
);
}
return;
}

await androidController.setWebAuthenticationSupport(WebAuthenticationSupport.forApp);

if (context.mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('Web Authentication enabled.')));
}
}

Future<void> _onDoPostRequest() {
return webViewController.loadRequest(
LoadRequestParams(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,26 @@ Future<void> enablePaymentRequest() async {
// #enddocregion payment_request_example
}

/// Example function for README demonstration of Web Authentication support.
Future<void> enableWebAuthentication() async {
final controller = PlatformWebViewController(AndroidWebViewControllerCreationParams());
final androidController = controller as AndroidWebViewController;
// #docregion web_authentication_example
final bool webAuthenticationSupported = await androidController.isWebViewFeatureSupported(
WebViewFeatureType.webAuthentication,
);

if (webAuthenticationSupported) {
// Enable WebAuthn for the embedded app
await androidController.setWebAuthenticationSupport(WebAuthenticationSupport.forApp);
// Or for browser-like behavior supporting any website:
// await androidController.setWebAuthenticationSupport(
// WebAuthenticationSupport.forBrowser,
// );
}
// #enddocregion web_authentication_example
}

/// Example function for README demonstration of geolocation permissions for
/// a use case where the content is always trusted (for example, it only shows
/// content from a domain controlled by the app developer) and geolocation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@ class PigeonOverrides {
/// Overrides [WebSettingsCompat.setPaymentRequestEnabled].
static Future<void> Function(WebSettings, bool)? webSettingsCompat_setPaymentRequestEnabled;

/// Overrides [WebSettingsCompat.setWebAuthenticationSupport].
static Future<void> Function(WebSettings, int)? webSettingsCompat_setWebAuthenticationSupport;

/// Overrides [WebViewFeature.isFeatureSupported].
static Future<bool> Function(String)? webViewFeature_isFeatureSupported;

Expand All @@ -232,6 +235,7 @@ class PigeonOverrides {
flutterAssetManager_instance = null;
webStorage_instance = null;
webSettingsCompat_setPaymentRequestEnabled = null;
webSettingsCompat_setWebAuthenticationSupport = null;
webViewFeature_isFeatureSupported = null;
}
}
Expand Down Expand Up @@ -7188,6 +7192,35 @@ class WebSettingsCompat extends PigeonInternalProxyApiBaseClass {
_extractReplyValueOrThrow(pigeonVar_replyList, pigeonVar_channelName, isNullValid: true);
}

static Future<void> setWebAuthenticationSupport(
WebSettings webSettings,
int support, {
BinaryMessenger? pigeon_binaryMessenger,
PigeonInstanceManager? pigeon_instanceManager,
}) async {
if (PigeonOverrides.webSettingsCompat_setWebAuthenticationSupport != null) {
return PigeonOverrides.webSettingsCompat_setWebAuthenticationSupport!(webSettings, support);
}
final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = _PigeonInternalProxyApiBaseCodec(
pigeon_instanceManager ?? PigeonInstanceManager.instance,
);
final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger;
const pigeonVar_channelName =
'dev.flutter.pigeon.webview_flutter_android.WebSettingsCompat.setWebAuthenticationSupport';
final pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[
webSettings,
support,
]);
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;

_extractReplyValueOrThrow(pigeonVar_replyList, pigeonVar_channelName, isNullValid: true);
}

@override
WebSettingsCompat pigeon_copy() {
return WebSettingsCompat.pigeon_detached(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,9 @@ class WebViewFeatureConstants {
///
/// See https://developer.android.com/reference/androidx/webkit/WebViewFeature#PAYMENT_REQUEST.
static const String paymentRequest = 'PAYMENT_REQUEST';

/// This feature covers [WebSettingsCompat.setWebAuthenticationSupport].
///
/// See https://developer.android.com/reference/androidx/webkit/WebViewFeature#WEB_AUTHENTICATION.
static const String webAuthentication = 'WEB_AUTHENTICATION';
}
Loading
Loading