packages

Activity, over time

Daily observations from the public event archive.

Attention

Daily stars & forks

Loading attention chart

Star accumulation

Cumulative stars

Loading star accumulation chart

Pull requests

Daily opens & merges

Loading pull requests chart

Conversations

Daily issues & comments

Loading conversations chart

Community

Daily accounts & reviews

Loading community chart

Releases

Daily releases

Loading releases chart

Mentions

4posts · 30d
Nov 40 posts
  1. Fix Google Sign-In [28444] in Flutter with google_sign_in 7 --- title: Fix Google Sign-In [28444] in Flutter with google_sign_in 7 published: true date: 2026-10-29 00:00:00 UTC tags: flutter,dart,android,firebase canonical_url: saymyframe.com --- Since version 7, google_sign_in signs users in on Android through Credential Manager, and a broken setup shows up as one of a few messages. The best known is [28444] Developer console is not set up correctly, and the checklists for it usually start with the SHA-1. I reproduced each case on an emulator, and with current Google Play services a missing SHA-1 doesn't give the app this error at all. The app gets a canceled with Account reauth failed instead. 28444 showed up when the app sent the wrong client ID. Everything below was checked on an Android 17 emulator with Google Play services 26.41, google_sign_in 7.2.0 and google_sign_in_android 7.2.18, against a Firebase project. Before version 7, the plugin reported a broken setup as ApiException: 10, the DEVELOPER_ERROR of Google Play services. What each Google Sign-In error means GoogleSignIn.instance.authenticate() throws a GoogleSignInException. The code and the description tell the cases apart: | Description | Code | What is wrong | | --- | --- | --- | | [28444] Developer console is not set up correctly. | unknownError | Google doesn't take the serverClientId as the web client of the app: it is the ID of the Android client, or a client that doesn't exist | | serverClientId must be provided on Android | clientConfigurationError | The app has no web client ID at all: google-services.json has none, or the release build dropped it | | [16] Account reauth failed. | canceled | No Android OAuth client with the package name of the app and the SHA-1 of the key that signed it, or no connection to Google | | [16] Cancelled by user. | canceled | The user closed the sheet, or backed out of adding a Google account | The two cases of canceled share a code. The README of the plugin warns about this: it can't tell some setup errors from a user who cancelled. attemptLightweightAuthentication() hides a missing SHA-1 completely and just returns null. logcat prints GetCredentialResponse error returned from framework for every failure, a plain cancel included, so that line doesn't point to the cause. And on a device or an emulator without a Google account, the sign-in never gets far enough to fail: Credential Manager opens the screen that adds an account. Add one before you test the setup. See the real cause in logcat Google Play services logs what its server answered. Filter logcat to its two tags while you try to sign in: adb logcat -s Auth:W Auth.Api.Credentials:W For a missing SHA-1, the first lines are: W Auth : [GetTokenResponseHandler] Server returned error: This android application is not registered to use OAuth2.0, please confirm the package name and SHA-1 certificate fingerprint match what you registered in Google Developer Console. W Auth.Api.Credentials: cwsp: [8] Unknown error [status=UNREGISTERED_ON_API_CONSOLE]. W Auth.Api.Credentials: cwsp: [16] Account reauth failed. For a wrong client ID, the same tags show 28444: Developer console is not set up correctly. [28444] Developer console is not set up correctly: the wrong client ID To give the app an ID token, Google needs the client ID of a web application OAuth client, which google_sign_in sends as serverClientId. When Google doesn't accept that ID, the app gets: GoogleSignInException(code GoogleSignInExceptionCode.unknownError, [28444] Developer console is not set up correctly., null) I got it in two ways. The first was passing the ID of the Android client to initialize(): a Firebase project has both kinds, and they look alike, <number>-<letters>.apps.googleusercontent.com. The second was an ID that doesn't exist, as with a typo or a client deleted in the Google Cloud console. In google-services.json, the web client is the one with "client_type": 3, and the Android clients have "client_type": 1. To print the web client ID: grep -B1 '"client_type": 3' android/app/google-services.json | grep -m1 client_id If you pass the ID yourself, pass that one: await GoogleSignIn.instance.initialize( serverClientId: '1234567890-abc123.apps.googleusercontent.com', ); With a google-services.json that has a web client, you don't need to pass anything: the plugin reads the ID from the resources that the Google services Gradle plugin generates from that file. The checklists aren't wrong to mention the SHA-1, though. Inside Google Play services, a missing SHA-1 is 28444 too: logcat shows cwsp: [28444] when attemptLightweightAuthentication() runs into one, even though the app only gets null. I can't rule out that other versions of Play services pass it on to the app. If the client ID is right, check the SHA-1 next. Account reauth failed: the SHA-1 of the signing key is missing Google recognizes an Android app by its package name and the SHA-1 of the certificate that signed it. With Firebase, adding a SHA fingerprint to the Android app creates the Android OAuth client that Google looks for. When there is none, the user picks an account and the app gets: GoogleSignInException(code GoogleSignInExceptionCode.canceled, [16] Account reauth failed., null) The same message shows up in two other cases. When the device can't reach Google (on the emulator I turned off Wi-Fi and mobile data), logcat shows [7] Network error. right before it. And in flutter/flutter#190296, an app with a correct setup got it on every sign-in after the first one. The reporter fixed it by calling GoogleSignIn.instance.signOut() on sign-out. I tried that on the same emulator: eight sign-ins in a row without signOut(), some right after the app's own sign-out, and all of them worked. So it may depend on something that report had and my setup didn't, such as a physical device, an older plugin or an app installed from Google Play. logcat tells the cases apart: a missing SHA-1 shows not registered to use OAuth2.0, and no network shows Network error. Find the SHA-1 of the build that fails Gradle prints the key that signs each build type, and it is not always the one you expect: cd android && ./gradlew signingReport Variant: release Config: debug Store: /Users/you/.android/debug.keystore Alias: AndroidDebugKey SHA1: 60:45:3E:2E:7F:82:01:EC:F4:84:F8:43:A2:44:70:F9:4D:F9:7B:F5 A new Flutter app signs its release build with the debug key too (signingConfig = signingConfigs.getByName("debug") in android/app/build.gradle.kts), until you give it a key of its own. From then on, the release needs that key's SHA-1. For an APK you already have, read the certificate from the file with apksigner from the Android SDK build tools: $ANDROID_HOME/build-tools/<version>/apksigner verify --print-certs build/app/outputs/flutter-apk/app-release.apk keytool -printcert -jarfile app.apk from Google's client authentication guide prints Not a signed jar file for a Flutter APK. Flutter sets minSdk to 24, and from that level on, the APK has only a v2 signature, which keytool doesn't read. Some keys are easy to forget. Every machine has its own ~/.android/debug.keystore, so a teammate's debug build has a different SHA-1, and so does a CI runner that creates a fresh key. Google Play adds two more: your upload key, and the app signing key. With Play App Signing, users install a build that Google signed with its own key, and its SHA-1 is in the Play Console, under Test and release > App integrity > App signing. Add the SHA-1 to Firebase In the Firebase console, open Project settings, pick the Android app under Your apps and click Add fingerprint. Add the SHA-256 too. The Firebase CLI does the same, with the app ID from firebase apps:list ANDROID: firebase apps:android:sha:create <appId> <sha1> You don't need to download google-services.json again for this: the app never reads the Android clients from it. In my test, the same build signed in about 40 seconds after I added the SHA-1, with the old file. Every SHA you add creates another Android OAuth client in the project, and Google caps the number of them. Removing a SHA from Firebase doesn't remove its client. Once the project is full, apps:android:sha:create fails with Failed to create SHA certificate hash, and only --debug shows the answer of the API: 429 Number of OAuth clients created exceeded limit. I ran into that because the CI of SMF registered the fresh debug key of every runner. Give CI one debug keystore that doesn't change, and register its SHA-1 once. serverClientId must be provided on Android: no web client in google-services.json When you don't pass serverClientId, google_sign_in_android reads the string resource default_web_client_id. The Google services Gradle plugin generates it from the web client in android/app/google-services.json. Without it, authenticate() fails at once, before any Google screen: GoogleSignInException(code GoogleSignInExceptionCode.clientConfigurationError, serverClientId must be provided on Android, null) The usual cause is a google-services.json downloaded before Google sign-in was enabled in Firebase Authentication. That file has "oauth_client": [], and the build gets no default_web_client_id. Check your file: grep -c '"client_type": 3' android/app/google-services.json 0 means no web client. Enable Google in the Firebase console, under Authentication > Sign-in method, then download the file again, from Project settings or with the Firebase CLI: firebase apps:sdkconfig ANDROID <appId> --out android/app/google-services.json Also check that android/app/build.gradle.kts applies the com.google.gms.google-services plugin, which FlutterFire CLI adds when it configures the app. Without it, nothing generates the resource. Works in debug, fails in release When Google Sign-In works in debug builds and fails in the release, the message tells you which of two things happened. If the release fails with [16] Account reauth failed., Firebase doesn't know the key of the release: your release key, or, for a build from Google Play, the app signing key of Google Play. Add its SHA-1 as above. If it fails with serverClientId must be provided on Android, the release build lost default_web_client_id. Flutter shrinks the resources of a release build by default, and the plugin looks the string up by its name at run time, which the shrinker doesn't see. Up to google_sign_in_android 7.2.15, the shrinker removed it from every app (flutter/flutter#188363). Version 7.2.16 added a keep rule in res/raw/keep.xml of the plugin (flutter/packages#12075). That rule has a catch (flutter/flutter#194943, open as I write this, with two pull requests that rename the plugin's file under review). If your app has its own android/app/src/main/res/raw/keep.xml, for example to keep a notification icon, Gradle merges the resources by file name, and the app's file replaces the plugin's. With 7.2.18, the release build then fails with serverClientId must be provided on Android. The shrinker says why: grep default_web_client_id build/app/outputs/mapping/release/resources.txt string:default_web_client_id:2131689529 is not reachable. Rename your file, for example to res/raw/app_keep.xml. The Android guide to keeping resources asks for unique names for exactly this reason, and the shrinker reads every keep file in res/raw. After the rename, the same line says reachable from keep xml file, and sign-in works. You can also add @string/default_web_client_id to the tools:keep of your own file. Either way, check that the app has 7.2.16 or later: flutter pub deps | grep google_sign_in_android A CI that builds only debug never sees either case. The same thing happened with the shared_preferences release build crash, where R8 broke a plugin only in release builds, so start a release build on an emulator before your users do. How SMF sets up Google Sign-In with Firebase I went through all of this while building the google_sign_in module of SMF, a free, open-source Flutter project generator. In an app with the firebase_auth module, SMF generates a script, tool/enable_firebase_sign_in.dart, and runs it after flutterfire configure once you agree. The script enables Google in the Firebase project, registers the SHA-1 and SHA-256 of the debug key of your machine, downloads google-services.json again with the web client in it, and writes the URL scheme of the iOS app. So a debug build signs in with Google from the first run, and a teammate runs the script once on their own machine. The keys of a release stay a step you do by hand, with the commands in the README of the app.

  2. Flutter's official Liquid glass seems to be coming soon, using the community's liquid_glass_renderer package github.com/flutter/packages/commit/c68226720402b5…

  3. Flutter's official Liquid glass seems to be coming soon, using the community's liquid_glass_renderer package github.com/flutter/packages/commit/c68226720402b5…

    1 like · 39 views
  4. DEV · · @niamtokik

    Q&D: Using WebView with Flutter Your website is already working well, compatible with mobile device? Why recreating the wheel, and simply recreate your application around the WebView feature? The idea is simple, use the native Android (or iOS) browser inside your application. It will not fix everything though, but you will be able to reuse your website without rewriting everything from scratch. Another purpose of WebView objects is to create an interface to a platform without public API. Indeed, with WebView you will have access to a real web browser capabilities, including executing requests on a remote service, storing cookies or executing Javascript script. on Dart/Flutter, the webview_flutter package is required. $ flutter pub add webview_flutter Resolving dependencies... Downloading packages... cupertino_icons 1.0.9 (2.0.0 available) material_color_utilities 0.13.0 (0.13.1 available) meta 1.18.3 (1.19.0 available) + plugin_platform_interface 2.1.8 test_api 0.7.12 (0.7.15 available) vector_math 2.4.2 (2.4.3 available) + webview_flutter 4.14.1 + webview_flutter_android 4.14.1 + webview_flutter_platform_interface 2.15.1 + webview_flutter_wkwebview 3.27.0 Changed 5 dependencies! 5 packages have newer versions incompatible with dependency constraints. Try `flutter pub outdated` for more information. WebView objects are objects to display web content (usually website) inside an application. This kind of object can be found on Android and on iOS, both implementation offering similar (but not perfectly identical) features. When one WebView object is instantiated, it can be used like a browser where a part of its interfaces are exposed to the application. The best way to demonstrate this is to create a WebViewController object. The one presented in the example should do the job for now (with few modification): - The setJavaScriptMode method is called, it can be set to disabled or unrestricted; - The setNavigationDelegate method is called, and a NavigationDelegate object is created. This last one is used to accept or reject the navigation changes. The following callback functions can be configured: - onNavigationRequest: executed to accept/deny navigation; - onPageFinished: executed when the page is fully loaded; - onPageStarted: executed when the page loading started; - onProgress: executed during the page loading progression; - onWebResourceError: executed when an error occurs. - The cacheClear method is called: use the remove the cache from the WebView; - The clearLocalStorage method is called: used to clear the local storage from the WebView; - The loadRequest method is called: used to load an URL. WebViewController controller({ String target = 'https://authenticationtest.com/simpleFormAuth/' }) { // parse an URL from a string final url = Uri.parse(target); // Return a WebViewController Object with custom callbacks // and parameters return WebViewController() // set the javascript mode to unrestricted (javascript // can be executed) ..setJavaScriptMode(JavaScriptMode.unrestricted) // configure the authorized navigation pattern ..setNavigationDelegate( NavigationDelegate( // a progression gauge can be created if necessary onProgress: (int progress) { print("loading in progress ($progress)..."); }, // when the connection to the page start onPageStarted: (String url) { print("page start to be loaded ($url)."); }, // when the page is totally loaded onPageFinished: (String url) { print("page loaded successfully ($url)."); showCookies(); }, // what to do in case of error onHttpError: (HttpResponseError error) { }, // this callback is used to select what kind of request // is allowed (to which website). onNavigationRequest: (NavigationRequest request) { return NavigationDecision.navigate; })) // when a new object is created, clear up the cache ..clearCache() // clear also the local storage ..clearLocalStorage() // and finally, load the requested url ..loadRequest(url); } With this future object created, it is possible to include it in the flutter tree. To that, one can use a WebViewWidget object, with the previous controller passed as argument. @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: .center, children: [ Container( child: Row(children: []) ), Expanded(child: WebViewWidget(controller: _controller == null ? controller() : _controller!)) ], ), ), ); } Some variable states can eventually store the reference of the current controller, initialized in the initState() method. This code was defined inside a StatefulWidget Widget. WebViewController? _controller; @override initState() { super.initState(); _controller = controller(); } Here the log output during the loading. Yeah, the authenticationtest.com was used to retrieve some session cookies. I/flutter (19018): page start to be loaded (https://authenticationtest.com/simpleFormAuth/). I/flutter (19018): loading in progress (20)... I/flutter (19018): loading in progress (20)... I/flutter (19018): loading in progress (29)... I/flutter (19018): loading in progress (70)... I/flutter (19018): loading in progress (87)... I/flutter (19018): loading in progress (100)... I/flutter (19018): loading in progress (100)... I/flutter (19018): loading in progress (100)... I/flutter (19018): page loaded successfully (https://authenticationtest.com/simpleFormAuth/) The cookies from the WebView can also be retrieved (and if needed stored in a local store). This can be achieved by created a new WebViewCookieManager object. After having been successfully instantiated, the cookies can be exported using the getCookies() method. WebViewController? _controller; @override initState() { super.initState(); _controller = controller(); _cookieManager = WebViewCookieManager(); } Future<void> showCookies() async { final currentUrl = await _controller?.currentUrl() ?? ''; final Uri? domain = Uri.tryParse(currentUrl); final cookies = await _cookieManager?.getCookies( domain: domain! ); print(cookies); } Here the log output when some cookies have been received from the website. Those cookies are using the WebViewCookie object, containing 4 fields, name, value, domain and path. The other cookies attributes like expires are not available on Android (but it seems they can be found on iOS). I/flutter (19018): [WebViewCookie{name: PHPSESSID, value: 8035d936660419d25f1969de60945682, domain: https://authenticationtest.com/simpleFormAuth/, path: /}] Here a screenshot of the WebView from the Android emulator. Few buttons have been added to do tests, like extract the cookies and save same in a permanent store, or switch to another URL. !WebView Flutter Screenshot from Android Application WebView is a really interesting feature to bypass some website limitations, especially the one with public API only. Collecting those cookies can then be reused afterward. Some of you would probably ask why I was playing with this package. Well, I needed to fetch some cookies from another website (not the one used here for test), on one of my project. Having access to the cookies will help me to query my target when authenticated. Anyway, here some interesting links if you want to know more: - webview_flutter package from pub.dev; - The official webview_flutter API documentation on pub.dev; - The webview_flutter source code from github; - The official WebView documentation from the Android developer website; - The official mkWebView documentation from the Apple developer website; - cURL HTTP cookies documentation where the cURL cookies format is explained; - The RFC6265 where the set-cookie is explained, containing also the official cookies attributes with their specifications; - Starting with Flutter: How to embed a webview by @theotherdevs; - Converting any website into an app: Flutter webview by @franciscodex; - Not Just a WebView: Building a Native Engine on Flutter to Convert Sites to Apps with SDUI by @__2ea5fee000c; - Flutter: Two-way communication between a WebView and a web page by @diamantidis; - Is there a way to show more cookies fields from webview_flutter's WebViewCookieManager on StackOverflow, where I asked a question regarding the extra cookies attributes. As usual, have fun and happy hacking! ---- The stackoverflow user who answered me on my question found a bug in the parser (I missed it), when a value is set to base64, it returns an empty string. In fact, while I was reading the code, I did not see any limits on the size of the key or the value. This should also be tested, a malicious server could eventually overflow an application with badly crafted cookies. This protected could be implemented on the WebView (Kotlin) part though and not in the flutter interface. Anyway, I will need a way to reproduce (at least) the following bug. Notes from Mozilla documentation: > A <cookie-name> can contain any US-ASCII characters except for control characters (ASCII characters 0 up to 31 and ASCII character 127) or separator characters (space, tab and the characters: ( ) < > @ , ; : \ " / [ ] ? = { } > > A <cookie-value> can optionally be wrapped in double quotes and include any US-ASCII character excluding control characters (ASCII characters 0 up to 31 and ASCII character 127), Whitespace, double quotes, commas, semicolons, and backslashes. > > Encoding: Many implementations perform percent-encoding on cookie values. However, this is not required by the RFC specification. The percent-encoding does help to satisfy the requirements of the characters allowed for <cookie-value>. 1. set a cookie with a base64 value (e.g. dGVzdAo=) 2. fetch the cookie as WebCookie, the value returned is """ instead of "dGVzdAo=" This issue is due to the current implementation of the parser used in the AndrdoiWebViewCookieManager implementation. import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; // code exfiltred from official sources poc(String cookies) { List<dynamic> store = []; for (final String cookie in cookies.split('; ')) { final List<String> cookieValue = cookie.split('='); store.add((cookieValue.first, cookieValue.last)); } return store; } void main() { test("poc", () { print(poc("test=dGVzdAo")); expect(poc("test=dGVzdAo"), ("test", "dGVzdAo")); print(poc("test=dGVzdAo=")); expect(poc("testdGVzdAo="), ("test", "dGVzdAo=")); }); } $ flutter test ... 00:00 +0: poc [(test, dGVzdAo)] 00:01 +0 -1: poc [E] Expected: (String, String):<(test, dGVzdAo)> Actual: [(String, String):(test, dGVzdAo)] package:matcher expect package:flutter_test/src/widget_tester.dart 473:18 expect test/widget_test.dart 25:5 main.<fn> ... Here a quick nginx configuration: events { worker_connections 2000; } http { server { listen 4001; add_header Set-Cookie "test=dGVzdAo=; Path=/"; return 200; } } Thanks to Mahendra Dhakad](stackoverflow.com) on stackoverflow. ---- Cover Image by <a href="unsplash.com">Paul Skorupskas</a> on <a href="unsplash.com">Unsplash</a>

Latest stargazers

The last 10 distinct accounts we observed this year.

Most active participants

PR, issue, comment & review events in the past year.

Packages

No packages found.

README badges

Embed a badge in your README or website.

Live mentions · last 30 days
flutter/packages mentions in the last 30 days