-
Notifications
You must be signed in to change notification settings - Fork 3.4k
[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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -18,6 +18,41 @@ import 'android_webkit_constants.dart'; | |||||||
import 'platform_views_service_proxy.dart'; | ||||||||
import 'weak_reference_utils.dart'; | ||||||||
|
||||||||
/// Object specifying parameters for loading a local file in a | ||||||||
/// [AndroidWebViewController]. | ||||||||
@immutable | ||||||||
base class AndroidLoadFileParams extends LoadFileParams { | ||||||||
/// Constructs a [AndroidLoadFileParams], the subclass of a [LoadFileParams]. | ||||||||
AndroidLoadFileParams({ | ||||||||
required String absoluteFilePath, | ||||||||
this.headers = const <String, String>{}, | ||||||||
}) : super( | ||||||||
absoluteFilePath: absoluteFilePath.startsWith('file://') | ||||||||
? absoluteFilePath | ||||||||
: Uri.file(absoluteFilePath).toString(), | ||||||||
); | ||||||||
|
||||||||
/// Constructs a [AndroidLoadFileParams] using a [LoadFileParams]. | ||||||||
factory AndroidLoadFileParams.fromLoadFileParams( | ||||||||
LoadFileParams params, { | ||||||||
Map<String, String> headers = const <String, String>{}, | ||||||||
}) { | ||||||||
return AndroidLoadFileParams( | ||||||||
absoluteFilePath: params.absoluteFilePath, | ||||||||
headers: headers, | ||||||||
); | ||||||||
} | ||||||||
|
||||||||
/// Additional HTTP headers to be included when loading the local file. | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Separate first sentence.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated. |
||||||||
/// | ||||||||
/// If not provided at initialization time, doesn't add any additional headers. | ||||||||
/// | ||||||||
/// On Android, WebView supports adding headers when loading local or remote | ||||||||
/// content. This can be useful for scenarios like authentication, | ||||||||
/// content-type overrides, or custom request context. | ||||||||
final Map<String, String> headers; | ||||||||
} | ||||||||
|
||||||||
/// Object specifying creation parameters for creating a [AndroidWebViewController]. | ||||||||
/// | ||||||||
/// When adding additional fields make sure they can be null or have a default | ||||||||
|
@@ -386,12 +421,29 @@ class AndroidWebViewController extends PlatformWebViewController { | |||||||
Future<void> loadFile( | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As stated in the iOS implementation as well, we keep There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated. |
||||||||
String absoluteFilePath, | ||||||||
) { | ||||||||
final String url = absoluteFilePath.startsWith('file://') | ||||||||
? absoluteFilePath | ||||||||
: Uri.file(absoluteFilePath).toString(); | ||||||||
return loadFileWithParams( | ||||||||
AndroidLoadFileParams( | ||||||||
absoluteFilePath: absoluteFilePath, | ||||||||
), | ||||||||
); | ||||||||
} | ||||||||
|
||||||||
_webView.settings.setAllowFileAccess(true); | ||||||||
return _webView.loadUrl(url, <String, String>{}); | ||||||||
@override | ||||||||
Future<void> loadFileWithParams( | ||||||||
LoadFileParams params, | ||||||||
) async { | ||||||||
switch (params) { | ||||||||
case final AndroidLoadFileParams params: | ||||||||
await Future.wait(<Future<void>>[ | ||||||||
_webView.settings.setAllowFileAccess(true), | ||||||||
_webView.loadUrl(params.absoluteFilePath, params.headers), | ||||||||
]); | ||||||||
|
||||||||
default: | ||||||||
await loadFileWithParams( | ||||||||
AndroidLoadFileParams.fromLoadFileParams(params), | ||||||||
); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
@override | ||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -274,7 +274,7 @@ void main() { | |
)); | ||
} | ||
|
||
test('loadFile without file prefix', () async { | ||
test('Initializing WebView settings on controller creation', () async { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 'loadFile' tests restored under 'loadFile' group. |
||
final MockWebView mockWebView = MockWebView(); | ||
final MockWebSettings mockWebSettings = MockWebSettings(); | ||
createControllerWithMocks( | ||
|
@@ -292,56 +292,209 @@ void main() { | |
verify(mockWebSettings.setUseWideViewPort(false)).called(1); | ||
}); | ||
|
||
test('loadFile without file prefix', () async { | ||
final MockWebView mockWebView = MockWebView(); | ||
final MockWebSettings mockWebSettings = MockWebSettings(); | ||
final AndroidWebViewController controller = createControllerWithMocks( | ||
mockWebView: mockWebView, | ||
mockSettings: mockWebSettings, | ||
); | ||
group('loadFile', () { | ||
test('Without file prefix', () async { | ||
final MockWebView mockWebView = MockWebView(); | ||
final MockWebSettings mockWebSettings = MockWebSettings(); | ||
final AndroidWebViewController controller = createControllerWithMocks( | ||
mockWebView: mockWebView, | ||
mockSettings: mockWebSettings, | ||
); | ||
|
||
await controller.loadFile('/path/to/file.html'); | ||
await controller.loadFile('/path/to/file.html'); | ||
|
||
verify(mockWebSettings.setAllowFileAccess(true)).called(1); | ||
verify(mockWebView.loadUrl( | ||
'file:///path/to/file.html', | ||
<String, String>{}, | ||
)).called(1); | ||
}); | ||
verify(mockWebSettings.setAllowFileAccess(true)).called(1); | ||
verify(mockWebView.loadUrl( | ||
'file:///path/to/file.html', | ||
<String, String>{}, | ||
)).called(1); | ||
}); | ||
|
||
test('loadFile without file prefix and characters to be escaped', () async { | ||
final MockWebView mockWebView = MockWebView(); | ||
final MockWebSettings mockWebSettings = MockWebSettings(); | ||
final AndroidWebViewController controller = createControllerWithMocks( | ||
mockWebView: mockWebView, | ||
mockSettings: mockWebSettings, | ||
); | ||
test('Without file prefix and characters to be escaped', () async { | ||
final MockWebView mockWebView = MockWebView(); | ||
final MockWebSettings mockWebSettings = MockWebSettings(); | ||
final AndroidWebViewController controller = createControllerWithMocks( | ||
mockWebView: mockWebView, | ||
mockSettings: mockWebSettings, | ||
); | ||
|
||
await controller.loadFile('/path/to/?_<_>_.html'); | ||
await controller.loadFile('/path/to/?_<_>_.html'); | ||
|
||
verify(mockWebSettings.setAllowFileAccess(true)).called(1); | ||
verify(mockWebView.loadUrl( | ||
'file:///path/to/%3F_%3C_%3E_.html', | ||
<String, String>{}, | ||
)).called(1); | ||
verify(mockWebSettings.setAllowFileAccess(true)).called(1); | ||
verify(mockWebView.loadUrl( | ||
'file:///path/to/%3F_%3C_%3E_.html', | ||
<String, String>{}, | ||
)).called(1); | ||
}); | ||
|
||
test('With file prefix', () async { | ||
final MockWebView mockWebView = MockWebView(); | ||
final MockWebSettings mockWebSettings = MockWebSettings(); | ||
final AndroidWebViewController controller = createControllerWithMocks( | ||
mockWebView: mockWebView, | ||
); | ||
|
||
when(mockWebView.settings).thenReturn(mockWebSettings); | ||
|
||
await controller.loadFile('file:///path/to/file.html'); | ||
|
||
verify(mockWebSettings.setAllowFileAccess(true)).called(1); | ||
verify(mockWebView.loadUrl( | ||
'file:///path/to/file.html', | ||
<String, String>{}, | ||
)).called(1); | ||
}); | ||
}); | ||
|
||
test('loadFile with file prefix', () async { | ||
final MockWebView mockWebView = MockWebView(); | ||
final MockWebSettings mockWebSettings = MockWebSettings(); | ||
final AndroidWebViewController controller = createControllerWithMocks( | ||
mockWebView: mockWebView, | ||
); | ||
group('loadFileWithParams', () { | ||
group('Using LoadFileParams model', () { | ||
test('Without file prefix', () async { | ||
final MockWebView mockWebView = MockWebView(); | ||
final MockWebSettings mockWebSettings = MockWebSettings(); | ||
final AndroidWebViewController controller = createControllerWithMocks( | ||
mockWebView: mockWebView, | ||
mockSettings: mockWebSettings, | ||
); | ||
|
||
when(mockWebView.settings).thenReturn(mockWebSettings); | ||
await controller.loadFileWithParams( | ||
const LoadFileParams(absoluteFilePath: '/path/to/file.html'), | ||
); | ||
|
||
await controller.loadFile('file:///path/to/file.html'); | ||
verify(mockWebSettings.setAllowFileAccess(true)).called(1); | ||
verify(mockWebView.loadUrl( | ||
'file:///path/to/file.html', | ||
<String, String>{}, | ||
)).called(1); | ||
}); | ||
|
||
verify(mockWebSettings.setAllowFileAccess(true)).called(1); | ||
verify(mockWebView.loadUrl( | ||
'file:///path/to/file.html', | ||
<String, String>{}, | ||
)).called(1); | ||
test('Without file prefix and characters to be escaped', () async { | ||
final MockWebView mockWebView = MockWebView(); | ||
final MockWebSettings mockWebSettings = MockWebSettings(); | ||
final AndroidWebViewController controller = createControllerWithMocks( | ||
mockWebView: mockWebView, | ||
mockSettings: mockWebSettings, | ||
); | ||
|
||
await controller.loadFileWithParams( | ||
const LoadFileParams(absoluteFilePath: '/path/to/?_<_>_.html'), | ||
); | ||
|
||
verify(mockWebSettings.setAllowFileAccess(true)).called(1); | ||
verify(mockWebView.loadUrl( | ||
'file:///path/to/%3F_%3C_%3E_.html', | ||
<String, String>{}, | ||
)).called(1); | ||
}); | ||
|
||
test('With file prefix', () async { | ||
final MockWebView mockWebView = MockWebView(); | ||
final MockWebSettings mockWebSettings = MockWebSettings(); | ||
final AndroidWebViewController controller = createControllerWithMocks( | ||
mockWebView: mockWebView, | ||
mockSettings: mockWebSettings, | ||
); | ||
|
||
await controller.loadFileWithParams( | ||
const LoadFileParams(absoluteFilePath: 'file:///path/to/file.html'), | ||
); | ||
|
||
verify(mockWebSettings.setAllowFileAccess(true)).called(1); | ||
verify(mockWebView.loadUrl( | ||
'file:///path/to/file.html', | ||
<String, String>{}, | ||
)).called(1); | ||
}); | ||
}); | ||
|
||
group('Using WebKitLoadFileParams model', () { | ||
test('Without file prefix', () async { | ||
final MockWebView mockWebView = MockWebView(); | ||
final MockWebSettings mockWebSettings = MockWebSettings(); | ||
final AndroidWebViewController controller = createControllerWithMocks( | ||
mockWebView: mockWebView, | ||
mockSettings: mockWebSettings, | ||
); | ||
|
||
await controller.loadFileWithParams( | ||
AndroidLoadFileParams(absoluteFilePath: '/path/to/file.html'), | ||
); | ||
|
||
verify(mockWebSettings.setAllowFileAccess(true)).called(1); | ||
verify(mockWebView.loadUrl( | ||
'file:///path/to/file.html', | ||
<String, String>{}, | ||
)).called(1); | ||
}); | ||
|
||
test('Without file prefix and characters to be escaped', () async { | ||
final MockWebView mockWebView = MockWebView(); | ||
final MockWebSettings mockWebSettings = MockWebSettings(); | ||
final AndroidWebViewController controller = createControllerWithMocks( | ||
mockWebView: mockWebView, | ||
mockSettings: mockWebSettings, | ||
); | ||
|
||
await controller.loadFileWithParams( | ||
AndroidLoadFileParams(absoluteFilePath: '/path/to/?_<_>_.html'), | ||
); | ||
|
||
verify(mockWebSettings.setAllowFileAccess(true)).called(1); | ||
verify(mockWebView.loadUrl( | ||
'file:///path/to/%3F_%3C_%3E_.html', | ||
<String, String>{}, | ||
)).called(1); | ||
}); | ||
|
||
test('With file prefix', () async { | ||
final MockWebView mockWebView = MockWebView(); | ||
final MockWebSettings mockWebSettings = MockWebSettings(); | ||
final AndroidWebViewController controller = createControllerWithMocks( | ||
mockWebView: mockWebView, | ||
mockSettings: mockWebSettings, | ||
); | ||
|
||
await controller.loadFileWithParams( | ||
AndroidLoadFileParams( | ||
absoluteFilePath: 'file:///path/to/file.html'), | ||
); | ||
|
||
verify(mockWebSettings.setAllowFileAccess(true)).called(1); | ||
verify(mockWebView.loadUrl( | ||
'file:///path/to/file.html', | ||
<String, String>{}, | ||
)).called(1); | ||
}); | ||
|
||
test('With additional headers', () async { | ||
final MockWebView mockWebView = MockWebView(); | ||
final MockWebSettings mockWebSettings = MockWebSettings(); | ||
final AndroidWebViewController controller = createControllerWithMocks( | ||
mockWebView: mockWebView, | ||
mockSettings: mockWebSettings, | ||
); | ||
|
||
await controller.loadFileWithParams( | ||
AndroidLoadFileParams( | ||
absoluteFilePath: 'file:///path/to/file.html', | ||
headers: const <String, String>{ | ||
'Authorization': 'Bearer test_token', | ||
'Cache-Control': 'no-cache', | ||
'X-Custom-Header': 'test-value', | ||
}, | ||
), | ||
); | ||
|
||
verify(mockWebSettings.setAllowFileAccess(true)).called(1); | ||
verify(mockWebView.loadUrl( | ||
'file:///path/to/file.html', | ||
const <String, String>{ | ||
'Authorization': 'Bearer test_token', | ||
'Cache-Control': 'no-cache', | ||
'X-Custom-Header': 'test-value', | ||
}, | ||
)).called(1); | ||
}); | ||
}); | ||
}); | ||
|
||
test('loadFlutterAsset when asset does not exist', () async { | ||
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -1,3 +1,8 @@ | ||||||||
## 3.23.0 | ||||||||
|
||||||||
* Adds support for `PlatformWebViewController.loadFileWithParams`. | ||||||||
* Introduces `WebKitLoadFileParams`, a platform-specific extension of `LoadFileParams` for iOS and macOS that adds support for `readAccessPath`. | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment from Android.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added. |
||||||||
|
||||||||
## 3.22.1 | ||||||||
|
||||||||
* Changes the handling of a Flutter method failure from throwing an assertion error to logging the | ||||||||
|
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.