mirror of
https://github.com/flutter/packages.git
synced 2025-06-30 23:03:11 +08:00
[google_sign_in_web] Migrate to pkg:web. (#5612)
This PR updates `google_sign_in_web` to: * Use the latest `google_identity_services_web` and `http` * Use `package:web` This should allow the package to compile to WASM. ## Issues * Fixes https://github.com/flutter/flutter/issues/139169 * Needed to https://github.com/flutter/flutter/issues/139170 ## Tests * All tests should pass (ran locally) * Demo app compiled to WASM: https://dit-gis-test.web.app
This commit is contained in:
@ -1,3 +1,8 @@
|
||||
## 0.12.3
|
||||
|
||||
* Migrates to `package:web`.
|
||||
* Updates minimum supported SDK version to Flutter 3.16.0/Dart 3.2.0.
|
||||
|
||||
## 0.12.2+1
|
||||
|
||||
* Re-publishes `0.12.2` with a small fix to the CodeClient initialization.
|
||||
|
@ -9,8 +9,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:google_sign_in_web/src/flexible_size_html_element_view.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'src/dom.dart';
|
||||
import 'package:web/web.dart' as web;
|
||||
|
||||
/// Used to keep track of the number of HtmlElementView factories the test has registered.
|
||||
int widgetFactoryNumber = 0;
|
||||
@ -54,7 +53,8 @@ void main() {
|
||||
(WidgetTester tester) async {
|
||||
const Size childSize = Size(300, 40);
|
||||
|
||||
final DomHtmlElement resizable = document.createElement('div');
|
||||
final web.HTMLDivElement resizable =
|
||||
web.document.createElement('div') as web.HTMLDivElement;
|
||||
resize(resizable, childSize);
|
||||
|
||||
final Element element = await pumpResizableWidget(
|
||||
@ -73,7 +73,8 @@ void main() {
|
||||
const Size initialSize = Size(160, 100);
|
||||
const Size newSize = Size(300, 40);
|
||||
|
||||
final DomHtmlElement resizable = document.createElement('div');
|
||||
final web.HTMLDivElement resizable =
|
||||
web.document.createElement('div') as web.HTMLDivElement;
|
||||
resize(resizable, newSize);
|
||||
|
||||
final Element element = await pumpResizableWidget(
|
||||
@ -94,7 +95,8 @@ void main() {
|
||||
final Size expandedSize = initialSize * 2;
|
||||
final Size contractedSize = initialSize / 2;
|
||||
|
||||
final DomHtmlElement resizable = document.createElement('div')
|
||||
final web.HTMLDivElement resizable = web.document.createElement('div')
|
||||
as web.HTMLDivElement
|
||||
..setAttribute(
|
||||
'style', 'width: 100%; height: 100%; background: #fabada;');
|
||||
|
||||
@ -160,7 +162,8 @@ class ResizableFromJs extends StatelessWidget {
|
||||
ui_web.platformViewRegistry.registerViewFactory(
|
||||
'resizable_from_js_$instanceId',
|
||||
(int viewId) {
|
||||
final DomHtmlElement element = document.createElement('div');
|
||||
final web.HTMLDivElement element =
|
||||
web.document.createElement('div') as web.HTMLDivElement;
|
||||
element.setAttribute('style',
|
||||
'width: 100%; height: 100%; overflow: hidden; background: red;');
|
||||
element.id = 'test_element_$viewId';
|
||||
@ -191,16 +194,16 @@ class ResizableFromJs extends StatelessWidget {
|
||||
}
|
||||
|
||||
/// Resizes `resizable` to `size`.
|
||||
void resize(DomHtmlElement resizable, Size size) {
|
||||
void resize(web.HTMLElement resizable, Size size) {
|
||||
resizable.setAttribute('style',
|
||||
'width: ${size.width}px; height: ${size.height}px; background: #fabada');
|
||||
}
|
||||
|
||||
/// Returns a function that can be used to inject `element` in `onPlatformViewCreated` callbacks.
|
||||
void Function(int) injectElement(DomHtmlElement element) {
|
||||
void Function(int) injectElement(web.HTMLElement element) {
|
||||
return (int viewId) {
|
||||
final DomHtmlElement root =
|
||||
document.querySelector('#test_element_$viewId')!;
|
||||
root.appendChild(element);
|
||||
final web.Element? root =
|
||||
web.document.querySelector('#test_element_$viewId');
|
||||
root!.appendChild(element);
|
||||
};
|
||||
}
|
||||
|
@ -13,9 +13,9 @@ import 'package:google_sign_in_web/src/people.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart' as mockito;
|
||||
import 'package:web/web.dart' as web;
|
||||
|
||||
import 'google_sign_in_web_test.mocks.dart';
|
||||
import 'src/dom.dart';
|
||||
import 'src/person.dart';
|
||||
|
||||
// Mock GisSdkClient so we can simulate any response from the JS side.
|
||||
@ -36,12 +36,12 @@ void main() {
|
||||
expect(plugin.autoDetectedClientId, isNull);
|
||||
|
||||
// Add it to the test page now, and try again
|
||||
final DomHtmlMetaElement meta =
|
||||
document.createElement('meta') as DomHtmlMetaElement
|
||||
final web.HTMLMetaElement meta =
|
||||
web.document.createElement('meta') as web.HTMLMetaElement
|
||||
..name = clientIdMetaName
|
||||
..content = expectedClientId;
|
||||
|
||||
document.head.appendChild(meta);
|
||||
web.document.head!.appendChild(meta);
|
||||
|
||||
final GoogleSignInPlugin another = GoogleSignInPlugin(
|
||||
debugOverrideLoader: true,
|
||||
|
@ -1,70 +0,0 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
/*
|
||||
// DOM shim. This file contains everything we need from the DOM API written as
|
||||
// @staticInterop, so we don't need dart:html
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/
|
||||
//
|
||||
// (To be replaced by `package:web`)
|
||||
*/
|
||||
|
||||
import 'package:js/js.dart';
|
||||
|
||||
/// Document interface
|
||||
@JS()
|
||||
@staticInterop
|
||||
abstract class DomHtmlDocument {}
|
||||
|
||||
/// Some methods of document
|
||||
extension DomHtmlDocumentExtension on DomHtmlDocument {
|
||||
/// document.head
|
||||
external DomHtmlElement get head;
|
||||
|
||||
/// document.createElement
|
||||
external DomHtmlElement createElement(String tagName);
|
||||
|
||||
/// document.querySelector
|
||||
external DomHtmlElement? querySelector(String selector);
|
||||
}
|
||||
|
||||
/// An instance of an HTMLElement
|
||||
@JS()
|
||||
@staticInterop
|
||||
abstract class DomHtmlElement {}
|
||||
|
||||
/// (Some) methods of HtmlElement
|
||||
extension DomHtmlElementExtension on DomHtmlElement {
|
||||
external String get id;
|
||||
external set id(String id);
|
||||
external set innerText(String innerText);
|
||||
external String? getAttribute(String attributeName);
|
||||
|
||||
/// Node.appendChild
|
||||
external DomHtmlElement appendChild(DomHtmlElement child);
|
||||
|
||||
/// Element.setAttribute
|
||||
external void setAttribute(String name, Object value);
|
||||
|
||||
/// Element.remove
|
||||
external void remove();
|
||||
}
|
||||
|
||||
/// An instance of an HTMLMetaElement
|
||||
@JS()
|
||||
@staticInterop
|
||||
abstract class DomHtmlMetaElement extends DomHtmlElement {}
|
||||
|
||||
/// Some methods exclusive of Script elements
|
||||
extension DomHtmlMetaElementExtension on DomHtmlMetaElement {
|
||||
external set name(String name);
|
||||
external set content(String content);
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
/// window.document
|
||||
@JS()
|
||||
@staticInterop
|
||||
external DomHtmlDocument get document;
|
@ -2,9 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:js/js_util.dart' as js_util;
|
||||
import 'dart:js_interop';
|
||||
|
||||
/// Converts a [data] object into a JS Object of type `T`.
|
||||
T jsifyAs<T>(Map<String, Object?> data) {
|
||||
return js_util.jsify(data) as T;
|
||||
return data.jsify() as T;
|
||||
}
|
||||
|
@ -2,14 +2,14 @@ name: google_sign_in_web_integration_tests
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=3.1.0 <4.0.0"
|
||||
flutter: ">=3.13.0"
|
||||
sdk: ">=3.2.0 <4.0.0"
|
||||
flutter: ">=3.16.0"
|
||||
|
||||
dependencies:
|
||||
cupertino_icons: ^1.0.2
|
||||
flutter:
|
||||
sdk: flutter
|
||||
google_identity_services_web: ^0.2.1
|
||||
google_identity_services_web: ^0.3.0
|
||||
google_sign_in_platform_interface: ^2.4.0
|
||||
google_sign_in_web:
|
||||
path: ../
|
||||
@ -21,8 +21,8 @@ dev_dependencies:
|
||||
http: ">=0.13.0 <2.0.0"
|
||||
integration_test:
|
||||
sdk: flutter
|
||||
js: ^0.6.3
|
||||
mockito: 5.4.3
|
||||
web: ">=0.3.0 <0.5.0"
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
@ -3,7 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:html' as html;
|
||||
import 'dart:js_interop';
|
||||
import 'dart:ui_web' as ui_web;
|
||||
|
||||
import 'package:flutter/foundation.dart' show visibleForTesting, kDebugMode;
|
||||
@ -13,9 +13,9 @@ import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
|
||||
import 'package:google_identity_services_web/loader.dart' as loader;
|
||||
import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart';
|
||||
import 'package:web/web.dart' as web;
|
||||
|
||||
import 'src/button_configuration.dart' show GSIButtonConfiguration;
|
||||
import 'src/dom.dart';
|
||||
import 'src/flexible_size_html_element_view.dart';
|
||||
import 'src/gis_client.dart';
|
||||
|
||||
@ -54,7 +54,7 @@ class GoogleSignInPlugin extends GoogleSignInPlatform {
|
||||
}) : _gisSdkClient = debugOverrideGisSdkClient,
|
||||
_userDataController = debugOverrideUserDataController ??
|
||||
StreamController<GoogleSignInUserData?>.broadcast() {
|
||||
autoDetectedClientId = html
|
||||
autoDetectedClientId = web.document
|
||||
.querySelector(clientIdMetaSelector)
|
||||
?.getAttribute(clientIdAttributeName);
|
||||
|
||||
@ -175,7 +175,7 @@ class GoogleSignInPlugin extends GoogleSignInPlatform {
|
||||
ui_web.platformViewRegistry.registerViewFactory(
|
||||
'gsi_login_button',
|
||||
(int viewId) {
|
||||
final DomElement element = createDomElement('div');
|
||||
final web.Element element = web.document.createElement('div');
|
||||
element.setAttribute('style',
|
||||
'width: 100%; height: 100%; overflow: hidden; display: flex; flex-wrap: wrap; align-content: center; justify-content: center;');
|
||||
element.id = 'sign_in_button_$viewId';
|
||||
@ -196,8 +196,8 @@ class GoogleSignInPlugin extends GoogleSignInPlatform {
|
||||
return FlexHtmlElementView(
|
||||
viewType: 'gsi_login_button',
|
||||
onPlatformViewCreated: (int viewId) {
|
||||
final DomElement? element =
|
||||
domDocument.querySelector('#sign_in_button_$viewId');
|
||||
final web.Element? element =
|
||||
web.document.querySelector('#sign_in_button_$viewId');
|
||||
assert(element != null,
|
||||
'Cannot render GSI button. DOM is not ready!');
|
||||
_gisClient.renderButton(element!, config);
|
||||
@ -219,10 +219,11 @@ class GoogleSignInPlugin extends GoogleSignInPlatform {
|
||||
@override
|
||||
Future<GoogleSignInUserData?> signIn() async {
|
||||
if (kDebugMode) {
|
||||
domConsole.warn(
|
||||
web.console.warn(
|
||||
"The `signIn` method is discouraged on the web because it can't reliably provide an `idToken`.\n"
|
||||
'Use `signInSilently` and `renderButton` to authenticate your users instead.\n'
|
||||
'Read more: https://pub.dev/packages/google_sign_in_web');
|
||||
'Use `signInSilently` and `renderButton` to authenticate your users instead.\n'
|
||||
'Read more: https://pub.dev/packages/google_sign_in_web'
|
||||
.toJS);
|
||||
}
|
||||
await initialized;
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:google_identity_services_web/id.dart' as id;
|
||||
import 'package:js/js_util.dart' as js_util;
|
||||
|
||||
/// Converts user-facing `GisButtonConfiguration` into the JS-Interop `id.GsiButtonConfiguration`.
|
||||
id.GsiButtonConfiguration? convertButtonConfiguration(
|
||||
@ -12,17 +11,16 @@ id.GsiButtonConfiguration? convertButtonConfiguration(
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
return js_util.jsify(<String, Object?>{
|
||||
if (config.type != null) 'type': _idType[config.type],
|
||||
if (config.theme != null) 'theme': _idTheme[config.theme],
|
||||
if (config.size != null) 'size': _idSize[config.size],
|
||||
if (config.text != null) 'text': _idText[config.text],
|
||||
if (config.shape != null) 'shape': _idShape[config.shape],
|
||||
if (config.logoAlignment != null)
|
||||
'logo_alignment': _idLogoAlignment[config.logoAlignment],
|
||||
if (config.minimumWidth != null) 'width': config.minimumWidth,
|
||||
if (config.locale != null) 'locale': config.locale,
|
||||
}) as id.GsiButtonConfiguration;
|
||||
return id.GsiButtonConfiguration(
|
||||
type: _idType[config.type],
|
||||
theme: _idTheme[config.theme],
|
||||
size: _idSize[config.size],
|
||||
text: _idText[config.text],
|
||||
shape: _idShape[config.shape],
|
||||
logo_alignment: _idLogoAlignment[config.logoAlignment],
|
||||
width: config.minimumWidth,
|
||||
locale: config.locale,
|
||||
);
|
||||
}
|
||||
|
||||
/// A class to configure the Google Sign-In Button for web.
|
||||
|
@ -1,211 +0,0 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'package:js/js.dart';
|
||||
import 'package:js/js_util.dart' as js_util;
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class DomConsole {}
|
||||
|
||||
extension DomConsoleExtension on DomConsole {
|
||||
void debug(String message, [List<Object?>? more]) =>
|
||||
js_util.callMethod(this, 'debug', <Object?>[message, ...?more]);
|
||||
void info(String message, [List<Object?>? more]) =>
|
||||
js_util.callMethod(this, 'info', <Object?>[message, ...?more]);
|
||||
void warn(String message, [List<Object?>? more]) =>
|
||||
js_util.callMethod(this, 'warn', <Object?>[message, ...?more]);
|
||||
}
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class DomWindow {}
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class DomDocument {}
|
||||
|
||||
extension DomDocumentExtension on DomDocument {
|
||||
external DomElement? querySelector(String selectors);
|
||||
DomElement createElement(String name, [Object? options]) =>
|
||||
js_util.callMethod(this, 'createElement',
|
||||
<Object>[name, if (options != null) options]) as DomElement;
|
||||
}
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class DomElement {}
|
||||
|
||||
extension DomElementExtension on DomElement {
|
||||
external String get id;
|
||||
external set id(String id);
|
||||
external String? getAttribute(String attributeName);
|
||||
external void remove();
|
||||
external void setAttribute(String name, Object value);
|
||||
external void removeAttribute(String name);
|
||||
external set tabIndex(double? value);
|
||||
external double? get tabIndex;
|
||||
external set className(String value);
|
||||
external String get className;
|
||||
external bool hasAttribute(String name);
|
||||
external DomElement? get firstChild;
|
||||
external DomElement? querySelector(String selectors);
|
||||
external String get tagName;
|
||||
}
|
||||
|
||||
@JS('window')
|
||||
external DomWindow get domWindow;
|
||||
|
||||
@JS('document')
|
||||
external DomDocument get domDocument;
|
||||
|
||||
@JS('console')
|
||||
external DomConsole get domConsole;
|
||||
|
||||
DomElement createDomElement(String tag) => domDocument.createElement(tag);
|
||||
|
||||
/// DOM Observers: Mutation and Size
|
||||
typedef DomMutationCallbackFn = void Function(
|
||||
List<DomMutationRecord> mutation, DomMutationObserver observer);
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class DomMutationObserver {}
|
||||
|
||||
DomMutationObserver createDomMutationObserver(DomMutationCallbackFn fn) =>
|
||||
domCallConstructorString('MutationObserver', <Object>[
|
||||
allowInterop(
|
||||
(List<dynamic> mutations, DomMutationObserver observer) {
|
||||
fn(mutations.cast<DomMutationRecord>(), observer);
|
||||
},
|
||||
)
|
||||
])! as DomMutationObserver;
|
||||
|
||||
extension DomMutationObserverExtension on DomMutationObserver {
|
||||
external void disconnect();
|
||||
void observe(DomElement target,
|
||||
{bool? childList,
|
||||
bool? attributes,
|
||||
bool? subtree,
|
||||
List<String>? attributeFilter}) {
|
||||
final Map<String, dynamic> options = <String, dynamic>{
|
||||
if (childList != null) 'childList': childList,
|
||||
if (attributes != null) 'attributes': attributes,
|
||||
if (subtree != null) 'subtree': subtree,
|
||||
if (attributeFilter != null) 'attributeFilter': attributeFilter
|
||||
};
|
||||
return js_util
|
||||
.callMethod(this, 'observe', <Object?>[target, js_util.jsify(options)]);
|
||||
}
|
||||
}
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class DomMutationRecord {}
|
||||
|
||||
extension DomMutationRecordExtension on DomMutationRecord {
|
||||
external List<DomElement>? get addedNodes;
|
||||
external List<DomElement>? get removedNodes;
|
||||
external String? get attributeName;
|
||||
external String? get type;
|
||||
}
|
||||
|
||||
/// ResizeObserver JS binding.
|
||||
///
|
||||
/// See: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
|
||||
@JS()
|
||||
@staticInterop
|
||||
abstract class DomResizeObserver {}
|
||||
|
||||
/// Creates a DomResizeObserver with a callback.
|
||||
///
|
||||
/// Internally converts the `List<dynamic>` of entries into the expected
|
||||
/// `List<DomResizeObserverEntry>`
|
||||
DomResizeObserver? createDomResizeObserver(DomResizeObserverCallbackFn fn) {
|
||||
return domCallConstructorString('ResizeObserver', <Object?>[
|
||||
allowInterop((List<dynamic> entries, DomResizeObserver observer) {
|
||||
fn(entries.cast<DomResizeObserverEntry>(), observer);
|
||||
}),
|
||||
]) as DomResizeObserver?;
|
||||
}
|
||||
|
||||
/// ResizeObserver instance methods.
|
||||
///
|
||||
/// See: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#instance_methods
|
||||
extension DomResizeObserverExtension on DomResizeObserver {
|
||||
external void disconnect();
|
||||
external void observe(DomElement target,
|
||||
[DomResizeObserverObserveOptions options]);
|
||||
external void unobserve(DomElement target);
|
||||
}
|
||||
|
||||
/// Options object passed to the `observe` method of a [DomResizeObserver].
|
||||
///
|
||||
/// See: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/observe#parameters
|
||||
@JS()
|
||||
@staticInterop
|
||||
@anonymous
|
||||
abstract class DomResizeObserverObserveOptions {
|
||||
external factory DomResizeObserverObserveOptions({
|
||||
String box,
|
||||
});
|
||||
}
|
||||
|
||||
/// Type of the function used to create a Resize Observer.
|
||||
typedef DomResizeObserverCallbackFn = void Function(
|
||||
List<DomResizeObserverEntry> entries, DomResizeObserver observer);
|
||||
|
||||
/// The object passed to the [DomResizeObserverCallbackFn], which allows access to the new dimensions of the observed element.
|
||||
///
|
||||
/// See: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry
|
||||
@JS()
|
||||
@staticInterop
|
||||
abstract class DomResizeObserverEntry {}
|
||||
|
||||
/// ResizeObserverEntry instance properties.
|
||||
///
|
||||
/// See: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry#instance_properties
|
||||
extension DomResizeObserverEntryExtension on DomResizeObserverEntry {
|
||||
/// A DOMRectReadOnly object containing the new size of the observed element when the callback is run.
|
||||
///
|
||||
/// Note that this is better supported than the above two properties, but it
|
||||
/// is left over from an earlier implementation of the Resize Observer API, is
|
||||
/// still included in the spec for web compat reasons, and may be deprecated
|
||||
/// in future versions.
|
||||
external DomRectReadOnly get contentRect;
|
||||
external DomElement get target;
|
||||
// Some more future getters:
|
||||
//
|
||||
// borderBoxSize
|
||||
// contentBoxSize
|
||||
// devicePixelContentBoxSize
|
||||
}
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class DomRectReadOnly {}
|
||||
|
||||
extension DomRectReadOnlyExtension on DomRectReadOnly {
|
||||
external double get x;
|
||||
external double get y;
|
||||
external double get width;
|
||||
external double get height;
|
||||
external double get top;
|
||||
external double get right;
|
||||
external double get bottom;
|
||||
external double get left;
|
||||
}
|
||||
|
||||
Object? domGetConstructor(String constructorName) =>
|
||||
js_util.getProperty(domWindow, constructorName);
|
||||
|
||||
T? domCallConstructorString<T>(String constructorName, List<Object?> args) {
|
||||
final Object? constructor = domGetConstructor(constructorName);
|
||||
if (constructor == null) {
|
||||
return null;
|
||||
}
|
||||
return js_util.callConstructor<T>(constructor, args);
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
import 'dart:js_interop';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'dom.dart';
|
||||
import 'package:web/web.dart' as web;
|
||||
|
||||
/// An HTMLElementView widget that resizes with its contents.
|
||||
class FlexHtmlElementView extends StatefulWidget {
|
||||
@ -37,12 +37,12 @@ class _FlexHtmlElementView extends State<FlexHtmlElementView> {
|
||||
/// Watches for changes being made to the DOM tree.
|
||||
///
|
||||
/// See: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
|
||||
DomMutationObserver? _mutationObserver;
|
||||
web.MutationObserver? _mutationObserver;
|
||||
|
||||
/// Reports changes to the dimensions of an Element's content box.
|
||||
///
|
||||
/// See: https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API
|
||||
DomResizeObserver? _resizeObserver;
|
||||
web.ResizeObserver? _resizeObserver;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@ -55,8 +55,13 @@ class _FlexHtmlElementView extends State<FlexHtmlElementView> {
|
||||
/// Update the state with the new `size`, if needed.
|
||||
void _doResize(Size size) {
|
||||
if (size != _lastReportedSize) {
|
||||
domConsole.debug(
|
||||
'Resizing', <Object>[widget.viewType, size.width, size.height]);
|
||||
final String log = <Object?>[
|
||||
'Resizing: ',
|
||||
widget.viewType,
|
||||
size.width,
|
||||
size.height
|
||||
].join(' ');
|
||||
web.console.debug(log.toJS);
|
||||
setState(() {
|
||||
_lastReportedSize = size;
|
||||
});
|
||||
@ -65,12 +70,13 @@ class _FlexHtmlElementView extends State<FlexHtmlElementView> {
|
||||
|
||||
/// The function called whenever an observed resize occurs.
|
||||
void _onResizeEntries(
|
||||
List<DomResizeObserverEntry> resizes,
|
||||
DomResizeObserver observer,
|
||||
JSArray resizes,
|
||||
web.ResizeObserver observer,
|
||||
) {
|
||||
final DomRectReadOnly rect = resizes.last.contentRect;
|
||||
final web.DOMRectReadOnly rect =
|
||||
resizes.toDart.cast<web.ResizeObserverEntry>().last.contentRect;
|
||||
if (rect.width > 0 && rect.height > 0) {
|
||||
_doResize(Size(rect.width, rect.height));
|
||||
_doResize(Size(rect.width.toDouble(), rect.height.toDouble()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,31 +85,35 @@ class _FlexHtmlElementView extends State<FlexHtmlElementView> {
|
||||
/// When mutations are received, this function attaches a Resize Observer to
|
||||
/// the first child of the mutation, which will drive
|
||||
void _onMutationRecords(
|
||||
List<DomMutationRecord> mutations,
|
||||
DomMutationObserver observer,
|
||||
JSArray mutations,
|
||||
web.MutationObserver observer,
|
||||
) {
|
||||
for (final DomMutationRecord mutation in mutations) {
|
||||
if (mutation.addedNodes != null) {
|
||||
final DomElement? element = _locateSizeProvider(mutation.addedNodes!);
|
||||
mutations.toDart
|
||||
.cast<web.MutationRecord>()
|
||||
.forEach((web.MutationRecord mutation) {
|
||||
if (mutation.addedNodes.length > 0) {
|
||||
final web.Element? element = _locateSizeProvider(mutation.addedNodes);
|
||||
if (element != null) {
|
||||
_resizeObserver = createDomResizeObserver(_onResizeEntries);
|
||||
_resizeObserver = web.ResizeObserver(_onResizeEntries.toJS);
|
||||
_resizeObserver?.observe(element);
|
||||
// Stop looking at other mutations
|
||||
observer.disconnect();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Registers a MutationObserver on the root element of the HtmlElementView.
|
||||
void _registerListeners(DomElement? root) {
|
||||
void _registerListeners(web.Element? root) {
|
||||
assert(root != null, 'DOM is not ready for the FlexHtmlElementView');
|
||||
_mutationObserver = createDomMutationObserver(_onMutationRecords);
|
||||
_mutationObserver = web.MutationObserver(_onMutationRecords.toJS);
|
||||
// Monitor the size of the child element, whenever it's created...
|
||||
_mutationObserver!.observe(
|
||||
root!,
|
||||
childList: true,
|
||||
web.MutationObserverInit(
|
||||
childList: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -127,14 +137,14 @@ class _FlexHtmlElementView extends State<FlexHtmlElementView> {
|
||||
///
|
||||
/// The `elements` list should contain a single element: the only child of the
|
||||
/// element returned by `_locatePlatformViewRoot`.
|
||||
DomElement? _locateSizeProvider(List<DomElement> elements) {
|
||||
return elements.first;
|
||||
web.Element? _locateSizeProvider(web.NodeList elements) {
|
||||
return elements.item(0) as web.Element?;
|
||||
}
|
||||
|
||||
/// Finds the root element of a platform view by its `viewId`.
|
||||
///
|
||||
/// This element matches the one returned by the registered platform view factory.
|
||||
DomElement? _locatePlatformViewRoot(int viewId) {
|
||||
return domDocument
|
||||
web.Element? _locatePlatformViewRoot(int viewId) {
|
||||
return web.document
|
||||
.querySelector('flt-platform-view[slot\$="-$viewId"] :first-child');
|
||||
}
|
||||
|
@ -2,18 +2,16 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
import 'dart:async';
|
||||
import 'dart:js_interop';
|
||||
|
||||
// TODO(dit): Split `id` and `oauth2` "services" for mocking. https://github.com/flutter/flutter/issues/120657
|
||||
import 'package:google_identity_services_web/id.dart';
|
||||
import 'package:google_identity_services_web/oauth2.dart';
|
||||
import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart';
|
||||
// ignore: unnecessary_import
|
||||
import 'package:js/js.dart';
|
||||
import 'package:js/js_util.dart';
|
||||
import 'package:web/web.dart' as web;
|
||||
|
||||
import 'button_configuration.dart'
|
||||
show GSIButtonConfiguration, convertButtonConfiguration;
|
||||
import 'dom.dart';
|
||||
import 'people.dart' as people;
|
||||
import 'utils.dart' as utils;
|
||||
|
||||
@ -65,7 +63,9 @@ class GisSdkClient {
|
||||
|
||||
void _logIfEnabled(String message, [List<Object?>? more]) {
|
||||
if (_loggingEnabled) {
|
||||
domConsole.info('[google_sign_in_web] $message', more);
|
||||
final String log =
|
||||
<Object?>['[google_sign_in_web]', message, ...?more].join(' ');
|
||||
web.console.info(log.toJS);
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ class GisSdkClient {
|
||||
_tokenResponses.stream.listen((TokenResponse response) {
|
||||
_lastTokenResponse = response;
|
||||
_lastTokenResponseExpiration =
|
||||
DateTime.now().add(Duration(seconds: response.expires_in));
|
||||
DateTime.now().add(Duration(seconds: response.expires_in!));
|
||||
}, onError: (Object error) {
|
||||
_logIfEnabled('Error on TokenResponse:', <Object>[error.toString()]);
|
||||
_lastTokenResponse = null;
|
||||
@ -130,7 +130,7 @@ class GisSdkClient {
|
||||
// Initialize `id` for the silent-sign in code.
|
||||
final IdConfiguration idConfig = IdConfiguration(
|
||||
client_id: clientId,
|
||||
callback: allowInterop(onResponse),
|
||||
callback: onResponse,
|
||||
cancel_on_tap_outside: false,
|
||||
auto_select: true, // Attempt to sign-in silently.
|
||||
hd: hostedDomain,
|
||||
@ -161,12 +161,14 @@ class GisSdkClient {
|
||||
// Create a Token Client for authorization calls.
|
||||
final TokenClientConfig tokenConfig = TokenClientConfig(
|
||||
client_id: clientId,
|
||||
hosted_domain: hostedDomain,
|
||||
callback: allowInterop(_onTokenResponse),
|
||||
error_callback: allowInterop(_onTokenError),
|
||||
// `scope` will be modified by the `signIn` method, in case we need to
|
||||
// backfill user Profile info.
|
||||
scope: ' ',
|
||||
hd: hostedDomain,
|
||||
callback: _onTokenResponse,
|
||||
error_callback: _onTokenError,
|
||||
// This is here only to satisfy the initialization of the JS TokenClient.
|
||||
// In reality, `scope` is always overridden when calling `requestScopes`
|
||||
// (or the deprecated `signIn`) through an [OverridableTokenClientConfig]
|
||||
// object.
|
||||
scope: <String>[' '], // Fake (but non-empty) list of scopes.
|
||||
);
|
||||
return oauth2.initTokenClient(tokenConfig);
|
||||
}
|
||||
@ -187,9 +189,9 @@ class GisSdkClient {
|
||||
// Token clients have an additional `error_callback` for miscellaneous
|
||||
// errors, like "popup couldn't open" or "popup closed by user".
|
||||
void _onTokenError(Object? error) {
|
||||
// This is handled in a funky (js_interop) way because of:
|
||||
// https://github.com/dart-lang/sdk/issues/50899
|
||||
_tokenResponses.addError(getProperty(error!, 'type'));
|
||||
if (error != null) {
|
||||
_tokenResponses.addError((error as GoogleIdentityServicesError).type);
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a `oauth2.CodeClient` used for authorization (scope) requests.
|
||||
@ -203,10 +205,10 @@ class GisSdkClient {
|
||||
// Create a Token Client for authorization calls.
|
||||
final CodeClientConfig codeConfig = CodeClientConfig(
|
||||
client_id: clientId,
|
||||
hosted_domain: hostedDomain,
|
||||
callback: allowInterop(_onCodeResponse),
|
||||
error_callback: allowInterop(_onCodeError),
|
||||
scope: scopes.join(' '),
|
||||
hd: hostedDomain,
|
||||
callback: _onCodeResponse,
|
||||
error_callback: _onCodeError,
|
||||
scope: scopes,
|
||||
select_account: true,
|
||||
ux_mode: UxMode.popup,
|
||||
);
|
||||
@ -222,7 +224,9 @@ class GisSdkClient {
|
||||
}
|
||||
|
||||
void _onCodeError(Object? error) {
|
||||
_codeResponses.addError(getProperty(error!, 'type'));
|
||||
if (error != null) {
|
||||
_codeResponses.addError((error as GoogleIdentityServicesError).type);
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to sign-in the user using the OneTap UX flow.
|
||||
@ -238,9 +242,9 @@ class GisSdkClient {
|
||||
// Ask the SDK to render the OneClick sign-in.
|
||||
//
|
||||
// And also handle its "moments".
|
||||
id.prompt(allowInterop((PromptMomentNotification moment) {
|
||||
id.prompt((PromptMomentNotification moment) {
|
||||
_onPromptMoment(moment, userDataCompleter);
|
||||
}));
|
||||
});
|
||||
|
||||
return userDataCompleter.future;
|
||||
}
|
||||
@ -287,7 +291,7 @@ class GisSdkClient {
|
||||
Object parent,
|
||||
GSIButtonConfiguration options,
|
||||
) async {
|
||||
return id.renderButton(parent, convertButtonConfiguration(options)!);
|
||||
return id.renderButton(parent, convertButtonConfiguration(options));
|
||||
}
|
||||
|
||||
/// Requests a server auth code per:
|
||||
@ -318,11 +322,10 @@ class GisSdkClient {
|
||||
'Use `renderButton` instead. See: https://pub.dev/packages/google_sign_in_web#migrating-to-v011-and-v012-google-identity-services')
|
||||
Future<GoogleSignInUserData?> signIn() async {
|
||||
// Warn users that this method will be removed.
|
||||
domConsole.warn(
|
||||
'The google_sign_in plugin `signIn` method is deprecated on the web, and will be removed in Q2 2024. Please use `renderButton` instead. See: ',
|
||||
<String>[
|
||||
'https://pub.dev/packages/google_sign_in_web#migrating-to-v011-and-v012-google-identity-services'
|
||||
]);
|
||||
web.console.warn(
|
||||
'The google_sign_in plugin `signIn` method is deprecated on the web, and will be removed in Q2 2024. Please use `renderButton` instead. See: '
|
||||
'https://pub.dev/packages/google_sign_in_web#migrating-to-v011-and-v012-google-identity-services'
|
||||
.toJS);
|
||||
// If we already know the user, use their `email` as a `hint`, so they don't
|
||||
// have to pick their user again in the Authorization popup.
|
||||
final GoogleSignInUserData? knownUser =
|
||||
@ -331,14 +334,14 @@ class GisSdkClient {
|
||||
// user activation.
|
||||
_tokenClient.requestAccessToken(OverridableTokenClientConfig(
|
||||
prompt: knownUser == null ? 'select_account' : '',
|
||||
hint: knownUser?.email,
|
||||
login_hint: knownUser?.email,
|
||||
scope: <String>[
|
||||
..._initialScopes,
|
||||
// If the user hasn't gone through the auth process,
|
||||
// the plugin will attempt to `requestUserData` after,
|
||||
// so we need extra scopes to retrieve that info.
|
||||
if (_lastCredentialResponse == null) ...people.scopes,
|
||||
].join(' '),
|
||||
],
|
||||
));
|
||||
|
||||
await _tokenResponses.stream.first;
|
||||
@ -384,7 +387,7 @@ class GisSdkClient {
|
||||
/// Revokes the current authorization and authentication.
|
||||
Future<void> disconnect() async {
|
||||
if (_lastTokenResponse != null) {
|
||||
oauth2.revoke(_lastTokenResponse!.access_token);
|
||||
oauth2.revoke(_lastTokenResponse!.access_token!);
|
||||
}
|
||||
await signOut();
|
||||
}
|
||||
@ -431,8 +434,8 @@ class GisSdkClient {
|
||||
|
||||
_tokenClient.requestAccessToken(OverridableTokenClientConfig(
|
||||
prompt: knownUser == null ? 'select_account' : '',
|
||||
hint: knownUser?.email,
|
||||
scope: scopes.join(' '),
|
||||
login_hint: knownUser?.email,
|
||||
scope: scopes,
|
||||
include_granted_scopes: true,
|
||||
));
|
||||
|
||||
|
@ -3,11 +3,11 @@ description: Flutter plugin for Google Sign-In, a secure authentication system
|
||||
for signing in with a Google account on Android, iOS and Web.
|
||||
repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_web
|
||||
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22
|
||||
version: 0.12.2+1
|
||||
version: 0.12.3
|
||||
|
||||
environment:
|
||||
sdk: ">=3.1.0 <4.0.0"
|
||||
flutter: ">=3.13.0"
|
||||
sdk: ">=3.2.0 <4.0.0"
|
||||
flutter: ">=3.16.0"
|
||||
|
||||
flutter:
|
||||
plugin:
|
||||
@ -22,10 +22,10 @@ dependencies:
|
||||
sdk: flutter
|
||||
flutter_web_plugins:
|
||||
sdk: flutter
|
||||
google_identity_services_web: ^0.2.2
|
||||
google_identity_services_web: ^0.3.0
|
||||
google_sign_in_platform_interface: ^2.4.0
|
||||
http: ">=0.13.0 <2.0.0"
|
||||
js: ^0.6.3
|
||||
web: ">=0.3.0 <0.5.0"
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
@ -9,8 +9,5 @@
|
||||
# NOTE: camera_android is semi-excluded via special casing in the repo tools.
|
||||
# See create_all_packages_app_command.dart.
|
||||
|
||||
# Excluded until the google_sign_in_web package updates
|
||||
- google_identity_services_web
|
||||
|
||||
# This is a permament entry, as it should never be a direct app dependency.
|
||||
- plugin_platform_interface
|
||||
|
Reference in New Issue
Block a user