From a08c06f149ec6b71fa15d81a4e7a89fd7611ea6f Mon Sep 17 00:00:00 2001 From: anushkavidanage Date: Wed, 20 May 2026 21:44:40 +1000 Subject: [PATCH 1/5] initial set of changes to work with solidpod changes --- example/pubspec.yaml | 4 ++++ lib/src/handlers/solid_auth_handler.dart | 21 +++++++++++++++++++ lib/src/widgets/solid_default_login.dart | 18 ++++++++++++++++ lib/src/widgets/solid_login.dart | 18 ++++++++++++++++ lib/src/widgets/solid_login_actions.dart | 6 ++++++ lib/src/widgets/solid_login_auth_handler.dart | 11 +++++++++- lib/src/widgets/solid_popup_login.dart | 3 ++- pubspec.yaml | 4 ++++ 8 files changed, 83 insertions(+), 2 deletions(-) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index b8083263..9d659f84 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -20,6 +20,10 @@ dependencies: universal_io: ^2.3.1 window_manager: ^0.5.1 +dependency_overrides: + solidpod: + path: ../../solidpod + flutter: uses-material-design: true assets: diff --git a/lib/src/handlers/solid_auth_handler.dart b/lib/src/handlers/solid_auth_handler.dart index a9ff6acd..1402b1d3 100644 --- a/lib/src/handlers/solid_auth_handler.dart +++ b/lib/src/handlers/solid_auth_handler.dart @@ -86,6 +86,18 @@ class SolidAuthConfig { final VoidCallback? onLogout; + /// URL of the app's client profile JSON-LD document. Required parameter + + final String? clientId; + + /// Custom URL scheme for the OAuth to redirect to after authentication. + + final String? redirectUri; + + /// Optional redirect URI for logout. + + final String? postLogoutRedirectUri; + const SolidAuthConfig({ this.returnTo, this.loginPageBuilder, @@ -98,6 +110,9 @@ class SolidAuthConfig { this.loginSuccessWidget, this.onSecurityKeyReset, this.onLogout, + this.clientId, + this.redirectUri, + this.postLogoutRedirectUri, }); } @@ -258,6 +273,9 @@ class SolidAuthHandler { themeConfig: _cachedThemeConfig, snackbarConfig: _cachedSnackbarConfig, required: _cachedRequired, + clientId: _config!.clientId!, + redirectUri: _config!.redirectUri!, + postLogoutRedirectUri: _config!.postLogoutRedirectUri!, ); } @@ -273,6 +291,9 @@ class SolidAuthHandler { appLink: _config?.appLink, loginSuccessWidget: _config?.loginSuccessWidget, navigateToRootOnSuccess: _config?.loginSuccessWidget == null, + clientId: _config!.clientId!, + redirectUri: _config!.redirectUri!, + postLogoutRedirectUri: _config!.postLogoutRedirectUri!, ); } diff --git a/lib/src/widgets/solid_default_login.dart b/lib/src/widgets/solid_default_login.dart index d884be24..f8b55930 100644 --- a/lib/src/widgets/solid_default_login.dart +++ b/lib/src/widgets/solid_default_login.dart @@ -113,6 +113,18 @@ class SolidDefaultLogin extends StatelessWidget { final bool required; + /// URL of the app's client profile JSON-LD document. Required parameter + + final String clientId; + + /// Custom URL scheme for the OAuth to redirect to after authentication. + + final String redirectUri; + + /// Optional redirect URI for logout. + + final String? postLogoutRedirectUri; + const SolidDefaultLogin({ super.key, required this.appTitle, @@ -131,6 +143,9 @@ class SolidDefaultLogin extends StatelessWidget { this.themeConfig, this.snackbarConfig, this.required = false, + required this.clientId, + required this.redirectUri, + this.postLogoutRedirectUri, }); @override @@ -168,6 +183,9 @@ class SolidDefaultLogin extends StatelessWidget { changeKeyButtonStyle ?? const ChangeKeyButtonStyle(), themeConfig: themeConfig ?? const SolidLoginTheme(), snackbarConfig: snackbarConfig ?? const SnackbarConfig(), + clientId: clientId, + redirectUri: redirectUri, + postLogoutRedirectUri: postLogoutRedirectUri, child: successWidget, ), ); diff --git a/lib/src/widgets/solid_login.dart b/lib/src/widgets/solid_login.dart index e7c2286d..a3fcad96 100644 --- a/lib/src/widgets/solid_login.dart +++ b/lib/src/widgets/solid_login.dart @@ -81,6 +81,9 @@ class SolidLogin extends StatefulWidget { this.themeConfig = const SolidLoginTheme(), this.snackbarConfig = const SnackbarConfig(), this.customFolderPathList = const [], + required this.clientId, + required this.redirectUri, + this.postLogoutRedirectUri, super.key, }); @@ -144,6 +147,18 @@ class SolidLogin extends StatefulWidget { final List customFolderPathList; + /// URL of the app's client profile JSON-LD document. Required parameter + + final String clientId; + + /// Custom URL scheme for the OAuth to redirect to after authentication. + + final String redirectUri; + + /// Optional redirect URI for logout. + + final String? postLogoutRedirectUri; + @override State createState() => _SolidLoginState(); } @@ -408,6 +423,9 @@ class _SolidLoginState extends State with WidgetsBindingObserver { updateDialogCanceledState: updateState, showSnackbar: _showSnackbar, staySignedIn: _staySignedIn, + clientId: widget.clientId, + redirectUri: widget.redirectUri, + postLogoutRedirectUri: widget.postLogoutRedirectUri, ); } diff --git a/lib/src/widgets/solid_login_actions.dart b/lib/src/widgets/solid_login_actions.dart index a70acd99..dd796755 100644 --- a/lib/src/widgets/solid_login_actions.dart +++ b/lib/src/widgets/solid_login_actions.dart @@ -85,6 +85,9 @@ class SolidLoginActions { required VoidCallback updateDialogCanceledState, required LoginSnackbar showSnackbar, required bool staySignedIn, + required final String clientId, + required final String redirectUri, + final String? postLogoutRedirectUri, }) async { // When the user has opted out of staying signed in, discard any existing // cached session immediately so browser authentication is always @@ -132,6 +135,9 @@ class SolidLoginActions { updateDialogCanceledState: updateDialogCanceledState, showSnackbar: showSnackbar, staySignedIn: staySignedIn, + clientId: clientId, + redirectUri: redirectUri, + postLogoutRedirectUri: postLogoutRedirectUri, ); } diff --git a/lib/src/widgets/solid_login_auth_handler.dart b/lib/src/widgets/solid_login_auth_handler.dart index 11e7c208..1657e8e2 100644 --- a/lib/src/widgets/solid_login_auth_handler.dart +++ b/lib/src/widgets/solid_login_auth_handler.dart @@ -250,11 +250,14 @@ class SolidLoginAuthHandler { required List defaultFolders, required Map defaultFiles, required dynamic originalLoginWidget, + required final String clientId, + required final String redirectUri, required Widget childWidget, required ValueGetter isDialogCanceled, required VoidCallback updateDialogCanceledState, required Function(String message, {Duration? duration, bool showAction}) showSnackbar, + final String? postLogoutRedirectUri, bool staySignedIn = true, }) async { // Method to show busy animation requiring BuildContext. @@ -305,7 +308,13 @@ class SolidLoginAuthHandler { List? authResult; try { - authResult = await solidAuthenticate(podServer, context); + authResult = await solidAuthenticate( + podServer, + context, + clientId: clientId, + redirectUri: redirectUri, + postLogoutRedirectUri: postLogoutRedirectUri, + ); } on Object catch (e) { // Check whether auth data was persisted before the failure (i.e. POD // not initialised) vs a genuine server/network error. diff --git a/lib/src/widgets/solid_popup_login.dart b/lib/src/widgets/solid_popup_login.dart index f2f0a498..eb35327e 100644 --- a/lib/src/widgets/solid_popup_login.dart +++ b/lib/src/widgets/solid_popup_login.dart @@ -141,7 +141,8 @@ class _SolidPopupLoginState extends State { Future _loginAndInitPods(String webId, BuildContext context) async { try { - await solidAuthenticate(webId, context); + await solidAuthenticate(webId, context, + clientId: '', redirectUri: '', postLogoutRedirectUri: ''); // Persist the WebID/server URL so the re-login dialog can prefill it // next time the user is logged out. Prefer the canonical WebID diff --git a/pubspec.yaml b/pubspec.yaml index 2540fe77..d31e0694 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,6 +36,10 @@ dependencies: url_launcher: ^6.3.2 version_widget: ^1.0.9 +dependency_overrides: + solidpod: + path: ../solidpod + dev_dependencies: flutter_lints: ^6.0.0 window_manager: ^0.5.1 From 32df7c9abfcaadd9718df46144b1453903baaae3 Mon Sep 17 00:00:00 2001 From: anushkavidanage Date: Wed, 20 May 2026 22:11:01 +1000 Subject: [PATCH 2/5] auto logging session restore --- lib/src/widgets/solid_login.dart | 54 ++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/lib/src/widgets/solid_login.dart b/lib/src/widgets/solid_login.dart index a3fcad96..1982a059 100644 --- a/lib/src/widgets/solid_login.dart +++ b/lib/src/widgets/solid_login.dart @@ -40,7 +40,8 @@ import 'package:solidpod/solidpod.dart' generateDefaultFolders, generateDefaultFiles, generateCustomFolders, - setAppDirName; + setAppDirName, + tryRestoreSession; import 'package:solidui/src/constants/solid_config.dart'; import 'package:solidui/src/handlers/solid_auth_handler.dart'; @@ -84,6 +85,7 @@ class SolidLogin extends StatefulWidget { required this.clientId, required this.redirectUri, this.postLogoutRedirectUri, + this.autoLogin = false, super.key, }); @@ -159,6 +161,14 @@ class SolidLogin extends StatefulWidget { final String? postLogoutRedirectUri; + /// When true, automatically restores a saved session on startup and navigates + /// directly to [child] without showing the login page. + /// + /// Falls back to showing the login page if no valid session is found or if + /// the user has opted out of "Stay signed in". + + final bool autoLogin; + @override State createState() => _SolidLoginState(); } @@ -199,6 +209,10 @@ class _SolidLoginState extends State with WidgetsBindingObserver { bool _assetsResolved = false; + /// Whether an auto-login check is in progress (shows a loading screen). + + bool _checkingAutoLogin = false; + /// Whether the user wishes to persist the login session across app restarts. bool _staySignedIn = true; @@ -232,6 +246,13 @@ class _SolidLoginState extends State with WidgetsBindingObserver { SolidLoginAuthHandler.clearSessionIfRequired(); + // If autoLogin is requested, attempt silent session restoration after the + // first frame so that context is available for navigation. + + if (widget.autoLogin) { + WidgetsBinding.instance.addPostFrameCallback((_) => _checkAutoLogin()); + } + // dc 20251022: please explain why calling an async without await. _initPackageInfo(); @@ -286,6 +307,32 @@ class _SolidLoginState extends State with WidgetsBindingObserver { if (mounted) setState(() => _staySignedIn = value); } + /// Attempts silent session restoration. On success navigates directly to + /// [widget.child]; on failure shows the login page as normal. + + Future _checkAutoLogin() async { + if (!mounted) return; + + // Honour the "Stay signed in" opt-out — if disabled, skip auto-login. + final staySignedIn = await SolidLoginAuthHandler.getStaySignedIn(); + if (!staySignedIn || !mounted) return; + + if (mounted) setState(() => _checkingAutoLogin = true); + + final session = await tryRestoreSession(); + + if (!mounted) return; + if (session == null) { + setState(() => _checkingAutoLogin = false); + return; + } + + // Session restored — navigate directly to the child widget. + await Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (_) => widget.child), + ); + } + @override void dispose() { WidgetsBinding.instance.removeObserver(this); @@ -381,9 +428,10 @@ class _SolidLoginState extends State with WidgetsBindingObserver { @override Widget build(BuildContext context) { - // Show a loading indicator whilst assets are being resolved. + // Show a loading indicator whilst assets are being resolved or an + // auto-login check is in progress. - if (!_assetsResolved) { + if (!_assetsResolved || _checkingAutoLogin) { return const Scaffold(body: Center(child: CircularProgressIndicator())); } From f53941d2dfc324b327b9708e24ca9c3729ddad82 Mon Sep 17 00:00:00 2001 From: anushkavidanage Date: Thu, 21 May 2026 16:30:32 +1000 Subject: [PATCH 3/5] changes to the example app --- example/.metadata | 12 ++++++------ example/analysis_options.yaml | 28 ++++++++++++++++++++++++++++ example/lib/app.dart | 17 +++++++++++++++-- example/test/widget_test.dart | 30 ++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 example/analysis_options.yaml create mode 100644 example/test/widget_test.dart diff --git a/example/.metadata b/example/.metadata index a8d40816..df13aa7f 100644 --- a/example/.metadata +++ b/example/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "20f82749394e68bcfbbeee96bad384abaae09c13" + revision: "ff37bef603469fb030f2b72995ab929ccfc227f0" channel: "stable" project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13 - base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13 - - platform: linux - create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13 - base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13 + create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + - platform: android + create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 # User provided section diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 00000000..0d290213 --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/example/lib/app.dart b/example/lib/app.dart index 107b9353..ceec376b 100644 --- a/example/lib/app.dart +++ b/example/lib/app.dart @@ -45,15 +45,28 @@ class App extends StatelessWidget { useMaterial3: true, ), home: const SolidLogin( - title: 'SOLID POD DEMONSTRATOR', + // Images generated using Bing Image Creator from Designer, powered by + // DALL-E3. + + title: 'SOLID UI DEMONSTRATOR', appDirectory: 'demopod', - image: AssetImage('assets/images/demopod_image.jpg'), + image: AssetImage('assets/images/demopod_image.png'), logo: AssetImage('assets/images/demopod_logo.png'), link: 'https://github.com/anusii/solidpod/blob/main/demopod/README.md', required: false, infoButtonStyle: InfoButtonStyle( tooltip: 'Visit the DemoPod documentation.', ), + clientId: + 'https://anushkavidanage.github.io/solidui/example/client-profile.jsonld', + // Use the following schemas depending on the platform + // Web: https://anushkavidanage.github.io/solidpod/example/redirect.html + // Mobile: com.example.demopod://redirect + // Desktop: http://localhost:4400/redirect + // (can use any port as long as it matches with the one in your id document) + redirectUri: 'http://localhost:4400/redirect', + postLogoutRedirectUri: 'http://localhost:4400/redirect', + autoLogin: true, child: appScaffold, ), ); diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart new file mode 100644 index 00000000..5fdc848c --- /dev/null +++ b/example/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +// import 'package:flutter/material.dart'; +// import 'package:flutter_test/flutter_test.dart'; + +// import 'package:demopod/main.dart'; + +// void main() { +// testWidgets('Counter increments smoke test', (WidgetTester tester) async { +// // Build our app and trigger a frame. +// await tester.pumpWidget(const MyApp()); + +// // Verify that our counter starts at 0. +// expect(find.text('0'), findsOneWidget); +// expect(find.text('1'), findsNothing); + +// // Tap the '+' icon and trigger a frame. +// await tester.tap(find.byIcon(Icons.add)); +// await tester.pump(); + +// // Verify that our counter has incremented. +// expect(find.text('0'), findsNothing); +// expect(find.text('1'), findsOneWidget); +// }); +// } From 132aea0fcdfa6edc7fe4a18418f53f372acc1b1f Mon Sep 17 00:00:00 2001 From: anushkavidanage Date: Thu, 21 May 2026 17:30:25 +1000 Subject: [PATCH 4/5] point to git branches --- example/pubspec.yaml | 5 ++++- pubspec.yaml | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 9d659f84..350340c9 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -22,7 +22,10 @@ dependencies: dependency_overrides: solidpod: - path: ../../solidpod + # path: ../../solidpod + git: + url: https://github.com/anusii/solidui.git + ref: av/40_migrate_oidc_implementation flutter: uses-material-design: true diff --git a/pubspec.yaml b/pubspec.yaml index d31e0694..2e6d09b8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,7 +38,10 @@ dependencies: dependency_overrides: solidpod: - path: ../solidpod + # path: ../solidpod + git: + url: https://github.com/anusii/solidpod.git + ref: av/40_migrate_oidc_implementation dev_dependencies: flutter_lints: ^6.0.0 From 500c4b6450b6f035a85d5dd3873c536869144297 Mon Sep 17 00:00:00 2001 From: anushkavidanage Date: Sat, 23 May 2026 09:22:16 +1000 Subject: [PATCH 5/5] point to local dir --- pubspec.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 2e6d09b8..562df527 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,10 +38,10 @@ dependencies: dependency_overrides: solidpod: - # path: ../solidpod - git: - url: https://github.com/anusii/solidpod.git - ref: av/40_migrate_oidc_implementation + path: ../solidpod + # git: + # url: https://github.com/anusii/solidpod.git + # ref: av/40_migrate_oidc_implementation dev_dependencies: flutter_lints: ^6.0.0