Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit e3a184f

Browse files
[webview_flutter_wkwebview] Implement WKNavigationDelegate.didFinishNavigation as a proof of concept for callback methods (#5199)
1 parent 2614d85 commit e3a184f

File tree

11 files changed

+407
-8
lines changed

11 files changed

+407
-8
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'instance_manager.dart';
6+
import 'web_kit.pigeon.dart';
7+
8+
/// Flutter api to dispose functions.
9+
class FunctionFlutterApiImpl extends FunctionFlutterApi {
10+
/// Constructs a [FunctionFlutterApiImpl].
11+
FunctionFlutterApiImpl({InstanceManager? instanceManager}) {
12+
this.instanceManager = instanceManager ?? InstanceManager.instance;
13+
}
14+
15+
/// Maintains instances stored to communicate with native language objects.
16+
late final InstanceManager instanceManager;
17+
18+
@override
19+
void dispose(int instanceId) {
20+
final Function? function = instanceManager.getInstance(instanceId);
21+
if (function != null) {
22+
instanceManager.removeInstance(function);
23+
}
24+
}
25+
}

packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,75 @@ class WKNavigationDelegateHostApi {
10221022
return;
10231023
}
10241024
}
1025+
1026+
Future<void> setDidFinishNavigation(
1027+
int arg_instanceId, int? arg_functionInstanceId) async {
1028+
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
1029+
'dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation',
1030+
codec,
1031+
binaryMessenger: _binaryMessenger);
1032+
final Map<Object?, Object?>? replyMap =
1033+
await channel.send(<Object?>[arg_instanceId, arg_functionInstanceId])
1034+
as Map<Object?, Object?>?;
1035+
if (replyMap == null) {
1036+
throw PlatformException(
1037+
code: 'channel-error',
1038+
message: 'Unable to establish connection on channel.',
1039+
);
1040+
} else if (replyMap['error'] != null) {
1041+
final Map<Object?, Object?> error =
1042+
(replyMap['error'] as Map<Object?, Object?>?)!;
1043+
throw PlatformException(
1044+
code: (error['code'] as String?)!,
1045+
message: error['message'] as String?,
1046+
details: error['details'],
1047+
);
1048+
} else {
1049+
return;
1050+
}
1051+
}
1052+
}
1053+
1054+
class _WKNavigationDelegateFlutterApiCodec extends StandardMessageCodec {
1055+
const _WKNavigationDelegateFlutterApiCodec();
1056+
}
1057+
1058+
abstract class WKNavigationDelegateFlutterApi {
1059+
static const MessageCodec<Object?> codec =
1060+
_WKNavigationDelegateFlutterApiCodec();
1061+
1062+
void didFinishNavigation(
1063+
int functionInstanceId, int webViewInstanceId, String? url);
1064+
static void setup(WKNavigationDelegateFlutterApi? api,
1065+
{BinaryMessenger? binaryMessenger}) {
1066+
{
1067+
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
1068+
'dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation',
1069+
codec,
1070+
binaryMessenger: binaryMessenger);
1071+
if (api == null) {
1072+
channel.setMessageHandler(null);
1073+
} else {
1074+
channel.setMessageHandler((Object? message) async {
1075+
assert(message != null,
1076+
'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null.');
1077+
final List<Object?> args = (message as List<Object?>?)!;
1078+
final int? arg_functionInstanceId = (args[0] as int?);
1079+
assert(arg_functionInstanceId != null,
1080+
'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null int.');
1081+
final int? arg_webViewInstanceId = (args[1] as int?);
1082+
assert(arg_webViewInstanceId != null,
1083+
'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null int.');
1084+
final String? arg_url = (args[2] as String?);
1085+
assert(arg_url != null,
1086+
'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null String.');
1087+
api.didFinishNavigation(
1088+
arg_functionInstanceId!, arg_webViewInstanceId!, arg_url!);
1089+
return;
1090+
});
1091+
}
1092+
}
1093+
}
10251094
}
10261095

10271096
class _NSObjectHostApiCodec extends StandardMessageCodec {
@@ -1142,6 +1211,38 @@ class NSObjectHostApi {
11421211
}
11431212
}
11441213

1214+
class _FunctionFlutterApiCodec extends StandardMessageCodec {
1215+
const _FunctionFlutterApiCodec();
1216+
}
1217+
1218+
abstract class FunctionFlutterApi {
1219+
static const MessageCodec<Object?> codec = _FunctionFlutterApiCodec();
1220+
1221+
void dispose(int instanceId);
1222+
static void setup(FunctionFlutterApi? api,
1223+
{BinaryMessenger? binaryMessenger}) {
1224+
{
1225+
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
1226+
'dev.flutter.pigeon.FunctionFlutterApi.dispose', codec,
1227+
binaryMessenger: binaryMessenger);
1228+
if (api == null) {
1229+
channel.setMessageHandler(null);
1230+
} else {
1231+
channel.setMessageHandler((Object? message) async {
1232+
assert(message != null,
1233+
'Argument for dev.flutter.pigeon.FunctionFlutterApi.dispose was null.');
1234+
final List<Object?> args = (message as List<Object?>?)!;
1235+
final int? arg_instanceId = (args[0] as int?);
1236+
assert(arg_instanceId != null,
1237+
'Argument for dev.flutter.pigeon.FunctionFlutterApi.dispose was null, expected non-null int.');
1238+
api.dispose(arg_instanceId!);
1239+
return;
1240+
});
1241+
}
1242+
}
1243+
}
1244+
}
1245+
11451246
class _WKWebViewHostApiCodec extends StandardMessageCodec {
11461247
const _WKWebViewHostApiCodec();
11471248
@override

packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,11 @@ class NSObject {
236236
: _api = NSObjectHostApiImpl(
237237
binaryMessenger: binaryMessenger,
238238
instanceManager: instanceManager,
239-
);
239+
) {
240+
// Ensures FlutterApis for the Foundation library and FunctionFlutterApi are
241+
// set up.
242+
FoundationFlutterApis.instance.ensureSetUp();
243+
}
240244

241245
final NSObjectHostApiImpl _api;
242246

packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'package:flutter/foundation.dart';
56
import 'package:flutter/services.dart';
67

8+
import '../common/function_flutter_api_impls.dart';
79
import '../common/instance_manager.dart';
810
import '../common/web_kit.pigeon.dart';
911
import 'foundation.dart';
@@ -35,6 +37,54 @@ Iterable<NSKeyValueObservingOptionsEnumData>
3537
});
3638
}
3739

40+
/// Handles initialization of Flutter APIs for the Foundation library.
41+
class FoundationFlutterApis {
42+
/// Constructs a [FoundationFlutterApis].
43+
@visibleForTesting
44+
FoundationFlutterApis({
45+
BinaryMessenger? binaryMessenger,
46+
InstanceManager? instanceManager,
47+
}) : _binaryMessenger = binaryMessenger {
48+
functionFlutterApi =
49+
FunctionFlutterApiImpl(instanceManager: instanceManager);
50+
}
51+
52+
static FoundationFlutterApis _instance = FoundationFlutterApis();
53+
54+
/// Sets the global instance containing the Flutter Apis for the Foundation library.
55+
@visibleForTesting
56+
static set instance(FoundationFlutterApis instance) {
57+
_instance = instance;
58+
}
59+
60+
/// Global instance containing the Flutter Apis for the Foundation library.
61+
static FoundationFlutterApis get instance {
62+
return _instance;
63+
}
64+
65+
final BinaryMessenger? _binaryMessenger;
66+
bool _hasBeenSetUp = false;
67+
68+
/// Flutter Api for disposing functions.
69+
///
70+
/// This FlutterApi is placed here because [FoundationFlutterApis.ensureSetUp]
71+
/// is called inside [NSObject] and [NSObject] is the parent class of all
72+
/// objects.
73+
@visibleForTesting
74+
late final FunctionFlutterApiImpl functionFlutterApi;
75+
76+
/// Ensures all the Flutter APIs have been set up to receive calls from native code.
77+
void ensureSetUp() {
78+
if (!_hasBeenSetUp) {
79+
FunctionFlutterApi.setup(
80+
functionFlutterApi,
81+
binaryMessenger: _binaryMessenger,
82+
);
83+
_hasBeenSetUp = true;
84+
}
85+
}
86+
}
87+
3888
/// Host api implementation for [NSObject].
3989
class NSObjectHostApiImpl extends NSObjectHostApi {
4090
/// Constructs an [NSObjectHostApiImpl].

packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -571,26 +571,28 @@ class WKUIDelegate {
571571
/// coordinate changes in your web view’s main frame.
572572
///
573573
/// Wraps [WKNavigationDelegate](https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc).
574-
class WKNavigationDelegate {
574+
class WKNavigationDelegate extends NSObject {
575575
/// Constructs a [WKNavigationDelegate].
576576
WKNavigationDelegate({
577577
BinaryMessenger? binaryMessenger,
578578
InstanceManager? instanceManager,
579-
}) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl(
579+
}) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl(
580+
binaryMessenger: binaryMessenger,
581+
instanceManager: instanceManager,
582+
),
583+
super(
580584
binaryMessenger: binaryMessenger,
581585
instanceManager: instanceManager,
582586
) {
587+
WebKitFlutterApis.instance.ensureSetUp();
583588
_navigationDelegateApi.createForInstances(this);
584589
}
585590

586591
final WKNavigationDelegateHostApiImpl _navigationDelegateApi;
587592

588593
/// Called when navigation from the main frame has started.
589594
Future<void> setDidStartProvisionalNavigation(
590-
void Function(
591-
WKWebView webView,
592-
String? url,
593-
)?
595+
void Function(WKWebView webView, String? url)?
594596
didStartProvisionalNavigation,
595597
) {
596598
throw UnimplementedError();
@@ -600,7 +602,10 @@ class WKNavigationDelegate {
600602
Future<void> setDidFinishNavigation(
601603
void Function(WKWebView webView, String? url)? didFinishNavigation,
602604
) {
603-
throw UnimplementedError();
605+
return _navigationDelegateApi.setDidFinishNavigationFromInstance(
606+
this,
607+
didFinishNavigation,
608+
);
604609
}
605610

606611
/// Called when permission is needed to navigate to new content.

packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'package:flutter/foundation.dart';
56
import 'package:flutter/services.dart';
67

78
import '../common/instance_manager.dart';
@@ -106,6 +107,51 @@ extension _NSUrlRequestConverter on NSUrlRequest {
106107
}
107108
}
108109

110+
/// Handles initialization of Flutter APIs for WebKit.
111+
class WebKitFlutterApis {
112+
/// Constructs a [WebKitFlutterApis].
113+
@visibleForTesting
114+
WebKitFlutterApis({
115+
BinaryMessenger? binaryMessenger,
116+
InstanceManager? instanceManager,
117+
}) : _binaryMessenger = binaryMessenger {
118+
navigationDelegateFlutterApi = WKNavigationDelegateFlutterApiImpl(
119+
instanceManager: instanceManager,
120+
);
121+
}
122+
123+
static WebKitFlutterApis _instance = WebKitFlutterApis();
124+
125+
/// Sets the global instance containing the Flutter Apis for the WebKit library.
126+
@visibleForTesting
127+
static set instance(WebKitFlutterApis instance) {
128+
_instance = instance;
129+
}
130+
131+
/// Global instance containing the Flutter Apis for the WebKit library.
132+
static WebKitFlutterApis get instance {
133+
return _instance;
134+
}
135+
136+
final BinaryMessenger? _binaryMessenger;
137+
bool _hasBeenSetUp = false;
138+
139+
/// Flutter Api for [WKNavigationDelegate].
140+
@visibleForTesting
141+
late final WKNavigationDelegateFlutterApiImpl navigationDelegateFlutterApi;
142+
143+
/// Ensures all the Flutter APIs have been set up to receive calls from native code.
144+
void ensureSetUp() {
145+
if (!_hasBeenSetUp) {
146+
WKNavigationDelegateFlutterApi.setup(
147+
navigationDelegateFlutterApi,
148+
binaryMessenger: _binaryMessenger,
149+
);
150+
_hasBeenSetUp = true;
151+
}
152+
}
153+
}
154+
109155
/// Host api implementation for [WKWebSiteDataStore].
110156
class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi {
111157
/// Constructs a [WebsiteDataStoreHostApiImpl].
@@ -381,6 +427,47 @@ class WKNavigationDelegateHostApiImpl extends WKNavigationDelegateHostApi {
381427
await create(instanceId);
382428
}
383429
}
430+
431+
/// Calls [setDidFinishNavigation] with the ids of the provided object instances.
432+
Future<void> setDidFinishNavigationFromInstance(
433+
WKNavigationDelegate instance,
434+
void Function(WKWebView, String?)? didFinishNavigation,
435+
) {
436+
int? functionInstanceId;
437+
if (didFinishNavigation != null) {
438+
functionInstanceId = instanceManager.getInstanceId(didFinishNavigation) ??
439+
instanceManager.tryAddInstance(didFinishNavigation)!;
440+
}
441+
return setDidFinishNavigation(
442+
instanceManager.getInstanceId(instance)!,
443+
functionInstanceId,
444+
);
445+
}
446+
}
447+
448+
/// Flutter api implementation for [WKNavigationDelegate].
449+
class WKNavigationDelegateFlutterApiImpl
450+
extends WKNavigationDelegateFlutterApi {
451+
/// Constructs a [WKNavigationDelegateFlutterApiImpl].
452+
WKNavigationDelegateFlutterApiImpl({InstanceManager? instanceManager}) {
453+
this.instanceManager = instanceManager ?? InstanceManager.instance;
454+
}
455+
456+
/// Maintains instances stored to communicate with native language objects.
457+
late final InstanceManager instanceManager;
458+
459+
@override
460+
void didFinishNavigation(
461+
int functionInstanceId,
462+
int webViewInstanceId,
463+
String? url,
464+
) {
465+
final void Function(
466+
WKWebView webView,
467+
String? url,
468+
) function = instanceManager.getInstance(functionInstanceId)!;
469+
function(instanceManager.getInstance(webViewInstanceId)!, url);
470+
}
384471
}
385472

386473
/// Host api implementation for [WKWebView].

0 commit comments

Comments
 (0)