-
Notifications
You must be signed in to change notification settings - Fork 28
Fix/upgrade dependencies google sign #143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Fix/upgrade dependencies google sign #143
Conversation
WalkthroughUpdates Gradle wrapper to 8.7 and Android Gradle Plugin to 8.6.0, migrates google_sign_in from v6 to v7 with API changes to both authentication implementations, and upgrades multiple Flutter dependencies across patient and therapist apps to resolve deprecations. Changes
Sequence Diagram(s)sequenceDiagram
participant App
participant GoogleSignIn v7
participant Supabase
rect rgb(240, 248, 255)
Note over App,Supabase: New Google Sign-In v7 Flow
end
App->>GoogleSignIn v7: initialize(clientId)
GoogleSignIn v7-->>App: initialized
App->>GoogleSignIn v7: authenticate(scopeHint: scopes)
GoogleSignIn v7-->>App: googleUser
rect rgb(200, 230, 201)
Note over App,GoogleSignIn v7: Token Retrieval (Changed)
end
App->>GoogleSignIn v7: authorizationClient.authorizeScopes(scopes)
GoogleSignIn v7-->>App: accessToken
App->>GoogleSignIn v7: googleUser.authentication
GoogleSignIn v7-->>App: idToken
rect rgb(255, 243, 224)
Note over App,Supabase: Sign-In with Retrieved Tokens (New)
end
App->>Supabase: signInWithIdToken(idToken, accessToken)
Supabase-->>App: authenticated session
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (2)
patient/lib/provider/auth_provider.dart (2)
118-122: Add null-safety check for authorization result.The
authorizeScopes()call returns an authorization object, but there's no null check before accessingauthorization.accessToken. While this may throw an exception if null, an explicit check with a meaningful error message would improve debugging.// Authorization to get access token final authorization = await googleUser.authorizationClient.authorizeScopes( scopes ); +if (authorization == null) throw 'Authorization failed'; final String accessToken = authorization.accessToken;
116-116: Use typed exceptions instead of string literals.Throwing string literals (lines 116, 127) makes error handling less structured and harder to catch specifically. Consider defining custom exception classes or using existing exception types.
-if (googleUser == null) throw 'Sign in cancelled'; +if (googleUser == null) throw Exception('Sign in cancelled');-if (idToken == null) throw 'No ID Token found'; +if (idToken == null) throw Exception('No ID Token found');Also applies to: 127-127
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
patient/android/gradle/wrapper/gradle-wrapper.properties(1 hunks)patient/android/settings.gradle(1 hunks)patient/lib/provider/auth_provider.dart(2 hunks)patient/pubspec.yaml(2 hunks)therapist/android/gradle/wrapper/gradle-wrapper.properties(1 hunks)therapist/lib/presentation/therapy_goals/widgets/therapy_dotted_empty_container.dart(1 hunks)therapist/lib/repository/supabase_auth_repository.dart(1 hunks)therapist/pubspec.yaml(3 hunks)
🔇 Additional comments (9)
patient/android/gradle/wrapper/gradle-wrapper.properties (1)
5-5: LGTM: Gradle wrapper upgrade aligns with AGP update.The Gradle wrapper upgrade to 8.7 is compatible with Android Gradle Plugin 8.6.0 (updated in settings.gradle) and represents a standard incremental update within the Gradle 8.x series.
patient/android/settings.gradle (2)
21-21: AGP 8.6.0 upgrade looks good.The Android Gradle Plugin upgrade from 8.2.1 to 8.6.0 aligns with the Gradle 8.7 wrapper update and is a stable, well-supported version released in September 2024.
21-22: Verify the build locally to confirm plugin compatibility with Kotlin 2.1.0.The version alignment is correct: AGP 8.6.0 is compatible with Kotlin 2.1.0, and Gradle 8.7 with Flutter 3.6.0+ supports this configuration. However, compatibility depends on binary libraries and plugins—some compiled with a different Kotlin metadata version can cause "incompatible Kotlin" errors.
Before merging, run a local build (
flutter clean && flutter build apk) to confirm none of your dependencies (supabase_flutter, google_sign_in, flutter_gemini, etc.) trigger Kotlin metadata mismatches. If errors occur, update the affected dependencies or align the Kotlin version.therapist/android/gradle/wrapper/gradle-wrapper.properties (1)
5-5: LGTM: Consistent Gradle wrapper upgrade.The Gradle wrapper upgrade to 8.7 matches the patient app configuration and aligns with the Android Gradle Plugin updates across the project.
therapist/lib/presentation/therapy_goals/widgets/therapy_dotted_empty_container.dart (1)
38-43: LGTM: Correct migration to dotted_border v3 API.The DottedBorder widget has been correctly updated to use the v3.0+ API with
RoundedRectDottedBorderOptions. The radius and color properties are properly configured within the options object, maintaining the same visual appearance as before.therapist/lib/repository/supabase_auth_repository.dart (1)
132-141: LGTM: Explicit user ID assignment improves data integrity.Explicitly setting
data['id'] = currentUser.idbefore the insert ensures the therapist record is correctly linked to the authenticated user. This is a good practice that prevents potential issues with Supabase RLS policies and foreign key constraints.patient/lib/provider/auth_provider.dart (1)
109-110: The scope values are correct for google_sign_in v7.0 — no changes needed.The google_sign_in (v7.x) package accepts the OpenID scopes "openid", "email", and "profile", so the use of
['email', 'profile']is valid and properly formatted for this package version.therapist/pubspec.yaml (1)
45-45: No critical breaking changes require code modifications.Based on verification:
- flutter_dotenv 6.0: testLoad() → loadFromString() rename — codebase does not use testLoad(); safe.
- get_it 9.0: strictDisposalOrder parameter removed; disposal order now strict LIFO — codebase does not use strictDisposalOrder; no custom service disposal logic depends on disposal order; safe.
- intl 0.20.0: Dart SDK ^3.3 required — project specifies
sdk: '>=3.6.0 <4.0.0', meeting the requirement; DateFormat usage unaffected by type tightening; safe.- flutter_lints 6.0: Additive lint rules only; not code-breaking; safe.
- flutter_tts 4.2.3: Minor version (not major upgrade); no documented breaking changes; safe.
patient/pubspec.yaml (1)
60-62: Remove unused carousel_slider dependency from pubspec.yaml or confirm retention is intentional.Verification found that all breaking change migrations for actively-used packages have been completed:
- flutter_dotenv 6.0: Using modern
dotenv.load()API (not deprecatedtestLoad()) in both main.dart files at startup; no code changes required.- get_it 9.0: Using modern
registerSingleton<Type>()andgetIt<Type>()patterns; nostrictDisposalOrderparameter references found; LIFO disposal order change is transparent.- flutter_lints 6.0: Version bump only; no functional code changes needed.
However, carousel_slider (^5.1.1) is listed in pubspec.yaml but has no package imports in the codebase—only custom
_buildCarouselItem()method names. This appears to be a dead dependency that was added during the upgrade but is not integrated. Confirm whether it should be removed or retained.
| Future<void> _handleMobileSignIn() async { | ||
| final webClientId = dotenv.env['GOOGLE_WEB_CLIENT_ID'] ?? | ||
| (throw Exception("WEB_CLIENT_ID not found in .env")); | ||
| final webClientId = dotenv.env['GOOGLE_WEB_CLIENT_ID'] ?? | ||
| (throw Exception("WEB_CLIENT_ID not found in .env")); | ||
| final iosClientId = dotenv.env['GOOGLE_IOS_CLIENT_ID']; | ||
|
|
||
| final GoogleSignIn googleSignIn = GoogleSignIn( | ||
|
|
||
| // Get the singleton instance. | ||
| final GoogleSignIn googleSignIn = GoogleSignIn.instance; | ||
|
|
||
| // Initialize the GoogleSignIn instance. | ||
| await googleSignIn.initialize( | ||
| clientId: Platform.isIOS ? iosClientId : null, | ||
| serverClientId: webClientId, | ||
| scopes: ['email', 'profile'], | ||
| ); | ||
|
|
||
| final GoogleSignInAccount? googleUser = await googleSignIn.signIn(); | ||
| if (googleUser == null) throw 'Sign in cancelled'; | ||
| // Define scopes (they were moved from the constructor/initialize to the sign-in call). | ||
| const List<String> scopes = ['email', 'profile']; | ||
|
|
||
| final GoogleSignInAuthentication googleAuth = | ||
| await googleUser.authentication; | ||
| // Sign-in logic | ||
| final GoogleSignInAccount? googleUser = await googleSignIn.authenticate( | ||
| scopeHint: scopes, | ||
| ); | ||
| if (googleUser == null) throw 'Sign in cancelled'; | ||
|
|
||
| if (googleAuth.idToken == null) throw 'No ID Token found'; | ||
| if (googleAuth.accessToken == null) throw 'No Access Token found'; | ||
| // Authorization to get access token | ||
| final authorization = await googleUser.authorizationClient.authorizeScopes( | ||
| scopes | ||
| ); | ||
| final String accessToken = authorization.accessToken; | ||
|
|
||
| // Authentication (It now synchronous as per v7.0+). | ||
| final GoogleSignInAuthentication googleAuth = googleUser.authentication; | ||
| final String? idToken = googleAuth.idToken; | ||
| if (idToken == null) throw 'No ID Token found'; | ||
|
|
||
| // Supabase login (now with the retrieved accessToken) | ||
| await supabase.auth.signInWithIdToken( | ||
| provider: OAuthProvider.google, | ||
| idToken: googleAuth.idToken!, | ||
| accessToken: googleAuth.accessToken, | ||
| idToken: idToken, | ||
| accessToken: accessToken, | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Significant code duplication with therapist authentication.
The _handleMobileSignIn() method is duplicated in therapist/lib/repository/supabase_auth_repository.dart with identical logic (lines 76-116). Consider extracting this into a shared authentication utility to maintain DRY principles and ensure consistent behavior across both apps.
Create a shared authentication helper:
// In a shared package or common directory
class GoogleSignInHelper {
static Future<({String idToken, String accessToken})> signInMobile({
required String webClientId,
String? iosClientId,
}) async {
final GoogleSignIn googleSignIn = GoogleSignIn.instance;
await googleSignIn.initialize(
clientId: Platform.isIOS ? iosClientId : null,
serverClientId: webClientId,
);
const List<String> scopes = ['email', 'profile'];
final GoogleSignInAccount? googleUser = await googleSignIn.authenticate(
scopeHint: scopes,
);
if (googleUser == null) throw Exception('Sign in cancelled');
final authorization = await googleUser.authorizationClient.authorizeScopes(scopes);
final String accessToken = authorization.accessToken;
final GoogleSignInAuthentication googleAuth = googleUser.authentication;
final String? idToken = googleAuth.idToken;
if (idToken == null) throw Exception('No ID Token found');
return (idToken: idToken, accessToken: accessToken);
}
}Then use it in both files:
Future<void> _handleMobileSignIn() async {
final webClientId = dotenv.env['GOOGLE_WEB_CLIENT_ID'] ??
(throw Exception("WEB_CLIENT_ID not found in .env"));
final iosClientId = dotenv.env['GOOGLE_IOS_CLIENT_ID'];
final tokens = await GoogleSignInHelper.signInMobile(
webClientId: webClientId,
iosClientId: iosClientId,
);
await supabase.auth.signInWithIdToken(
provider: OAuthProvider.google,
idToken: tokens.idToken,
accessToken: tokens.accessToken,
);
}| // Get the singleton instance. | ||
| final GoogleSignIn googleSignIn = GoogleSignIn.instance; | ||
|
|
||
| // Initialize the GoogleSignIn instance. | ||
| await googleSignIn.initialize( | ||
| clientId: Platform.isIOS ? iosClientId : null, | ||
| serverClientId: webClientId, | ||
| scopes: ['email', 'profile'], | ||
| ); | ||
|
|
||
| final GoogleSignInAccount? googleUser = await googleSignIn.signIn(); | ||
| if (googleUser == null) throw 'Sign in cancelled'; | ||
| // Define scopes (they were moved from the constructor/initialize to the sign-in call). | ||
| const List<String> scopes = ['email', 'profile']; | ||
|
|
||
| final GoogleSignInAuthentication googleAuth = | ||
| await googleUser.authentication; | ||
| // Sign-in logic | ||
| final GoogleSignInAccount? googleUser = await googleSignIn.authenticate( | ||
| scopeHint: scopes, | ||
| ); | ||
| if (googleUser == null) throw 'Sign in cancelled'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify the Google Sign-In v7 authentication flow.
The migration to google_sign_in v7 uses authenticate() with scopeHint, but this pattern should be verified against the official migration guide. In v7.0+, the typical flow is:
signIn()was replaced byauthenticate()for initial sign-inauthorizeScopes()is typically used for incremental authorization (requesting additional scopes after initial sign-in)
The current implementation calls both authenticate() and then immediately authorizeScopes(), which may be redundant. Please verify:
- Whether
authenticate()already returns tokens that can be accessed directly - If the
scopeHintparameter is the correct API for passing scopes toauthenticate() - Whether
authorizeScopes()is necessary immediately afterauthenticate()for the initial sign-in flow
🌐 Web query:
google_sign_in Flutter package version 7.0 migration guide authenticate method
💡 Result:
Summary — what changed in google_sign_in v7.x
- signIn() was replaced by authenticate(); signInSilently() → attemptLightweightAuthentication(). Authentication (who the user is) is separate from authorization (OAuth scopes / access tokens). [1][2][3]
Quick migration steps (minimal)
-
Initialize once:
await GoogleSignIn.instance.initialize();. [1] -
Lightweight / silent:
await GoogleSignIn.instance.attemptLightweightAuthentication(); // no UI if possible. [1] -
User-initiated sign-in UI:
final GoogleSignInAccount account = await GoogleSignIn.instance.authenticate();
// may throw GoogleSignInException if cancelled. [1][4] -
Request OAuth tokens (authorization) only after authentication:
// request scopes / tokens from the account’s authorization client
final authorized = await account.authorizationClient.authorizeScopes(desiredScopes);
final accessToken = authorized.accessToken;
// Use authorized.idToken / serverAuthCode depending on platform and flow. [1][2] -
Firebase / signInWithCredential:
- Use idToken / accessToken from the authorization step to create credentials for Firebase (e.g. GoogleAuthProvider.credential). Ensure you request appropriate token (idToken for Firebase auth). Manage server codes/tokens on your backend when using serverAuthCode. [1][5]
Platform notes / gotchas
- On web the plugin removed the old signIn behavior; web requires using the Google Identity Services button or following web-specific flow and may not return idToken from authenticate — follow web migration docs. [3][6]
- On platforms where authorizationRequiresUserInteraction() is true, token requests must be started from a user interaction (button press). [1]
- authenticate() can throw GoogleSignInException (e.g., canceled) instead of returning null; handle exceptions. [4]
Minimal example
- Authenticate then authorize scopes:
final account = await GoogleSignIn.instance.authenticate();
final authorized = await account.authorizationClient.authorizeScopes(['email','profile','https://www.googleapis.com/auth/drive.readonly']);
final accessToken = authorized.accessToken;
final idToken = authorized.idToken; // if provided for that flow
// Use tokens (e.g., Firebase credential, API calls).
Sources
[1] google_sign_in v7.0.0 package docs (migration + API examples).
[2] StackOverflow — example showing authenticate() then account.authorizationClient.authorizeScopes(...) to obtain accessToken.
[3] GitHub / flutter issues & plugin notes about signIn removal and web migration.
🌐 Web query:
google_sign_in Flutter 7.0 authorizeScopes vs authenticate difference
💡 Result:
Short answer: in google_sign_in v7 the flows are separated — authenticate() (or signIn/signInSilently) performs authentication (who the user is) while authorizeScopes / authorizeServer perform OAuth authorization (granting scopes and returning an access token or server auth code). Use authenticate() to sign the user in and get profile/id info; call authorizeScopes(...) (or authorizeServer(...)) when you need accessToken or additional OAuth scopes (Drive, Gmail, backend server code exchange). [1][2][3]
Quick usage sketch:
- Sign-in (authenticate): GoogleSignIn.instance.authenticate(...) → returns GoogleSignInAccount (identity, idToken on some platforms).
- Authorize scopes: account.authorizationClient.authorizeScopes(scopes) → returns authorization object with accessToken (or server auth code via authorizeServer). [1][3]
Notes:
- On web GIS limits token lifetime and the plugin no longer auto-refreshes access tokens — you must re-request scopes when tokens expire. [1][2]
Sources:
- google_sign_in 7.0.0 README/changelog. [1]
- google_sign_in_web / package docs (scopes, GIS behavior on web). [2]
- Community explanation / example showing authenticate then authorizeScopes (StackOverflow). [3]
Fix Google Sign-In v7 authentication flow to follow the official migration pattern.
In google_sign_in v7, authentication and authorization are separated: authenticate() performs authentication (identity), while authorizeScopes() performs OAuth authorization (scopes/tokens). The current implementation has two issues:
-
Incorrect
scopeHintparameter: Theauthenticate()method should not receive scopes; the correct flow is to callauthenticate()and then callaccount.authorizationClient.authorizeScopes(scopes)to obtain OAuth tokens. -
Missing authorization step: After authentication, you must call
account.authorizationClient.authorizeScopes(desiredScopes)to request OAuth tokens and scopes. This is required for accessing tokens, not redundant.
Required fix: Remove scopeHint from authenticate() and add the authorization step:
final GoogleSignInAccount? googleUser = await googleSignIn.authenticate();
if (googleUser == null) throw 'Sign in cancelled';
final authorized = await googleUser.authorizationClient.authorizeScopes(['email', 'profile']);
final accessToken = authorized.accessToken;🤖 Prompt for AI Agents
In patient/lib/provider/auth_provider.dart around lines 100-116, the current
sign-in calls authenticate() with a scopeHint and never requests OAuth scopes;
remove the scopeHint parameter from googleSignIn.authenticate(), keep the
null-check, then call
googleUser.authorizationClient.authorizeScopes(['email','profile']) to obtain
the OAuth authorization result and extract the access token (e.g.,
authorized.accessToken) for downstream use.
| Future<void> _handleMobileSignIn() async { | ||
| final webClientId = dotenv.env['GOOGLE_WEB_CLIENT_ID'] ?? | ||
| (throw Exception("WEB_CLIENT_ID not found in .env")); | ||
| final webClientId = dotenv.env['GOOGLE_WEB_CLIENT_ID'] ?? | ||
| (throw Exception("WEB_CLIENT_ID not found in .env")); | ||
| final iosClientId = dotenv.env['GOOGLE_IOS_CLIENT_ID']; | ||
|
|
||
| final GoogleSignIn googleSignIn = GoogleSignIn( | ||
|
|
||
| // Get the singleton instance. | ||
| final GoogleSignIn googleSignIn = GoogleSignIn.instance; | ||
|
|
||
| // Initialize the GoogleSignIn instance. | ||
| await googleSignIn.initialize( | ||
| clientId: Platform.isIOS ? iosClientId : null, | ||
| serverClientId: webClientId, | ||
| scopes: ['email', 'profile'], | ||
| ); | ||
|
|
||
| final GoogleSignInAccount? googleUser = await googleSignIn.signIn(); | ||
| // Define scopes (they were moved from the constructor/initialize to the sign-in call). | ||
| const List<String> scopes = ['email', 'profile']; | ||
|
|
||
| // Sign-in logic | ||
| final GoogleSignInAccount? googleUser = await googleSignIn.authenticate( | ||
| scopeHint: scopes, | ||
| ); | ||
| if (googleUser == null) throw 'Sign in cancelled'; | ||
|
|
||
| final GoogleSignInAuthentication googleAuth = | ||
| await googleUser.authentication; | ||
| // Authorization to get access token | ||
| final authorization = await googleUser.authorizationClient.authorizeScopes( | ||
| scopes | ||
| ); | ||
| final String accessToken = authorization.accessToken; | ||
|
|
||
| if (googleAuth.idToken == null) throw 'No ID Token found'; | ||
| if (googleAuth.accessToken == null) throw 'No Access Token found'; | ||
| // Authentication (It now synchronous as per v7.0+). | ||
| final GoogleSignInAuthentication googleAuth = googleUser.authentication; | ||
| final String? idToken = googleAuth.idToken; | ||
| if (idToken == null) throw 'No ID Token found'; | ||
|
|
||
| // Supabase login (now with the retrieved accessToken) | ||
| await _supabaseClient.auth.signInWithIdToken( | ||
| provider: OAuthProvider.google, | ||
| idToken: googleAuth.idToken!, | ||
| accessToken: googleAuth.accessToken, | ||
| idToken: idToken, | ||
| accessToken: accessToken, | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
Same Google Sign-In v7 concerns as patient app.
This mobile sign-in implementation is identical to patient/lib/provider/auth_provider.dart (lines 95-135). Please refer to the review comments on that file regarding:
- Verification of the
authenticate()+authorizeScopes()pattern - OAuth scope format validation
- Typed exceptions instead of string literals
- Code duplication across patient and therapist apps
The suggestion to extract shared authentication logic into a common utility would eliminate this duplication and ensure consistent behavior.
See the detailed refactor suggestion in the review of patient/lib/provider/auth_provider.dart (lines 95-135).
🏁 Script executed:
#!/bin/bash
# Locate and display the patient app auth provider file
echo "=== Checking patient app auth file ==="
fd "auth_provider.dart" --type f 2>/dev/null | head -20
echo -e "\n=== Displaying patient/lib/provider/auth_provider.dart (lines 95-135) ==="
if [ -f "patient/lib/provider/auth_provider.dart" ]; then
sed -n '95,135p' "patient/lib/provider/auth_provider.dart"
else
echo "File not found at patient/lib/provider/auth_provider.dart"
fi
echo -e "\n=== Searching for other sign-in implementations ==="
rg -n "authenticate\s*\(" --type dart -B2 -A2 | head -50Length of output: 2510
🏁 Script executed:
#!/bin/bash
# Search for OAuth/GoogleSignIn patterns to identify code duplication
echo "=== Searching for GoogleSignIn.authenticate patterns ==="
rg -n "GoogleSignIn.*authenticate|googleSignIn\.authenticate" --type dart -B3 -A3
echo -e "\n=== Searching for authorizeScopes patterns ==="
rg -n "authorizeScopes" --type dart -B2 -A2
echo -e "\n=== Searching for shared auth utilities ==="
fd "auth" --type d | grep -i util
fd "auth" --type f -path "*/shared/*" -o -path "*/common/*" | head -20Length of output: 2319
🏁 Script executed:
#!/bin/bash
# Check exception handling patterns
echo "=== String exceptions in sign-in methods ==="
rg -n "throw\s+['\"].*['\"]" --type dart -B2 -A1 | grep -i "sign\|auth" | head -30
echo -e "\n=== Comparing file structures ==="
echo "Therapist file:"
wc -l therapist/lib/repository/supabase_auth_repository.dart 2>/dev/null || echo "Not found"
echo -e "\nPatient file:"
wc -l patient/lib/provider/auth_provider.dart 2>/dev/null || echo "Not found"Length of output: 1575
Extract shared Google Sign-In v7 authentication logic into a common utility to eliminate duplication and standardize exception handling.
Code duplication confirmed: both patient (patient/lib/provider/auth_provider.dart lines 95-135) and therapist (therapist/lib/repository/supabase_auth_repository.dart lines 76-116) apps contain identical _handleMobileSignIn() implementations. Additionally, both use string exceptions ('Sign in cancelled', 'No ID Token found') instead of typed exceptions—replace with Exception or custom exception classes. Extract the shared logic into a reusable utility (e.g., lib/shared/auth/google_sign_in_handler.dart) to maintain consistent behavior across apps.
🤖 Prompt for AI Agents
In therapist/lib/repository/supabase_auth_repository.dart around lines 76-116,
the mobile Google Sign-In logic is duplicated and throws string exceptions;
extract this logic into a shared utility (e.g.,
lib/shared/auth/google_sign_in_handler.dart) that: reads client IDs from dotenv,
initializes GoogleSignIn with platform-specific clientId and serverClientId,
requests scopes, performs the sign-in and authorization, returns a typed result
(e.g., object with idToken and accessToken) and throws typed Exceptions (or a
small custom exception class) instead of string literals; then replace the
in-file implementation with a call to the new utility and update imports in both
therapist and patient apps to use it so behavior and error handling are
centralized and consistent.
| # Voice features | ||
| speech_to_text: ^6.6.0 | ||
| flutter_tts: ^3.8.5 | ||
| speech_to_text: ^7.3.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Excellent: speech_to_text upgraded beyond target version.
The issue #142 targeted speech_to_text ^6.x.x, but this PR upgrades to ^7.3.0, which is even better as it includes more recent bug fixes and improvements. Ensure the upgrade doesn't introduce breaking changes in the speech recognition functionality.
🌐 Web query:
speech_to_text Flutter package 7.0 breaking changes migration
💡 Result:
Here are the key breaking / migration points for speech_to_text >= 7.0 and what to change (short checklist + minimal code examples).
Summary of changes
- v7.0: MacOS support + WASM web compilation; removed support for deprecated Android v1 plugin registration; updated Android build settings; improved locale / on‑device language handling. [1][2]
- v6.6.0 (important precursor): listen(...) now prefers a single SpeechListenOptions parameter (several older listen parameters were deprecated). [2][3]
What to do when upgrading to 7.x
-
Update Android embedding to V2 (remove legacy v1 registration)
- The plugin no longer supports the old (pre-Flutter‑1.12) v1 registration. Ensure your Android app uses the V2 embedding (MainActivity extends FlutterActivity and does not manually register plugins with PluginRegistry). If you have custom plugin registration code, remove/convert it to the V2 approach. (See the package changelog note about removing deprecated v1 registration.) [1]
-
Replace deprecated listen parameters with SpeechListenOptions
- Prior code like:
await speech.listen(
onResult: _onResult,
listenFor: Duration(seconds:30),
localeId: 'en_US',
partialResults: true,
cancelOnError: false,
...
); - Should be migrated to:
await speech.listen(
onResult: _onResult,
listenOptions: SpeechListenOptions(
listenFor: Duration(seconds:30),
localeId: 'en_US',
partialResults: true,
cancelOnError: false,
onDevice: false, // example field
autoPunctuation: true, // example field
enableHapticFeedback: true // example field
),
); - Use the example and API docs to see all available fields on SpeechListenOptions. [2][3]
- Prior code like:
-
Check async behavior of stop/cancel (older breaking change)
- Earlier releases made stop() and cancel() async. If you awaited stop/cancel previously or relied on synchronous behavior, be sure to await them now. (This change predates 7.0 but may affect your code.) [1]
-
Android build updates / language codes
- Update Android build settings if you see deprecation/build warnings (changelog notes updates). Also test on-device recognition with language codes — v7.0 adjusted handling for on‑device recognition and locale duplicates were improved. Verify locales() and localeId usage in your app. [1]
-
macOS and web (WASM)
- If you target macOS, add macOS platform support in your project (enable macOS in Flutter if not already). For web, 7.x adds WASM compilation support — test web builds for any JS/wasm dependency changes. [1]
-
Run & test on all target platforms
- Test initialize(), locales(), listen(), stop(), cancel() on each platform you target (Android, iOS, web, macOS). Look for differences in pause timeouts and partialResult behavior (Android may impose its own pause timeout).
Useful references (read these while migrating)
- Changelog (7.0 notes + earlier): pub.dev speech_to_text changelog. [1]
- API docs & usage (SpeechListenOptions / listen examples): package docs & example on pub.dev. [2][3]
- Repository (examples / issues if you hit platform-specific bugs): GitHub csdcorp/speech_to_text. [4]
References
[1] speech_to_text changelog (7.0 release notes).
[2] speech_to_text API docs / documentation (SpeechListenOptions & listen).
[3] speech_to_text example (example usage showing SpeechListenOptions).
[4] csdcorp/speech_to_text GitHub repo.
If you want I can:
- scan your pubspec + a small code snippet and produce the exact edits (MainActivity, listen calls) needed.
I need to verify how speech_to_text is actually used in the codebase to determine if the breaking changes in v7.0 require code updates.
🏁 Script executed:
#!/bin/bash
# Search for speech_to_text imports and usage in the therapist app
rg -n "speech_to_text|SpeechToText" --type dart -C 3Length of output: 994
🏁 Script executed:
#!/bin/bash
# Search specifically for listen() calls which may need migration
rg -nP "\.listen\s*\(" --type dart -C 5Length of output: 4576
🏁 Script executed:
#!/bin/bash
# Check for SpeechListenOptions (v7.0 new parameter structure)
rg "SpeechListenOptions" --type dartLength of output: 0
🏁 Script executed:
#!/bin/bash
# Check Android embedding version in MainActivity
fd "MainActivity" --type f -x cat {}Length of output: 261
🏁 Script executed:
#!/bin/bash
# Check Android build configuration
fd "build.gradle" --type f | xargs catLength of output: 4554
🏁 Script executed:
#!/bin/bash
# Look for any explicit platform configurations
fd "pubspec.yaml" -x cat {}Length of output: 8820
🏁 Script executed:
#!/bin/bash
# Check if there are any deprecated listen() parameter usages
rg -nP "listen\s*\(\s*onResult|listen\s*\(\s*listenFor|listen\s*\(\s*localeId" --type dartLength of output: 0
🏁 Script executed:
#!/bin/bash
# Search for any web or macOS configuration files
fd -e "web" -e "macos" --type d -C 5Length of output: 230
🏁 Script executed:
#!/bin/bash
# Check for iOS and Android platform configurations
ls -la ios/ android/ macos/ web/ 2>/dev/null || echo "Checking available directories..."Length of output: 33
🏁 Script executed:
#!/bin/bash
# Get an overview of the project structure
find . -maxdepth 2 -type d -name "lib" -o -name "android" -o -name "ios" -o -name "macos" -o -name "web" | head -20Length of output: 161
🏁 Script executed:
#!/bin/bash
# Check for migration-related comments or TODOs in the code
rg -n "TODO|FIXME|migrate|breaking" --type dart -i | head -20Length of output: 1001
🏁 Script executed:
#!/bin/bash
# Get full pubspec.yaml to understand all dependencies and platform support
cat therapist/pubspec.yamlLength of output: 4452
🏁 Script executed:
#!/bin/bash
# Check if speech_to_text is actually used by searching pubspec files
rg "speech_to_text" --type yamlLength of output: 47
🏁 Script executed:
#!/bin/bash
# Look for platform-specific implementations
find . -path "*/lib/*" -name "*.dart" -type f | xargs rg -l "SpeechToText\|speech_to_text" 2>/dev/null | head -10Length of output: 0
🏁 Script executed:
#!/bin/bash
# Check for any listen method calls in detail
rg -nPA 15 "\.listen\(" --type dart | head -50Length of output: 4189
🏁 Script executed:
#!/bin/bash
# Verify Android embedding by checking MainActivity
find . -name "MainActivity.kt" -o -name "MainActivity.java" | xargs cat 2>/dev/nullLength of output: 261
🏁 Script executed:
#!/bin/bash
# Check gradle version and settings
find . -path "*/android/*" -name "*.gradle" | xargs head -30Length of output: 4119
🏁 Script executed:
#!/bin/bash
# Look for migration issues or platform-specific code
rg -n "onDevice|autoPunctuation|enableHapticFeedback|listenOptions" --type dartLength of output: 0
🏁 Script executed:
#!/bin/bash
# Check if macOS is supported
test -d "macos" && echo "macOS platform exists" || echo "macOS platform not found"
test -d "web" && echo "web platform exists" || echo "web platform not found"Length of output: 47
🏁 Script executed:
#!/bin/bash
# Final comprehensive check - list all Dart files that might use speech_to_text
find therapist/lib -name "*.dart" -type f 2>/dev/null | xargs grep -l "speech_to_text\|SpeechToText" 2>/dev/nullLength of output: 46
🏁 Script executed:
#!/bin/bash
# Check the actual listen() implementation for any parameter passing
rg -nA 20 "await.*\.listen\(" --type dartLength of output: 1679
🏁 Script executed:
#!/bin/bash
# Search for stop() and cancel() calls
rg -nP "\.stop\(\)|\.cancel\(\)" --type dart -C 2Length of output: 3199
🏁 Script executed:
#!/bin/bash
# Check for initialize() calls which might need verification
rg -nP "\.initialize\(" --type dart -C 3Length of output: 3641
🏁 Script executed:
#!/bin/bash
# Look for locale-related code
rg -n "localeId|locales\(\)" --type dart -C 2Length of output: 440
🏁 Script executed:
#!/bin/bash
# Examine version constraints in pubspec
cat therapist/pubspec.yaml | grep -A 2 -B 2 "speech_to_text"Length of output: 69
🏁 Script executed:
#!/bin/bash
# Final check - see if there's any existing migration discussion or PRs
git log --oneline --all | grep -i "speech\|upgrade\|migrate\|7\." | head -10Length of output: 57
🏁 Script executed:
#!/bin/bash
# Check git diff to see exactly what changed
git diff HEAD -- therapist/pubspec.yamlLength of output: 0
🏁 Script executed:
#!/bin/bash
# Verify the full context of the change
git show HEAD:therapist/pubspec.yaml | grep -A 1 -B 1 "speech_to_text" || echo "Previous version check failed"Length of output: 65
🏁 Script executed:
#!/bin/bash
# Look for any error handling or try-catch around speech recognition
rg -n "try\s*{|catch|Exception" --type dart -C 3 | grep -A 5 -B 5 "listen\|speech"Length of output: 112
🏁 Script executed:
#!/bin/bash
# Check if this is an existing project with established usage
test -f "therapist/pubspec.lock" && echo "Lock file exists" && head -50 therapist/pubspec.lock | grep -A 2 "speech_to_text"Length of output: 0
🏁 Script executed:
#!/bin/bash
# Simple check - does the code actually import and use speech_to_text?
rg -c "speech_to_text" therapist/lib/ therapist/pubspec.yaml 2>/dev/null || echo "No usage found"Length of output: 73
🏁 Script executed:
#!/bin/bash
# List all files in lib directory
ls -la therapist/lib/ 2>/dev/null || ls -la lib/ 2>/dev/null || find . -maxdepth 3 -type d -name "lib"Length of output: 466
🏁 Script executed:
#!/bin/bash
# Search broadly for any speech recognition code
rg -n "speech|recognize|listen" --type dart -i | head -30Length of output: 2175
Fix required: Migrate speech_to_text listen() call to v7.0+ API
The upgrade introduces a breaking change in the listen() method signature. v6.6.0+ deprecated separate listen parameters in favor of a single SpeechListenOptions parameter.
File: therapist/lib/core/services/voice_service.dart, lines 85–98
Replace the current listen() call with the new structure:
await _speechToText.listen(
onResult: (result) {
if (result.finalResult) {
_speechController.add(result.recognizedWords);
}
},
listenOptions: SpeechListenOptions(
listenFor: const Duration(seconds: 30),
pauseFor: const Duration(seconds: 3),
partialResults: true,
localeId: "en_US",
onSoundLevelChange: (level) {
// Optional: Handle sound level changes
},
),
);Also verify the Android app uses V2 embedding (MainActivity extends FlutterActivity)—this is already correct in your setup.
🤖 Prompt for AI Agents
In therapist/lib/core/services/voice_service.dart around lines 85 to 98, the
current speech_to_text.listen() call uses the old parameter style; update it to
the v7+ API by calling _speechToText.listen with onResult as a named callback
and pass a single listenOptions: SpeechListenOptions(...) containing listenFor,
pauseFor, partialResults, localeId and optional onSoundLevelChange; ensure
inside onResult you check result.finalResult and add result.recognizedWords to
_speechController, and remove the old separate parameters so the call matches
the example structure from the review comment.
|
@mdmohsin7 Can you review this ? |
Closes #142
📝 Description
Upgraded project dependencies and migrated deprecated code for compatibility with the latest Flutter stable version.
🔧 Changes Made
✅ Checklist
📚 Reference:
https://github.com/flutter/packages/blob/main/packages/google_sign_in/google_sign_in/MIGRATION.md https://isaacadariku.medium.com/google-sign-in-flutter-migration-guide-pre-7-0-versions-to-v7-version-cdc9efd7f182
Summary by CodeRabbit
Chores
Style