Skip to content

Improve the Quickstart sample#2

Merged
brionmario merged 1 commit into
thunder-id:mainfrom
brionmario:flutter-sdk
Jul 8, 2026
Merged

Improve the Quickstart sample#2
brionmario merged 1 commit into
thunder-id:mainfrom
brionmario:flutter-sdk

Conversation

@brionmario

@brionmario brionmario commented Jul 6, 2026

Copy link
Copy Markdown
Member

Purpose

  • Update the Quickstart UI
  • Update App Icon

Approach

Screenshot 2026-07-07 at 23 17 32

Related Issues

Related PRs

  • N/A

Checklist

  • Followed the contribution guidelines.
  • Manual test round performed and verified.
  • Documentation provided. (Add links if there are any)
  • Tests provided. (Add links if there are any)
    • Unit Tests
    • Integration Tests
  • Breaking changes. (Fill if applicable)
    • Breaking changes section filled.
    • breaking change label added.

Security checks

  • Followed secure coding standards in WSO2 Secure Coding Guidelines
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

Summary by CodeRabbit

Summary

  • New Features
    • Refreshes the Quickstart app with a new landing-style sign-in flow using a draggable authentication sheet.
    • Updates the home experience to show session countdown and a refreshed token debug view (including copy-to-clipboard).
  • Bug Fixes
    • Updates Quickstart branding and loading text to match the ThunderID Quickstart flow.
    • Improves the profile screen to use the renamed user profile widget while preserving save behavior.
  • Tests
    • Updates widget tests to match the renamed widgets and reduced mocked SDK behavior.
  • Chores
    • Renames/updates public widget re-exports and refreshes sample asset/certificate handling and CI lint setup.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@brionmario, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 23 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c7d57b0-8920-4d00-b89e-a39da2006b8a

📥 Commits

Reviewing files that changed from the base of the PR and between 4552c8d and 04e410d.

⛔ Files ignored due to path filters (20)
  • samples/quickstart/android/app/src/main/res/mipmap-hdpi/ic_launcher.png is excluded by !**/*.png
  • samples/quickstart/android/app/src/main/res/mipmap-mdpi/ic_launcher.png is excluded by !**/*.png
  • samples/quickstart/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png is excluded by !**/*.png
  • samples/quickstart/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png is excluded by !**/*.png
  • samples/quickstart/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png is excluded by !**/*.png
📒 Files selected for processing (51)
  • .github/workflows/pr-builder.yml
  • AGENTS.md
  • lib/src/thunderid_client.dart
  • lib/src/widgets/callback.dart
  • lib/src/widgets/flow_form.dart
  • lib/src/widgets/language_switcher.dart
  • lib/src/widgets/loading.dart
  • lib/src/widgets/sign_in.dart
  • lib/src/widgets/sign_in_button.dart
  • lib/src/widgets/sign_out_button.dart
  • lib/src/widgets/sign_up.dart
  • lib/src/widgets/sign_up_button.dart
  • lib/src/widgets/signed_in.dart
  • lib/src/widgets/signed_out.dart
  • lib/src/widgets/thunderid_provider.dart
  • lib/src/widgets/user_dropdown.dart
  • lib/src/widgets/user_object.dart
  • lib/src/widgets/user_profile.dart
  • lib/thunderid_flutter.dart
  • pubspec.yaml
  • samples/quickstart/.gitignore
  • samples/quickstart/.metadata
  • samples/quickstart/android/.gitignore
  • samples/quickstart/android/app/build.gradle.kts
  • samples/quickstart/android/app/src/debug/AndroidManifest.xml
  • samples/quickstart/android/app/src/main/AndroidManifest.xml
  • samples/quickstart/android/app/src/main/kotlin/dev/thunderid/flutter_quickstart/MainActivity.kt
  • samples/quickstart/android/app/src/main/res/drawable-v21/launch_background.xml
  • samples/quickstart/android/app/src/main/res/drawable/launch_background.xml
  • samples/quickstart/android/app/src/main/res/values-night/styles.xml
  • samples/quickstart/android/app/src/main/res/values/styles.xml
  • samples/quickstart/android/app/src/profile/AndroidManifest.xml
  • samples/quickstart/android/build.gradle.kts
  • samples/quickstart/android/flutter_quickstart_android.iml
  • samples/quickstart/android/gradle.properties
  • samples/quickstart/android/gradle/wrapper/gradle-wrapper.properties
  • samples/quickstart/android/settings.gradle.kts
  • samples/quickstart/flutter_quickstart.iml
  • samples/quickstart/ios/Podfile
  • samples/quickstart/ios/Runner.xcodeproj/project.pbxproj
  • samples/quickstart/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • samples/quickstart/ios/Runner/Info.plist
  • samples/quickstart/lib/app.dart
  • samples/quickstart/lib/screens/auth_screen.dart
  • samples/quickstart/lib/screens/home_screen.dart
  • samples/quickstart/lib/screens/profile_screen.dart
  • samples/quickstart/pubspec.yaml
  • samples/quickstart/server.cert
  • samples/quickstart/server.key
  • test/thunderid_client_test.dart
  • test/widget_test.dart
📝 Walkthrough

Walkthrough

Renames widget exports and tests to shorter names, switches the iOS quickstart pod source to a remote Git repo, removes committed dev TLS files, adds Android quickstart project scaffolding, and rewrites the quickstart auth, home, profile, and token screens.

Changes

Widget renaming across SDK, exports, and tests

Layer / File(s) Summary
Widget file renames, re-exports, and consumer usage
AGENTS.md, lib/thunderid_flutter.dart, samples/quickstart/lib/screens/profile_screen.dart
Widget file names and public exports move to the shorter widget names, and the profile screen switches to UserProfile.
Widget test suite rename and mock trimming
test/widget_test.dart
Tests now target SignedIn, SignedOut, Loading, BaseUserObject, and LanguageSwitcher, with organization mocks and MethodChannel cases removed.

iOS SDK dependency and cert cleanup

Layer / File(s) Summary
iOS Podfile and Package.resolved updates
samples/quickstart/ios/Podfile, samples/quickstart/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
The ThunderID pod source changes from a local path to a Git URL, and SwiftPM pins the ios-sdks revision.
Remove committed dev cert/key and Xcode references
samples/quickstart/ios/Runner.xcodeproj/project.pbxproj, samples/quickstart/.gitignore
server.cert is removed from the Xcode project, and the local cert/key files are ignored.

Android quickstart project setup

Layer / File(s) Summary
Gradle and project metadata
samples/quickstart/.metadata, samples/quickstart/android/.gitignore, samples/quickstart/android/build.gradle.kts, samples/quickstart/android/settings.gradle.kts, samples/quickstart/android/gradle.properties, samples/quickstart/android/gradle/wrapper/gradle-wrapper.properties, samples/quickstart/android/flutter_quickstart_android.iml
Flutter metadata, Android ignore rules, root Gradle settings, wrapper config, properties, and IDE module files are added or updated for the sample.
Android app manifest, activity, and resources
samples/quickstart/android/app/build.gradle.kts, samples/quickstart/android/app/src/debug/AndroidManifest.xml, samples/quickstart/android/app/src/main/AndroidManifest.xml, samples/quickstart/android/app/src/profile/AndroidManifest.xml, samples/quickstart/android/app/src/main/kotlin/dev/thunderid/flutter_quickstart/MainActivity.kt, samples/quickstart/android/app/src/main/res/*
The Android app module adds its build script, manifests, activity entry point, splash drawables, and light/night theme resources.

Quickstart app UI rebrand and screen rework

Layer / File(s) Summary
App branding, display name, and ATS settings
samples/quickstart/lib/app.dart, samples/quickstart/ios/Runner/Info.plist
The app title, theme seed, loading text, display name, and ATS settings are updated.
Auth screen landing rework and modal sheet
samples/quickstart/lib/screens/auth_screen.dart
The auth screen becomes a landing page with feature tags and CTA buttons that open a modal bottom sheet, and the sheet switches between sign-in, sign-up, and recovery views.
Home screen routing and imports
samples/quickstart/lib/screens/home_screen.dart
Home navigation changes to a string-based screen router for the home, profile, and token views.
Home tab session stats and actions
samples/quickstart/lib/screens/home_screen.dart
The home tab adds a timer-driven session countdown, greeting/stat derivation, action rows, and shared row widgets.
Profile and token debug screens
samples/quickstart/lib/screens/home_screen.dart
The profile view renders identity details, and the token view fetches, decodes, formats, and copies the access token with expiry and metadata display.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant AuthScreen
    participant AuthSheet
    participant ThunderID

    User->>AuthScreen: Tap "Get started" or "Sign in"
    AuthScreen->>AuthSheet: _showSheet('signup' or 'login')
    AuthSheet->>AuthSheet: read THUNDERID_APP_ID from dotenv
    AuthSheet->>ThunderID: render SignIn/SignUp sheet
    User->>AuthSheet: Switch to recovery or back to sign-in
    AuthSheet->>AuthSheet: setState(_mode = recover/login/signup)
Loading
sequenceDiagram
    participant User
    participant HomeTabScreen
    participant ThunderIDProvider
    participant Clipboard

    User->>HomeTabScreen: Open token view
    HomeTabScreen->>ThunderIDProvider: getAccessToken()
    ThunderIDProvider-->>HomeTabScreen: access token string
    HomeTabScreen->>HomeTabScreen: decode JWT, compute expiry, format metadata
    User->>HomeTabScreen: Tap copy button
    HomeTabScreen->>Clipboard: setData(access token)
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and clearly reflects the main change: improvements to the Quickstart sample.
Description check ✅ Passed The description matches the template sections and includes purpose, issue link, and checklist, though the Approach and checklist items are mostly unfilled.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (2)
samples/quickstart/lib/screens/auth_screen.dart (2)

265-268: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Inconsistent string-quote style; escape the apostrophe instead of switching to double quotes.

Line 267 uses double quotes to sidestep escaping the apostrophe in "Don't have an account?...", while line 294 correctly escapes it with single quotes (we\'ll). As per coding guidelines, **/*.dart should "Use single quotes for all string literals."

✏️ Proposed fix
-              child: const Text("Don't have an account? Create one"),
+              child: const Text('Don\'t have an account? Create one'),

Also applies to: 293-296

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@samples/quickstart/lib/screens/auth_screen.dart` around lines 265 - 268, The
string literal style in auth_screen.dart is inconsistent with the Dart
single-quote rule: the TextButton label in the signup toggle should use single
quotes and escape the apostrophe rather than switching to double quotes. Update
the affected Text and any related nearby string literals in the signup/login UI
so they consistently follow the existing single-quote convention used elsewhere
in the file, including the strings around the _mode toggle and the "we'll" text.

Source: Coding guidelines


54-55: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use const for compile-time constant locals.

logoHeight and logoWidth are compile-time constants but declared final. As per coding guidelines, **/*.dart should "Use const constructors wherever possible, and prefer const values when the value is compile-time constant."

♻️ Proposed fix
-    final logoHeight = 72.0;
-    final logoWidth = logoHeight * (207 / 257);
+    const logoHeight = 72.0;
+    const logoWidth = logoHeight * (207 / 257);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@samples/quickstart/lib/screens/auth_screen.dart` around lines 54 - 55, The
local values in auth_screen’s build path are compile-time constants but are
declared with final; update the logo sizing locals in the relevant widget logic
to use const instead, following the same pattern used elsewhere in the Dart
codebase. Keep the existing identifiers logoHeight and logoWidth, but make them
const so the compiler can treat them as compile-time values.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@samples/quickstart/ios/Runner/Info.plist`:
- Around line 69-73: Remove the global ATS opt-out in Info.plist by replacing
NSAppTransportSecurity/NSAllowsArbitraryLoads with a scoped exception for the
local/dev host only. Update the Runner app’s ATS configuration to use
NSExceptionDomains (or the narrowest equivalent) so the quickstart can reach the
intended endpoint without enabling insecure HTTP everywhere. Keep the change
localized to the Info.plist settings in the iOS Runner sample.

In `@samples/quickstart/lib/screens/home_screen.dart`:
- Around line 525-541: The back control built with GestureDetector and the Home
row has a tap target that is too small because it only matches the icon/text
size. Update the back navigation widget in home_screen.dart to use a constrained
or padded button surface so the tappable area is at least 44x44 logical pixels,
and apply the same fix to the other matching back control referenced by the
diff.
- Around line 853-863: The JWT token display in the home screen container can
overflow because `_buildJwtRichText` is rendered directly inside a fixed-width
`Container` without horizontal scrolling. Wrap the token output in a horizontal
`SingleChildScrollView` the same way the payload section is handled, and apply
the same fix anywhere the token renderer is used in the referenced token display
blocks so long access tokens can scroll instead of overflowing.
- Around line 499-505: The profile screen is hardcoding the verified badge
instead of using the user’s actual verification state. Update the
`home_screen.dart` profile rendering logic around
`ThunderIDProvider.of(context)` so the verified label/badge is derived from the
user/claims data (for example, the email verification claim) rather than always
showing “Email verified”. Handle unverified or missing claim data by showing a
false/unknown state in the same profile UI sections affected by the duplicated
block.
- Around line 316-320: The Settings action in HomeScreen is inert because its
onTap handler does nothing, so either remove the _ActionRow entirely until a
real settings destination exists or wire it to an actual settings
screen/navigation target. Update the _ActionRow usage in home_screen.dart so the
Settings row is not presented as interactive unless it performs a meaningful
action.
- Around line 253-258: The updated widget tree in home_screen.dart needs Dart
style cleanup: add trailing commas to multi-line constructor and argument lists,
and mark any widgets or values that are compile-time constants as const where
possible. Review the changed _StatCell, VerticalDivider, and similar widget
constructions in the affected sections, plus the other referenced spots, and
update the existing widget/build code to follow the repo’s const and
trailing-comma guidelines without changing behavior.
- Around line 143-150: The `_initials` helper in `HomeScreen` is using the raw
`displayName` and can still produce blank initials or hit empty split parts when
the name is whitespace-only. Trim the value once, reject empty tokens before
indexing, and make the helper return a safe fallback for empty input. Reuse this
same normalized initials logic anywhere the profile screen computes initials so
both places behave consistently.
- Around line 223-241: The session status UI in the home screen is hardcoded to
always show “Session active” in green, which ignores the computed expiry state.
Update the widget in the HomeScreen row to use the existing expiry/session state
logic so it displays active, expired, or unknown based on whether exp is present
and whether secondsLeft is greater than zero. Make sure the text and indicator
color both come from that derived status rather than a constant label.

In `@test/widget_test.dart`:
- Around line 26-30: The import block in the test widget file is out of
alphabetical order and will trip the directives_ordering lint. Reorder the
package imports in the section containing signed_in.dart, signed_out.dart,
loading.dart, user_object.dart, and language_switcher.dart so they are sorted
alphabetically, keeping the existing set of imports unchanged.

---

Nitpick comments:
In `@samples/quickstart/lib/screens/auth_screen.dart`:
- Around line 265-268: The string literal style in auth_screen.dart is
inconsistent with the Dart single-quote rule: the TextButton label in the signup
toggle should use single quotes and escape the apostrophe rather than switching
to double quotes. Update the affected Text and any related nearby string
literals in the signup/login UI so they consistently follow the existing
single-quote convention used elsewhere in the file, including the strings around
the _mode toggle and the "we'll" text.
- Around line 54-55: The local values in auth_screen’s build path are
compile-time constants but are declared with final; update the logo sizing
locals in the relevant widget logic to use const instead, following the same
pattern used elsewhere in the Dart codebase. Keep the existing identifiers
logoHeight and logoWidth, but make them const so the compiler can treat them as
compile-time values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 29b486e6-aceb-4d60-b7e2-dc8a7687e2cc

📥 Commits

Reviewing files that changed from the base of the PR and between f12a8bb and a75f62d.

⛔ Files ignored due to path filters (15)
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png is excluded by !**/*.png
📒 Files selected for processing (27)
  • AGENTS.md
  • lib/src/widgets/callback.dart
  • lib/src/widgets/language_switcher.dart
  • lib/src/widgets/loading.dart
  • lib/src/widgets/sign_in.dart
  • lib/src/widgets/sign_in_button.dart
  • lib/src/widgets/sign_out_button.dart
  • lib/src/widgets/sign_up.dart
  • lib/src/widgets/sign_up_button.dart
  • lib/src/widgets/signed_in.dart
  • lib/src/widgets/signed_out.dart
  • lib/src/widgets/user_dropdown.dart
  • lib/src/widgets/user_object.dart
  • lib/src/widgets/user_profile.dart
  • lib/thunderid_flutter.dart
  • samples/quickstart/.gitignore
  • samples/quickstart/ios/Podfile
  • samples/quickstart/ios/Runner.xcodeproj/project.pbxproj
  • samples/quickstart/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • samples/quickstart/ios/Runner/Info.plist
  • samples/quickstart/lib/app.dart
  • samples/quickstart/lib/screens/auth_screen.dart
  • samples/quickstart/lib/screens/home_screen.dart
  • samples/quickstart/lib/screens/profile_screen.dart
  • samples/quickstart/server.cert
  • samples/quickstart/server.key
  • test/widget_test.dart
💤 Files with no reviewable changes (3)
  • samples/quickstart/ios/Runner.xcodeproj/project.pbxproj
  • samples/quickstart/server.key
  • samples/quickstart/server.cert

Comment on lines +69 to +73
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Avoid disabling ATS globally with NSAllowsArbitraryLoads.

This blanket exemption permits unencrypted HTTP connections to any domain app-wide, weakening transport security for the sample. This is notable since a sibling change in this stack removes the committed dev TLS cert/key files — if the intent is only to support a local/dev endpoint, scope the exception to that specific domain via NSExceptionDomains rather than allowing arbitrary loads everywhere. Since this is a quickstart others may copy, the insecure pattern could get propagated into production apps.

🔒 Proposed scoped exception instead of global opt-out
 	<key>NSAppTransportSecurity</key>
 	<dict>
-		<key>NSAllowsArbitraryLoads</key>
-		<true/>
+		<key>NSExceptionDomains</key>
+		<dict>
+			<key>localhost</key>
+			<dict>
+				<key>NSExceptionAllowsInsecureHTTPLoads</key>
+				<true/>
+			</dict>
+		</dict>
 	</dict>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@samples/quickstart/ios/Runner/Info.plist` around lines 69 - 73, Remove the
global ATS opt-out in Info.plist by replacing
NSAppTransportSecurity/NSAllowsArbitraryLoads with a scoped exception for the
local/dev host only. Update the Runner app’s ATS configuration to use
NSExceptionDomains (or the narrowest equivalent) so the quickstart can reach the
intended endpoint without enabling insecure HTTP everywhere. Keep the change
localized to the Info.plist settings in the iOS Runner sample.

Comment on lines +143 to +150
static String _initials(String? displayName) {
if (displayName == null || displayName.isEmpty) return '?';
final parts = displayName.trim().split(RegExp(r'\s+'));
if (parts.length >= 2) {
return '${parts.first[0]}${parts.last[0]}'.toUpperCase();
}
return displayName[0].toUpperCase();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Normalize whitespace before deriving initials.

A whitespace-only displayName can produce blank initials in the home header and can throw in the profile screen when p[0] is evaluated on an empty split part. Trim once, reject empty parts, and reuse the same helper in both places.

Suggested fix
-int? _epochSecondsFromClaim(dynamic value) {
+int? _epochSecondsFromClaim(dynamic value) {
   if (value is num) return value.toInt();
   return null;
 }
+
+String _initialsFromDisplayName(String? displayName) {
+  final parts = (displayName ?? '')
+      .trim()
+      .split(RegExp(r'\s+'))
+      .where((part) => part.isNotEmpty)
+      .toList();
+  if (parts.isEmpty) return '?';
+  if (parts.length >= 2) {
+    return '${parts.first[0]}${parts.last[0]}'.toUpperCase();
+  }
+  return parts.first[0].toUpperCase();
+}
-  static String _initials(String? displayName) {
-    if (displayName == null || displayName.isEmpty) return '?';
-    final parts = displayName.trim().split(RegExp(r'\s+'));
-    if (parts.length >= 2) {
-      return '${parts.first[0]}${parts.last[0]}'.toUpperCase();
-    }
-    return displayName[0].toUpperCase();
-  }
-    final initials = _initials(displayName);
+    final initials = _initialsFromDisplayName(displayName);
-    final initials = displayName.isNotEmpty
-        ? displayName
-            .trim()
-            .split(RegExp(r'\s+'))
-            .take(2)
-            .map((p) => p[0])
-            .join()
-            .toUpperCase()
-        : '?';
+    final initials = _initialsFromDisplayName(displayName);

Also applies to: 506-514

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@samples/quickstart/lib/screens/home_screen.dart` around lines 143 - 150, The
`_initials` helper in `HomeScreen` is using the raw `displayName` and can still
produce blank initials or hit empty split parts when the name is
whitespace-only. Trim the value once, reject empty tokens before indexing, and
make the helper return a safe fallback for empty input. Reuse this same
normalized initials logic anywhere the profile screen computes initials so both
places behave consistently.

Comment on lines +223 to +241
Row(
children: [
Container(
width: 8,
height: 8,
decoration: const BoxDecoration(
color: _kGreen,
shape: BoxShape.circle,
),
),
const SizedBox(width: 6),
const Text(
'Session active',
style: TextStyle(
fontSize: 13,
color: _kGreen,
fontWeight: FontWeight.w500,
),
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Derive the session status from expiry state.

This always renders Session active in green, even when exp is missing or secondsLeft <= 0. Use the computed expiry state to show active, expired, or unknown.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@samples/quickstart/lib/screens/home_screen.dart` around lines 223 - 241, The
session status UI in the home screen is hardcoded to always show “Session
active” in green, which ignores the computed expiry state. Update the widget in
the HomeScreen row to use the existing expiry/session state logic so it displays
active, expired, or unknown based on whether exp is present and whether
secondsLeft is greater than zero. Make sure the text and indicator color both
come from that derived status rather than a constant label.

Comment thread samples/quickstart/lib/screens/home_screen.dart
Comment on lines +316 to +320
_ActionRow(
icon: Icons.settings_outlined,
label: 'Settings',
onTap: () {},
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove or implement the inert Settings row.

The row is tappable and shows a chevron, but onTap: () {} does nothing. Hide it until implemented or route it to an actual settings screen.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@samples/quickstart/lib/screens/home_screen.dart` around lines 316 - 320, The
Settings action in HomeScreen is inert because its onTap handler does nothing,
so either remove the _ActionRow entirely until a real settings destination
exists or wire it to an actual settings screen/navigation target. Update the
_ActionRow usage in home_screen.dart so the Settings row is not presented as
interactive unless it performs a meaningful action.

Comment on lines +499 to +505
final thunder = ThunderIDProvider.of(context);
final user = thunder.user;
final displayName = user?.displayName ?? 'Guest';
final email = user?.email ?? '';
final userId = user?.sub ?? '—';
final username = user?.username ?? '—';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not hardcode Email verified.

The profile screen always displays a verified badge regardless of the user’s actual email verification claim. Derive this from the user/claims data and show a false/unknown state when it is not verified.

Also applies to: 582-597

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@samples/quickstart/lib/screens/home_screen.dart` around lines 499 - 505, The
profile screen is hardcoding the verified badge instead of using the user’s
actual verification state. Update the `home_screen.dart` profile rendering logic
around `ThunderIDProvider.of(context)` so the verified label/badge is derived
from the user/claims data (for example, the email verification claim) rather
than always showing “Email verified”. Handle unverified or missing claim data by
showing a false/unknown state in the same profile UI sections affected by the
duplicated block.

Comment on lines +525 to +541
GestureDetector(
onTap: onBack,
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.chevron_left, color: _kBlue, size: 22),
Text(
'Home',
style: TextStyle(
color: _kBlue,
fontSize: 15,
fontWeight: FontWeight.w500,
),
),
],
),
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Increase the back control tap target.

These GestureDetector rows are only as tall as their icon/text content, so they fall below the 44×44 logical-pixel tap target requirement. Wrap them in a constrained/padded button surface.

Based on learnings, “Keep widget tap targets at least 44×44 logical pixels.”

Also applies to: 789-805

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@samples/quickstart/lib/screens/home_screen.dart` around lines 525 - 541, The
back control built with GestureDetector and the Home row has a tap target that
is too small because it only matches the icon/text size. Update the back
navigation widget in home_screen.dart to use a constrained or padded button
surface so the tappable area is at least 44x44 logical pixels, and apply the
same fix to the other matching back control referenced by the diff.

Source: Learnings

Comment on lines +853 to +863
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: cs.primary,
borderRadius: BorderRadius.circular(12),
color: _kCodeBg,
borderRadius: BorderRadius.circular(10),
),
child: Text(
'NEW',
style: TextStyle(
color: cs.onPrimary,
fontSize: 10,
fontWeight: FontWeight.bold,
child: _token != null
? _buildJwtRichText(_token!)
: const SizedBox.shrink(),
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Prevent long JWTs from overflowing horizontally.

Access tokens are usually long unbroken strings; _buildJwtRichText is placed directly inside a fixed-width container without horizontal scrolling. Wrap the token renderer in a horizontal SingleChildScrollView, matching the payload section.

Also applies to: 953-1010

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@samples/quickstart/lib/screens/home_screen.dart` around lines 853 - 863, The
JWT token display in the home screen container can overflow because
`_buildJwtRichText` is rendered directly inside a fixed-width `Container`
without horizontal scrolling. Wrap the token output in a horizontal
`SingleChildScrollView` the same way the payload section is handled, and apply
the same fix anywhere the token renderer is used in the referenced token display
blocks so long access tokens can scroll instead of overflowing.

Comment thread test/widget_test.dart Outdated
@brionmario
brionmario force-pushed the flutter-sdk branch 3 times, most recently from 635a6a3 to 4552c8d Compare July 7, 2026 18:26

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
samples/quickstart/android/app/src/main/AndroidManifest.xml (1)

4-4: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Update android:label to match the rebranded app name.

The label is left at the default flutter_quickstart template value while the iOS side and app UI are being rebranded per this PR. Consider aligning the Android launcher label with the new app name/branding used elsewhere.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@samples/quickstart/android/app/src/main/AndroidManifest.xml` at line 4, The
Android launcher label is still using the default flutter_quickstart template
name, so update the android:label in AndroidManifest.xml to match the new app
branding used elsewhere in the app. Keep the change aligned with the rebranded
name already reflected on iOS/UI, and ensure the manifest’s application label
matches the final product name.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@samples/quickstart/android/settings.gradle.kts`:
- Around line 26-31: The includeBuild path in settings.gradle.kts points too far
up and resolves outside the repo, so update the build path used by includeBuild
to the correct local android project location and keep the existing
dependencySubstitution for dev.thunderid:android intact.

---

Nitpick comments:
In `@samples/quickstart/android/app/src/main/AndroidManifest.xml`:
- Line 4: The Android launcher label is still using the default
flutter_quickstart template name, so update the android:label in
AndroidManifest.xml to match the new app branding used elsewhere in the app.
Keep the change aligned with the rebranded name already reflected on iOS/UI, and
ensure the manifest’s application label matches the final product name.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d8f0aa2-7501-4fd2-ae16-edbf9a4767e7

📥 Commits

Reviewing files that changed from the base of the PR and between a75f62d and 4552c8d.

⛔ Files ignored due to path filters (20)
  • samples/quickstart/android/app/src/main/res/mipmap-hdpi/ic_launcher.png is excluded by !**/*.png
  • samples/quickstart/android/app/src/main/res/mipmap-mdpi/ic_launcher.png is excluded by !**/*.png
  • samples/quickstart/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png is excluded by !**/*.png
  • samples/quickstart/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png is excluded by !**/*.png
  • samples/quickstart/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png is excluded by !**/*.png
  • samples/quickstart/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png is excluded by !**/*.png
📒 Files selected for processing (45)
  • .github/workflows/pr-builder.yml
  • AGENTS.md
  • lib/src/widgets/callback.dart
  • lib/src/widgets/language_switcher.dart
  • lib/src/widgets/loading.dart
  • lib/src/widgets/sign_in.dart
  • lib/src/widgets/sign_in_button.dart
  • lib/src/widgets/sign_out_button.dart
  • lib/src/widgets/sign_up.dart
  • lib/src/widgets/sign_up_button.dart
  • lib/src/widgets/signed_in.dart
  • lib/src/widgets/signed_out.dart
  • lib/src/widgets/user_dropdown.dart
  • lib/src/widgets/user_object.dart
  • lib/src/widgets/user_profile.dart
  • lib/thunderid_flutter.dart
  • samples/quickstart/.gitignore
  • samples/quickstart/.metadata
  • samples/quickstart/android/.gitignore
  • samples/quickstart/android/app/build.gradle.kts
  • samples/quickstart/android/app/src/debug/AndroidManifest.xml
  • samples/quickstart/android/app/src/main/AndroidManifest.xml
  • samples/quickstart/android/app/src/main/kotlin/dev/thunderid/flutter_quickstart/MainActivity.kt
  • samples/quickstart/android/app/src/main/res/drawable-v21/launch_background.xml
  • samples/quickstart/android/app/src/main/res/drawable/launch_background.xml
  • samples/quickstart/android/app/src/main/res/values-night/styles.xml
  • samples/quickstart/android/app/src/main/res/values/styles.xml
  • samples/quickstart/android/app/src/profile/AndroidManifest.xml
  • samples/quickstart/android/build.gradle.kts
  • samples/quickstart/android/flutter_quickstart_android.iml
  • samples/quickstart/android/gradle.properties
  • samples/quickstart/android/gradle/wrapper/gradle-wrapper.properties
  • samples/quickstart/android/settings.gradle.kts
  • samples/quickstart/flutter_quickstart.iml
  • samples/quickstart/ios/Podfile
  • samples/quickstart/ios/Runner.xcodeproj/project.pbxproj
  • samples/quickstart/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • samples/quickstart/ios/Runner/Info.plist
  • samples/quickstart/lib/app.dart
  • samples/quickstart/lib/screens/auth_screen.dart
  • samples/quickstart/lib/screens/home_screen.dart
  • samples/quickstart/lib/screens/profile_screen.dart
  • samples/quickstart/server.cert
  • samples/quickstart/server.key
  • test/widget_test.dart
💤 Files with no reviewable changes (3)
  • samples/quickstart/server.key
  • samples/quickstart/server.cert
  • samples/quickstart/ios/Runner.xcodeproj/project.pbxproj
✅ Files skipped from review due to trivial changes (13)
  • samples/quickstart/android/gradle/wrapper/gradle-wrapper.properties
  • samples/quickstart/android/app/src/debug/AndroidManifest.xml
  • samples/quickstart/android/app/src/main/res/drawable-v21/launch_background.xml
  • samples/quickstart/.metadata
  • samples/quickstart/android/.gitignore
  • samples/quickstart/android/gradle.properties
  • .github/workflows/pr-builder.yml
  • samples/quickstart/.gitignore
  • samples/quickstart/android/flutter_quickstart_android.iml
  • samples/quickstart/flutter_quickstart.iml
  • AGENTS.md
  • samples/quickstart/android/app/src/main/res/drawable/launch_background.xml
  • samples/quickstart/lib/app.dart
🚧 Files skipped from review as they are similar to previous changes (8)
  • samples/quickstart/lib/screens/profile_screen.dart
  • samples/quickstart/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • lib/thunderid_flutter.dart
  • samples/quickstart/ios/Podfile
  • samples/quickstart/ios/Runner/Info.plist
  • samples/quickstart/lib/screens/auth_screen.dart
  • test/widget_test.dart
  • samples/quickstart/lib/screens/home_screen.dart

Comment thread samples/quickstart/android/settings.gradle.kts Outdated
@brionmario
brionmario force-pushed the flutter-sdk branch 2 times, most recently from e318a88 to 4c6e3f7 Compare July 7, 2026 18:48
@brionmario
brionmario merged commit 39a89be into thunder-id:main Jul 8, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants