updated onPermissionRequest event for ios, added DebugSettings
This commit is contained in:
parent
17ed6c881a
commit
28455c696a
@ -7,6 +7,7 @@
|
|||||||
- Added `underPageBackgroundColor`, `isTextInteractionEnabled`, `isSiteSpecificQuirksModeEnabled`, `upgradeKnownHostsToHTTPS`, `forceDarkStrategy` WebView settings
|
- Added `underPageBackgroundColor`, `isTextInteractionEnabled`, `isSiteSpecificQuirksModeEnabled`, `upgradeKnownHostsToHTTPS`, `forceDarkStrategy` WebView settings
|
||||||
- Added `onCameraCaptureStateChanged`, `onMicrophoneCaptureStateChanged` WebView events
|
- Added `onCameraCaptureStateChanged`, `onMicrophoneCaptureStateChanged` WebView events
|
||||||
- Added support for `onPermissionRequest` event on iOS 15.0+
|
- Added support for `onPermissionRequest` event on iOS 15.0+
|
||||||
|
- Added `debugSettings` static property for WebView and ChromeSafariBrowser
|
||||||
- Updated `getMetaThemeColor` on iOS 15.0+
|
- Updated `getMetaThemeColor` on iOS 15.0+
|
||||||
- Deprecated `onLoadError` for `onReceivedError`. `onReceivedError` will be called also for subframes.
|
- Deprecated `onLoadError` for `onReceivedError`. `onReceivedError` will be called also for subframes.
|
||||||
- Deprecated `onLoadHttpError` for `onReceivedError`. `onReceivedHttpError` will be called also for subframes.
|
- Deprecated `onLoadHttpError` for `onReceivedError`. `onReceivedHttpError` will be called also for subframes.
|
||||||
|
@ -96,7 +96,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||||||
TextField(
|
TextField(
|
||||||
decoration: InputDecoration(prefixIcon: Icon(Icons.search)),
|
decoration: InputDecoration(prefixIcon: Icon(Icons.search)),
|
||||||
controller: urlController,
|
controller: urlController,
|
||||||
keyboardType: TextInputType.url,
|
keyboardType: TextInputType.text,
|
||||||
onSubmitted: (value) {
|
onSubmitted: (value) {
|
||||||
var url = Uri.parse(value);
|
var url = Uri.parse(value);
|
||||||
if (url.scheme.isEmpty) {
|
if (url.scheme.isEmpty) {
|
||||||
|
@ -19,7 +19,7 @@ Future main() async {
|
|||||||
// await Permission.microphone.request();
|
// await Permission.microphone.request();
|
||||||
// await Permission.storage.request();
|
// await Permission.storage.request();
|
||||||
|
|
||||||
WebView.debugLogging = false;
|
WebView.debugSettings.maxLogMessageLength = 500;
|
||||||
|
|
||||||
if (defaultTargetPlatform == TargetPlatform.android) {
|
if (defaultTargetPlatform == TargetPlatform.android) {
|
||||||
await InAppWebViewController.setWebContentsDebuggingEnabled(true);
|
await InAppWebViewController.setWebContentsDebuggingEnabled(true);
|
||||||
|
@ -669,10 +669,12 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||||||
if let newValue = change?[.newKey] as? Int {
|
if let newValue = change?[.newKey] as? Int {
|
||||||
newState = WKMediaCaptureState.init(rawValue: newValue)
|
newState = WKMediaCaptureState.init(rawValue: newValue)
|
||||||
}
|
}
|
||||||
if keyPath == #keyPath(WKWebView.cameraCaptureState) {
|
if oldState != newState {
|
||||||
onCameraCaptureStateChanged(oldState: oldState, newState: newState)
|
if keyPath == #keyPath(WKWebView.cameraCaptureState) {
|
||||||
} else {
|
onCameraCaptureStateChanged(oldState: oldState, newState: newState)
|
||||||
onMicrophoneCaptureStateChanged(oldState: oldState, newState: newState)
|
} else {
|
||||||
|
onMicrophoneCaptureStateChanged(oldState: oldState, newState: newState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// else if keyPath == #keyPath(WKWebView.fullscreenState) {
|
// else if keyPath == #keyPath(WKWebView.fullscreenState) {
|
||||||
@ -1552,10 +1554,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||||||
onPermissionRequest(request: permissionRequest, result: {(result) -> Void in
|
onPermissionRequest(request: permissionRequest, result: {(result) -> Void in
|
||||||
if result is FlutterError {
|
if result is FlutterError {
|
||||||
print((result as! FlutterError).message ?? "")
|
print((result as! FlutterError).message ?? "")
|
||||||
decisionHandler(.deny)
|
decisionHandler(.prompt)
|
||||||
}
|
}
|
||||||
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
||||||
decisionHandler(.deny)
|
decisionHandler(.prompt)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var response: [String: Any]
|
var response: [String: Any]
|
||||||
@ -1575,7 +1577,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
decisionHandler(.deny)
|
decisionHandler(.prompt)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1593,10 +1595,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||||||
onPermissionRequest(request: permissionRequest, result: {(result) -> Void in
|
onPermissionRequest(request: permissionRequest, result: {(result) -> Void in
|
||||||
if result is FlutterError {
|
if result is FlutterError {
|
||||||
print((result as! FlutterError).message ?? "")
|
print((result as! FlutterError).message ?? "")
|
||||||
decisionHandler(.deny)
|
decisionHandler(.prompt)
|
||||||
}
|
}
|
||||||
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
else if (result as? NSObject) == FlutterMethodNotImplemented {
|
||||||
decisionHandler(.deny)
|
decisionHandler(.prompt)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var response: [String: Any]
|
var response: [String: Any]
|
||||||
@ -1616,7 +1618,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
decisionHandler(.deny)
|
decisionHandler(.prompt)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import 'dart:developer' as developer;
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import '../util.dart';
|
import '../util.dart';
|
||||||
|
import '../debug_settings.dart';
|
||||||
|
|
||||||
import 'chrome_safari_browser_settings.dart';
|
import 'chrome_safari_browser_settings.dart';
|
||||||
|
|
||||||
@ -44,12 +45,8 @@ class ChromeSafariBrowserNotOpenedException implements Exception {
|
|||||||
///- Android native WebView
|
///- Android native WebView
|
||||||
///- iOS
|
///- iOS
|
||||||
class ChromeSafariBrowser {
|
class ChromeSafariBrowser {
|
||||||
///Enables [ChromeSafariBrowser] debug logging info.
|
///Debug settings.
|
||||||
///
|
static DebugSettings debugSettings = DebugSettings();
|
||||||
///The default value is the same value of [kDebugMode],
|
|
||||||
///so it is enabled by default when the application is compiled in debug mode
|
|
||||||
///and disabled when it is not.
|
|
||||||
static bool debugLogging = kDebugMode;
|
|
||||||
|
|
||||||
///View ID used internally.
|
///View ID used internally.
|
||||||
late final String id;
|
late final String id;
|
||||||
@ -70,12 +67,22 @@ class ChromeSafariBrowser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_debugLog(String method, dynamic args) {
|
_debugLog(String method, dynamic args) {
|
||||||
if (ChromeSafariBrowser.debugLogging) {
|
if (ChromeSafariBrowser.debugSettings.enabled) {
|
||||||
String message =
|
for (var regExp in ChromeSafariBrowser.debugSettings.excludeFilter) {
|
||||||
"ChromeSafariBrowser ID " + id + " calling \"" +
|
if (regExp.hasMatch(method)) return;
|
||||||
method.toString() + "\" using " + args.toString();
|
}
|
||||||
developer.log(message,
|
var maxLogMessageLength =
|
||||||
name: this.runtimeType.toString());
|
ChromeSafariBrowser.debugSettings.maxLogMessageLength;
|
||||||
|
String message = "ChromeSafariBrowser ID " +
|
||||||
|
id +
|
||||||
|
" calling \"" +
|
||||||
|
method.toString() +
|
||||||
|
"\" using " +
|
||||||
|
args.toString();
|
||||||
|
if (maxLogMessageLength >= 0 && message.length > maxLogMessageLength) {
|
||||||
|
message = message.substring(0, maxLogMessageLength) + "...";
|
||||||
|
}
|
||||||
|
developer.log(message, name: this.runtimeType.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
28
lib/src/debug_settings.dart
Normal file
28
lib/src/debug_settings.dart
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'in_app_webview/webview.dart';
|
||||||
|
import 'chrome_safari_browser/chrome_safari_browser.dart';
|
||||||
|
|
||||||
|
///Class that represents the debug settings used by [WebView] and [ChromeSafariBrowser].
|
||||||
|
class DebugSettings {
|
||||||
|
///Enables debug logging info.
|
||||||
|
///
|
||||||
|
///The default value is the same value of [kDebugMode],
|
||||||
|
///so it is enabled by default when the application is compiled in debug mode
|
||||||
|
///and disabled when it is not.
|
||||||
|
bool enabled;
|
||||||
|
|
||||||
|
///Filters used to exclude some logs from logging.
|
||||||
|
List<RegExp> excludeFilter;
|
||||||
|
|
||||||
|
///Max length of the log message.
|
||||||
|
///Set to `-1` to indicate that the log message needs to display the full content.
|
||||||
|
///
|
||||||
|
///The default value is `-1`.
|
||||||
|
int maxLogMessageLength;
|
||||||
|
|
||||||
|
DebugSettings({
|
||||||
|
this.enabled = kDebugMode,
|
||||||
|
this.excludeFilter = const [],
|
||||||
|
this.maxLogMessageLength = -1
|
||||||
|
});
|
||||||
|
}
|
@ -804,10 +804,13 @@ class InAppBrowser {
|
|||||||
///
|
///
|
||||||
///[resources] represents the array of resources the web content wants to access.
|
///[resources] represents the array of resources the web content wants to access.
|
||||||
///
|
///
|
||||||
///**NOTE**: available only on Android 23+.
|
///**NOTE for Android**: available only on Android 23+.
|
||||||
|
///
|
||||||
|
///**NOTE for iOS**: available only on iOS 15.0+. The default [PermissionResponse.action] is [PermissionResponseAction.PROMPT].
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android native WebView ([Official API - WebChromeClient.onPermissionRequest](https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest)))
|
///- Android native WebView ([Official API - WebChromeClient.onPermissionRequest](https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest)))
|
||||||
|
///- iOS
|
||||||
Future<PermissionResponse?>? onPermissionRequest(
|
Future<PermissionResponse?>? onPermissionRequest(
|
||||||
PermissionRequest permissionRequest) {}
|
PermissionRequest permissionRequest) {}
|
||||||
|
|
||||||
|
@ -104,7 +104,11 @@ class InAppWebViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_debugLog(String method, dynamic args) {
|
_debugLog(String method, dynamic args) {
|
||||||
if (WebView.debugLogging) {
|
if (WebView.debugSettings.enabled) {
|
||||||
|
for (var regExp in WebView.debugSettings.excludeFilter) {
|
||||||
|
if (regExp.hasMatch(method)) return;
|
||||||
|
}
|
||||||
|
var maxLogMessageLength = WebView.debugSettings.maxLogMessageLength;
|
||||||
String viewId = (getViewId() ?? _inAppBrowser?.id).toString();
|
String viewId = (getViewId() ?? _inAppBrowser?.id).toString();
|
||||||
String message = (_inAppBrowser == null ? "WebView" : "InAppBrowser") +
|
String message = (_inAppBrowser == null ? "WebView" : "InAppBrowser") +
|
||||||
" ID " +
|
" ID " +
|
||||||
@ -113,12 +117,15 @@ class InAppWebViewController {
|
|||||||
method.toString() +
|
method.toString() +
|
||||||
"\" using " +
|
"\" using " +
|
||||||
args.toString();
|
args.toString();
|
||||||
|
if (maxLogMessageLength >= 0 && message.length > maxLogMessageLength) {
|
||||||
|
message = message.substring(0, maxLogMessageLength) + "...";
|
||||||
|
}
|
||||||
developer.log(message, name: this.runtimeType.toString());
|
developer.log(message, name: this.runtimeType.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> handleMethod(MethodCall call) async {
|
Future<dynamic> handleMethod(MethodCall call) async {
|
||||||
if (WebView.debugLogging && call.method != "onCallJsHandler") {
|
if (WebView.debugSettings.enabled && call.method != "onCallJsHandler") {
|
||||||
_debugLog(call.method, call.arguments);
|
_debugLog(call.method, call.arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,15 +170,15 @@ class InAppWebViewController {
|
|||||||
_webview!.onReceivedError!(this, request, error);
|
_webview!.onReceivedError!(this, request, error);
|
||||||
else if (isForMainFrame) {
|
else if (isForMainFrame) {
|
||||||
// ignore: deprecated_member_use_from_same_package
|
// ignore: deprecated_member_use_from_same_package
|
||||||
_webview!.onLoadError!(this, request.url, error.type.toNativeValue(),
|
_webview!.onLoadError!(this, request.url,
|
||||||
error.description);
|
error.type.toNativeValue(), error.description);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isForMainFrame) {
|
if (isForMainFrame) {
|
||||||
_inAppBrowser!
|
_inAppBrowser!
|
||||||
// ignore: deprecated_member_use_from_same_package
|
// ignore: deprecated_member_use_from_same_package
|
||||||
.onLoadError(
|
.onLoadError(request.url, error.type.toNativeValue(),
|
||||||
request.url, error.type.toNativeValue(), error.description);
|
error.description);
|
||||||
}
|
}
|
||||||
_inAppBrowser!.onReceivedError(request, error);
|
_inAppBrowser!.onReceivedError(request, error);
|
||||||
}
|
}
|
||||||
@ -203,9 +210,9 @@ class InAppWebViewController {
|
|||||||
} else {
|
} else {
|
||||||
if (isForMainFrame) {
|
if (isForMainFrame) {
|
||||||
_inAppBrowser!
|
_inAppBrowser!
|
||||||
// ignore: deprecated_member_use_from_same_package
|
// ignore: deprecated_member_use_from_same_package
|
||||||
.onLoadHttpError(request.url, errorResponse.statusCode ?? -1,
|
.onLoadHttpError(request.url, errorResponse.statusCode ?? -1,
|
||||||
errorResponse.reasonPhrase ?? '');
|
errorResponse.reasonPhrase ?? '');
|
||||||
}
|
}
|
||||||
_inAppBrowser!.onReceivedHttpError(request, errorResponse);
|
_inAppBrowser!.onReceivedHttpError(request, errorResponse);
|
||||||
}
|
}
|
||||||
@ -1091,10 +1098,13 @@ class InAppWebViewController {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "onCameraCaptureStateChanged":
|
case "onCameraCaptureStateChanged":
|
||||||
if ((_webview != null && _webview!.onCameraCaptureStateChanged != null) ||
|
if ((_webview != null &&
|
||||||
|
_webview!.onCameraCaptureStateChanged != null) ||
|
||||||
_inAppBrowser != null) {
|
_inAppBrowser != null) {
|
||||||
var oldState = MediaCaptureState.fromValue(call.arguments["oldState"]);
|
var oldState =
|
||||||
var newState = MediaCaptureState.fromValue(call.arguments["newState"]);
|
MediaCaptureState.fromValue(call.arguments["oldState"]);
|
||||||
|
var newState =
|
||||||
|
MediaCaptureState.fromValue(call.arguments["newState"]);
|
||||||
|
|
||||||
if (_webview != null && _webview!.onCameraCaptureStateChanged != null)
|
if (_webview != null && _webview!.onCameraCaptureStateChanged != null)
|
||||||
_webview!.onCameraCaptureStateChanged!(this, oldState, newState);
|
_webview!.onCameraCaptureStateChanged!(this, oldState, newState);
|
||||||
@ -1103,13 +1113,18 @@ class InAppWebViewController {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "onMicrophoneCaptureStateChanged":
|
case "onMicrophoneCaptureStateChanged":
|
||||||
if ((_webview != null && _webview!.onMicrophoneCaptureStateChanged != null) ||
|
if ((_webview != null &&
|
||||||
|
_webview!.onMicrophoneCaptureStateChanged != null) ||
|
||||||
_inAppBrowser != null) {
|
_inAppBrowser != null) {
|
||||||
var oldState = MediaCaptureState.fromValue(call.arguments["oldState"]);
|
var oldState =
|
||||||
var newState = MediaCaptureState.fromValue(call.arguments["newState"]);
|
MediaCaptureState.fromValue(call.arguments["oldState"]);
|
||||||
|
var newState =
|
||||||
|
MediaCaptureState.fromValue(call.arguments["newState"]);
|
||||||
|
|
||||||
if (_webview != null && _webview!.onMicrophoneCaptureStateChanged != null)
|
if (_webview != null &&
|
||||||
_webview!.onMicrophoneCaptureStateChanged!(this, oldState, newState);
|
_webview!.onMicrophoneCaptureStateChanged != null)
|
||||||
|
_webview!.onMicrophoneCaptureStateChanged!(
|
||||||
|
this, oldState, newState);
|
||||||
else
|
else
|
||||||
_inAppBrowser!.onMicrophoneCaptureStateChanged(oldState, newState);
|
_inAppBrowser!.onMicrophoneCaptureStateChanged(oldState, newState);
|
||||||
}
|
}
|
||||||
@ -3286,8 +3301,7 @@ class InAppWebViewController {
|
|||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- iOS ([Official API - WKWebView.setCameraCaptureState](https://developer.apple.com/documentation/webkit/wkwebview/3763097-setcameracapturestate)).
|
///- iOS ([Official API - WKWebView.setCameraCaptureState](https://developer.apple.com/documentation/webkit/wkwebview/3763097-setcameracapturestate)).
|
||||||
Future<void> setCameraCaptureState(
|
Future<void> setCameraCaptureState({required MediaCaptureState state}) async {
|
||||||
{required MediaCaptureState state}) async {
|
|
||||||
Map<String, dynamic> args = <String, dynamic>{};
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
args.putIfAbsent('state', () => state.toValue());
|
args.putIfAbsent('state', () => state.toValue());
|
||||||
await _channel.invokeMethod('setCameraCaptureState', args);
|
await _channel.invokeMethod('setCameraCaptureState', args);
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
|
|
||||||
import '../pull_to_refresh/pull_to_refresh_controller.dart';
|
import '../pull_to_refresh/pull_to_refresh_controller.dart';
|
||||||
|
|
||||||
import '../context_menu.dart';
|
import '../context_menu.dart';
|
||||||
@ -12,14 +10,16 @@ import 'in_app_webview_controller.dart';
|
|||||||
import 'in_app_webview_settings.dart';
|
import 'in_app_webview_settings.dart';
|
||||||
import 'headless_in_app_webview.dart';
|
import 'headless_in_app_webview.dart';
|
||||||
|
|
||||||
|
import '../debug_settings.dart';
|
||||||
|
|
||||||
///Abstract class that represents a WebView. Used by [InAppWebView], [HeadlessInAppWebView] and the WebView of [InAppBrowser].
|
///Abstract class that represents a WebView. Used by [InAppWebView], [HeadlessInAppWebView] and the WebView of [InAppBrowser].
|
||||||
abstract class WebView {
|
abstract class WebView {
|
||||||
///Enables [WebView] debug logging info.
|
///Debug settings used by [InAppWebView], [HeadlessInAppWebView] and [InAppBrowser].
|
||||||
///
|
///The default value excludes the [WebView.onScrollChanged] and [WebView.onOverScrolled] events.
|
||||||
///The default value is the same value of [kDebugMode],
|
static DebugSettings debugSettings = DebugSettings(excludeFilter: [
|
||||||
///so it is enabled by default when the application is compiled in debug mode
|
RegExp(r"onScrollChanged"),
|
||||||
///and disabled when it is not.
|
RegExp(r"onOverScrolled")
|
||||||
static bool debugLogging = kDebugMode;
|
]);
|
||||||
|
|
||||||
///The window id of a [CreateWindowAction.windowId].
|
///The window id of a [CreateWindowAction.windowId].
|
||||||
final int? windowId;
|
final int? windowId;
|
||||||
@ -567,10 +567,13 @@ abstract class WebView {
|
|||||||
///
|
///
|
||||||
///[resources] represents the array of resources the web content wants to access.
|
///[resources] represents the array of resources the web content wants to access.
|
||||||
///
|
///
|
||||||
///**NOTE**: available only on Android 23+.
|
///**NOTE for Android**: available only on Android 23+.
|
||||||
|
///
|
||||||
|
///**NOTE for iOS**: available only on iOS 15.0+. The default [PermissionResponse.action] is [PermissionResponseAction.PROMPT].
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android native WebView ([Official API - WebChromeClient.onPermissionRequest](https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest)))
|
///- Android native WebView ([Official API - WebChromeClient.onPermissionRequest](https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest)))
|
||||||
|
///- iOS
|
||||||
final Future<PermissionResponse?> Function(InAppWebViewController controller,
|
final Future<PermissionResponse?> Function(InAppWebViewController controller,
|
||||||
PermissionRequest permissionRequest)? onPermissionRequest;
|
PermissionRequest permissionRequest)? onPermissionRequest;
|
||||||
|
|
||||||
|
@ -14,3 +14,4 @@ export 'http_auth_credentials_database.dart';
|
|||||||
export 'context_menu.dart';
|
export 'context_menu.dart';
|
||||||
export 'pull_to_refresh/main.dart';
|
export 'pull_to_refresh/main.dart';
|
||||||
export 'web_message/main.dart';
|
export 'web_message/main.dart';
|
||||||
|
export 'debug_settings.dart';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user