[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:
David Iglesias
2023-12-08 17:07:05 -08:00
committed by GitHub
parent b99ade61ec
commit 5e81fd5d67
13 changed files with 125 additions and 389 deletions

View File

@ -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.

View File

@ -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);
};
}

View File

@ -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,

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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.

View File

@ -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);
}

View File

@ -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');
}

View File

@ -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,
));

View File

@ -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:

View File

@ -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