Skip to content

[webview_flutter_wkwebview] Extended Web View API on iOS to add flexibility when working with local HTML content #8787

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

agavrilko
Copy link

Overview

The readAccessURLProvider property has been added to the WebKitWebViewControllerCreationParams class. This function allows customization of the path to associated resources when loading a local HTML page using loadFile on iOS.

What Is the Issue

[#136479] readAccessUrl of the webview_flutter_wkwebview should be accessible for customization

The native WKWebView takes two arguments when loading local HTML pages:

  1. The file URL – The local HTML file to be loaded.
  2. The read access URL – A file or directory that WKWebView can access (e.g., .css, .js files).

Currently, the Flutter implementation always sets the parent folder of the specified HTML file as the read access directory. This behavior is not configurable, which limits how local web content can be structured.

For example, the following structure does not work because the .css and .js files are not in the parent directory of the HTML file:

/app_resources/
│── styles/
│    ├── main.css
│
│── scripts/
│    ├── app.js
│
│── pages/
     ├── index.html  (Loaded file)

With the existing behavior, WKWebView cannot access styles/main.css and scripts/app.js because the parent folder of index.html (/pages/) does not contain them.

How This Resolves the Issue

The readAccessURLProvider property has been added to the WebKitWebViewControllerCreationParams class. This property accepts a function that takes the path of the HTML file being loaded and returns the path to the associated resources.

Screenshot at Mar 04 14-49-46

Each time loadFile is called, the controller invokes this function and passes its result as the second argument to the underlying WKWebView implementation.

By default, the function returns the parent directory of the specified HTML file, preserving the existing behavior when no custom provider is set.

Impact on End Users

  1. Developers can now explicitly specify the directory that WKWebView should allow access to when loading a local HTML page, providing greater flexibility in organizing assets.
  2. The existing API signature remains unchanged, ensuring that the update does not require any modifications to existing codebases unless the new functionality is utilized.

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@agavrilko agavrilko requested a review from cbracken as a code owner March 4, 2025 23:00
Copy link

google-cla bot commented Mar 4, 2025

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Copy link
Contributor

@bparrishMines bparrishMines left a comment

Choose a reason for hiding this comment

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

@agavrilko To follow the pattern of other methods, we should introduce a new platform interface method to webview_flutter_platform_interface. Something like PlatformWebViewController.loadFileWithParams(LoadFileParams). LoadFileParams would contain a url parameter. Then the webview_flutter_wkwebview package can create its own implementation of LoadFileParams with the readAccessUrl parameter because it is unique to iOS/macOS.

@agavrilko
Copy link
Author

Hello @bparrishMines,
Thank you for taking the time to review this MR!
I would like to ensure that I understand your feedback before proceeding.

I initially aimed to introduce this minor update without making any API changes, focusing solely on exposing the functionality that was already present but not accessible to users.
However, based on your comment, it seems you are suggesting a broader update that would introduce new APIs (e.g., LoadFileParams and a new/updated platform interface method).

Based on my understanding, your suggestion would require:

  1. Introducing LoadFileParams at the platform interface level.
  2. Extending the PlatformWebViewController to accept LoadFileParams instead of a plain string.
  3. Adapting webview_flutter_wkwebview with a WebKitLoadFileParams subclass.

webview_design_update

While this design would make the API more consistent and extensible, it would also introduce a breaking change for current users, requiring them to update their code.
That is why I am in doubt.

And if my understanding is correct, should I just update an existing loadFile method or introduce a new one?

@bparrishMines
Copy link
Contributor

@agavrilko I think you should create a new platform interface method named something like: PlatformWebViewController.loadFileWithParams(LoadFileParams). This is consistent with updates to other platform interfaces where we realized in hindsight the method should have taken a class so that we can add parameters later.

Everything else you stated seems correct.

Additionally, after this feature lands, PlatformWebViewController.loadFile can be deprecated and it can just call loadFileWithParams.

@agavrilko agavrilko force-pushed the feature/#136479-readAccessUrl-customization-on-iOS branch from bc4128f to afad597 Compare May 1, 2025 23:55
@agavrilko agavrilko requested a review from LouiseHsu as a code owner May 1, 2025 23:55
@agavrilko agavrilko force-pushed the feature/#136479-readAccessUrl-customization-on-iOS branch from afad597 to e3639a2 Compare May 5, 2025 18:12
@agavrilko
Copy link
Author

Hello @bparrishMines

I'm seeing that the pipeline fails due to a version resolution issue:

Because webview_flutter_example depends on webview_flutter_platform_interface ^2.12.0 which doesn't match any versions, version solving failed.

Could you please advise on the proper workflow for updating versions across the packages?
Also, when you have a moment, could you review the changes to ensure everything aligns with your expectations?

Thank you!

@bparrishMines
Copy link
Contributor

@agavrilko agavrilko force-pushed the feature/#136479-readAccessUrl-customization-on-iOS branch 2 times, most recently from d08642a to bffcb4e Compare May 21, 2025 22:22
@agavrilko agavrilko force-pushed the feature/#136479-readAccessUrl-customization-on-iOS branch from bffcb4e to f3090f0 Compare May 29, 2025 22:35
@agavrilko
Copy link
Author

Hello @bparrishMines,

I need further assistance with this PR.
I expected to see ...you can't publish a package that has dependency overrides..., as stated here.

But it looks like changing _platform_interface and implementations in the same PR is not allowed:

Changed packages: webview_flutter/webview_flutter,webview_flutter_android,webview_flutter_platform_interface,webview_flutter_wkwebview
[0:00] Running for packages/webview_flutter/webview_flutter...
Dart changes are not allowed to other packages in webview_flutter in the same PR as changes to public Dart code in webview_flutter_platform_interface, as this can cause accidental breaking changes to be missed by automated checks. Please split the changes to these two packages into separate PRs.

Should this change be split into 2 different PRs?
If yes, please specify what parts should go to each.

Copy link
Contributor

@bparrishMines bparrishMines left a comment

Choose a reason for hiding this comment

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

@agavrilko Yes, this PR won't be published as is. The first step is to get this PR approved first, then individual PRs for each package will be created. This allows us to review all the changes together before we start landing seaprate PRs.

/// such as headers (on Android) or resource paths (on iOS/macOS).
///
/// Throws a `PlatformException` if the [absoluteFilePath] does not exist.
Future<void> loadFileWithParams(LoadFileParams params) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This method shouldn't be necessary. A user can now use the platform specific loadFileWithParams to add platform specific parameters.

Copy link
Author

Choose a reason for hiding this comment

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

I am not sure I am property understand this comment.
Do you suggest to remove this method from the WebViewController class?
The loading of the file with custom parameters would look like the following:

switch (controller.platform) {
case final WebKitWebViewController platform:
platform.loadFileWithParams(...)

default:
...
}

Just want to ensure I understand it right.

Copy link
Contributor

Choose a reason for hiding this comment

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

Correct. WebViewController.loadFileWithParams should be removed and users could use the code snippet you provided to access the platform-specific params. The goal of the app-facing package is to provide a simple and clean API while the platform interface provides a more robust and extendable API.

Copy link
Author

Choose a reason for hiding this comment

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

The method was removed.
All changes to the WebViewController were reverted.

@@ -131,10 +131,25 @@ class WebViewController {
/// `/Users/username/Documents/www/index.html`.
///
/// Throws a `PlatformException` if the [absoluteFilePath] does not exist.
@Deprecated('Use loadFileWithParams(LocalFileParams params) instead. '
Copy link
Contributor

Choose a reason for hiding this comment

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

This shouldn't need deprecation. This can just change the platform call from platform.loadFile to platform.loadFileWithParams.

Copy link
Author

Choose a reason for hiding this comment

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

Updated.

/// Platform-specific implementations can add additional fields by extending
/// this class.
///
/// {@tool sample}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// {@tool sample}

This doesn't actually do anything right now. We just haven't removed them from other classes yet.

Copy link
Author

Choose a reason for hiding this comment

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

Removed.

/// final String readAccessPath;
/// }
/// ```
/// {@end-tool}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// {@end-tool}

Same here

Copy link
Author

Choose a reason for hiding this comment

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

Removed.

/// this class.
///
/// {@tool sample}
/// This example demonstrates how to extend [LocalFileParams] to provide
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// This example demonstrates how to extend [LocalFileParams] to provide
/// This example demonstrates how to extend [LoadFileParams] to provide

Copy link
Author

Choose a reason for hiding this comment

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

Fixed.

class WebKitLoadFileParams extends LoadFileParams {
/// Constructs a [WebKitLoadFileParams], the subclass of a [LoadFileParams].
///
/// The optional [readAccessPath] defines the directory that the WebView is
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this information should be moved to the documentation for the field.

Copy link
Author

Choose a reason for hiding this comment

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

Moved.

class AndroidLoadFileParams extends LoadFileParams {
/// Constructs a [AndroidLoadFileParams], the subclass of a [LoadFileParams].
///
/// Optionally, [headers] map may be included, which contains key-value pairs
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this information should be moved to the field.

Copy link
Author

Choose a reason for hiding this comment

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

Moved.

/// that will be passed as additional HTTP headers when loading the file.
AndroidLoadFileParams({
required String absoluteFilePath,
this.headers = _defaultHeaders,
Copy link
Contributor

Choose a reason for hiding this comment

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

This should work right?

Suggested change
this.headers = _defaultHeaders,
this.headers = const <String, String>{},

Copy link
Author

Choose a reason for hiding this comment

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

Updated.

@@ -383,15 +425,19 @@ class AndroidWebViewController extends PlatformWebViewController {
_webView.pigeon_instanceManager.getIdentifier(_webView)!;

@override
Future<void> loadFile(
Copy link
Contributor

Choose a reason for hiding this comment

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

As stated in the iOS implementation as well, we keep loadFile and it should just call loadFileWithParams.

Copy link
Author

Choose a reason for hiding this comment

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

Updated.

@@ -274,7 +274,7 @@ void main() {
));
}

test('loadFile without file prefix', () async {
test('Initializing WebView settings on controller creation', () async {
Copy link
Contributor

Choose a reason for hiding this comment

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

Since the loadFile method will be kept in AndroidWebViewController, one of the tests for loadFile should be kept.

Copy link
Author

Choose a reason for hiding this comment

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

'loadFile' tests restored under 'loadFile' group.
NOTE: the test on 277 line was not related to the 'loadFile' method, this change just fixes the label.

@agavrilko agavrilko force-pushed the feature/#136479-readAccessUrl-customization-on-iOS branch from f3090f0 to 9531f5c Compare June 4, 2025 00:12
@bparrishMines
Copy link
Contributor

@agavrilko Is this waiting for another review? WebViewController.loadFileWithParams is still included in webview_flutter.

flutter/flutter#136479

• Deprecates `WebViewController.loadFile(String)` in favor of `WebViewController.loadFileWithParams(LoadFileParams)`.
• Introduces `AndroidLoadFileParams` and `WebKitLoadFileParams` to support platform-specific parameters when loading local HTML files on Android and iOS/macOS.
@agavrilko agavrilko force-pushed the feature/#136479-readAccessUrl-customization-on-iOS branch 3 times, most recently from 29db233 to aa37e8d Compare June 24, 2025 22:49
@agavrilko
Copy link
Author

@agavrilko Is this waiting for another review? WebViewController.loadFileWithParams is still included in webview_flutter.

Hello @bparrishMines, my apologies, I did not see the notification about your reply.
The method was removed as requested.

I have rebased the branch onto the latest main and cleaned the rest of the code.
Let me know what I should do next.
Thank you for your assistance.

Copy link
Contributor

@bparrishMines bparrishMines left a comment

Choose a reason for hiding this comment

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

LGTM with a handful of nits. Thanks for working on this!

@stuartmorgan-g / @tarrinneal This is ready for a secondary review.

/// Platform-specific implementations may extend [LocalFileParams] to support
/// additional parameters, such as iOS/macOS-specific read access options.
///
/// Throws an [ArgumentError] if the file specified in
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I don't think this is true anymore for this method. I see that this was copied from loadFile, but this sentence can be removed for this method.

Copy link
Author

Choose a reason for hiding this comment

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

Removed from loadFileWithParams only.

);
}

/// Additional HTTP headers to be included when loading the local file.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Separate first sentence.

Suggested change
/// Additional HTTP headers to be included when loading the local file.
/// Additional HTTP headers to be included when loading the local file.
///

Copy link
Author

Choose a reason for hiding this comment

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

Updated.

) async {
switch (params) {
case final AndroidLoadFileParams params:
await _webView.settings.setAllowFileAccess(true);
Copy link
Contributor

@bparrishMines bparrishMines Jun 25, 2025

Choose a reason for hiding this comment

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

nit: Using Future.wait here is a little faster. Method calls are called sequentially and are handled synchronously.

Suggested change
await _webView.settings.setAllowFileAccess(true);
await Future.wait(<Future<void>>[
_webView.settings.setAllowFileAccess(true),
_webView.loadUrl(params.absoluteFilePath, params.headers),
]);

Copy link
Author

Choose a reason for hiding this comment

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

Updated.
I'm not familiar with the internal implementation, but these operations seem closely related. Could this introduce any potential race conditions? Setting the access flag after the page starts loading doesn't seem reliable to me.

);
}

/// The directory to which the WebView is granted read access.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Separate first sentence.

Suggested change
/// The directory to which the WebView is granted read access.
/// The directory to which the WebView is granted read access.
///

Copy link
Author

Choose a reason for hiding this comment

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

Updated.

@@ -1,3 +1,7 @@
## 4.8.0

* Introduces `AndroidLoadFileParams`, a platform-specific extension of `LoadFileParams` for Android that adds support for `headers`.
Copy link
Contributor

Choose a reason for hiding this comment

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

I would also add something like this

Suggested change
* Introduces `AndroidLoadFileParams`, a platform-specific extension of `LoadFileParams` for Android that adds support for `headers`.
* Adds support for `PlatformWebViewController.loadFileWithParams`.
* Introduces `AndroidLoadFileParams`, a platform-specific extension of `LoadFileParams` for Android that adds support for `headers`.

Copy link
Author

Choose a reason for hiding this comment

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

Added.


* Updates minimum supported SDK version to Flutter 3.27/Dart 3.6.
* Introduces `WebKitLoadFileParams`, a platform-specific extension of `LoadFileParams` for iOS and macOS that adds support for `readAccessPath`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment from Android.

Suggested change
* Introduces `WebKitLoadFileParams`, a platform-specific extension of `LoadFileParams` for iOS and macOS that adds support for `readAccessPath`.
* Adds support for `PlatformWebViewController.loadFileWithParams`.
* Introduces `WebKitLoadFileParams`, a platform-specific extension of `LoadFileParams` for iOS and macOS that adds support for `readAccessPath`.

Copy link
Author

Choose a reason for hiding this comment

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

Added.

## 2.14.0

* Adds `PlatformWebViewController.loadFileWithParams(LoadFileParams)` to support loading local HTML files with platform-specific parameters.
* Deprecates `PlatformWebViewController.loadFile(String)` in favour of `PlatformWebViewController.loadFileWithParams(LoadFileParams)`.
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be removed for now since we won't deprecate it until after the platform implementations have landed.

Copy link
Contributor

Choose a reason for hiding this comment

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

We can do deprecations in the initial pass now. That used to be impossible due to linting, but we no longer have deprecation lints enabled in the repo.

Copy link
Author

Choose a reason for hiding this comment

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

Removed from the change log, since we removed it from the code.

@agavrilko agavrilko force-pushed the feature/#136479-readAccessUrl-customization-on-iOS branch from aa37e8d to 1650c9b Compare June 25, 2025 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants