Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
8751446
new
Miduo666 Dec 9, 2025
70a947e
new
Miduo666 Dec 9, 2025
8b40173
new
Miduo666 Dec 10, 2025
8a2e815
new
Miduo666 Dec 10, 2025
03ab4d5
new
Miduo666 Dec 10, 2025
fda7957
new
Miduo666 Dec 10, 2025
67a3939
new
Miduo666 Dec 10, 2025
d2ec702
new
Miduo666 Dec 11, 2025
4b135a6
new
Miduo666 Dec 11, 2025
44fd9b7
new
Miduo666 Dec 11, 2025
617de91
new
Miduo666 Dec 12, 2025
c8abf7f
new
Miduo666 Dec 13, 2025
6d22721
new
Miduo666 Dec 13, 2025
d936549
new
Miduo666 Dec 15, 2025
e830ee4
new
Miduo666 Dec 15, 2025
2f96bbb
new
Miduo666 Dec 16, 2025
ec478aa
new
Miduo666 Dec 16, 2025
be59177
new
Miduo666 Dec 19, 2025
293c5dc
all_fixed
Miduo666 Dec 19, 2025
e7db7d6
new
Miduo666 Dec 25, 2025
bc35243
new
Miduo666 Dec 25, 2025
1b4861e
new
Miduo666 Dec 25, 2025
ec42754
new
Miduo666 Dec 25, 2025
7e32c93
new
Miduo666 Jan 2, 2026
ea6be28
new
Miduo666 Jan 2, 2026
35b0a3d
new
Miduo666 Jan 2, 2026
3cf1ac0
new
Miduo666 Jan 2, 2026
1d8bb14
new
Miduo666 Jan 6, 2026
e0b9292
fix_issue_overflow
Miduo666 Jan 7, 2026
0f78914
merge dev
Miduo666 Jan 12, 2026
2eca185
fix_issue
Miduo666 Jan 12, 2026
9655019
add_support_example_login
Miduo666 Jan 12, 2026
8721568
format_issue
Miduo666 Jan 12, 2026
f02fdc9
merge dev
Miduo666 Jan 13, 2026
8b94958
resolve import order
Miduo666 Jan 13, 2026
c6e0e30
merge_dev
Miduo666 Jan 14, 2026
25c4cac
Lint
tonypioneer Jan 14, 2026
4c63276
Lint
tonypioneer Jan 14, 2026
c732c7b
Lint
tonypioneer Jan 14, 2026
48fbe42
Lint
tonypioneer Jan 14, 2026
fb712d8
add_new_lines
Miduo666 Jan 14, 2026
c7eb547
merge_dev
Miduo666 Jan 16, 2026
ed97499
new
Miduo666 Jan 16, 2026
d2a54f1
Merge remote-tracking branch 'upstream/dev' into dev
Miduo666 Jan 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
14 changes: 7 additions & 7 deletions example/lib/app_scaffold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -134,19 +134,19 @@ class AppScaffold extends StatelessWidget {
text: '''

This template app demonstrates the following key SolidUI features:

🧭 Responsive navigation (rail ↔ drawer);

🎨 Theme switching (light/dark/system);

ℹ️ Customisable About dialogues;

📋 Version information display;

🔐 Security key management;

📊 Status bar integration;

👤 User information display.

For more information, visit the
Expand Down
25 changes: 25 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,13 @@ library;

import 'package:flutter/material.dart';

import 'package:solidpod/solidpod.dart' show KeyManager, setAppDirName;
import 'package:window_manager/window_manager.dart';

import 'package:solidui/solidui.dart';

import 'app.dart';
import 'app_scaffold.dart';
import 'constants/app.dart';
import 'utils/is_desktop.dart';

Expand All @@ -41,6 +45,27 @@ import 'utils/is_desktop.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();

// CRITICAL: Set app directory name BEFORE any Pod operations.

await setAppDirName('myapp');

// Configure SolidAuthHandler with app-specific settings.

SolidAuthHandler.instance.configure(
SolidAuthConfig(
appTitle: appTitle,
appDirectory: 'myapp',
defaultServerUrl: 'https://pods.solidcommunity.au',
appImage: const AssetImage('assets/images/app_image.jpg'),
appLogo: const AssetImage('assets/images/app_icon.jpg'),
loginSuccessWidget: appScaffold,
onSecurityKeyReset: () async {
await KeyManager.clear();
debugPrint('MyApp: Security key cleared on logout');
},
),
);

// Set window options for desktop platforms (Windows, Linux, macOS).

if (isDesktop) {
Expand Down
2 changes: 2 additions & 0 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ dependency_overrides:

dev_dependencies:
flutter_lints: ^5.0.0
flutter_test:
sdk: flutter

flutter:
uses-material-design: true
Expand Down
37 changes: 37 additions & 0 deletions example/web/callback.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Authentication Callback</title>
<script>
// This script helps solid_auth handle the OAuth callback.
// It passes the authorization code back to the parent window
// and closes the popup if applicable.
const AUTH_DESTINATION_KEY = "openidconnect_auth_destination_url";
const AUTH_RESPONSE_KEY = "openidconnect_auth_response_info";

window.onload = function () {
// Check if this is a popup window
if (window.opener && window.opener !== window) {
// Pass the full URL (including code) to the parent window
window.opener.postMessage(window.location.href, "*");
window.close();
} else {
// If not a popup, redirect to the main app with the code
// This handles the case where the browser doesn't support popups
const currentUrl = window.location.href;
if (currentUrl.includes('code=')) {
// Store the response info for the main window to pick up
localStorage.setItem(AUTH_RESPONSE_KEY, currentUrl);
// Redirect to the base URL (the main app will pick up the code)
window.location.href = window.location.origin + window.location.pathname.replace('/callback.html', '/');
}
}
}
</script>
</head>
<body>
<p>Authentication complete. You can close this window.</p>
<p>If this window does not close automatically, please close it manually.</p>
</body>
</html>
1 change: 1 addition & 0 deletions lib/solidui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export 'src/utils/solid_file_operations.dart';
export 'src/utils/solid_alert.dart';
export 'src/utils/solid_pod_helpers.dart'
show loginIfRequired, getKeyFromUserIfRequired;
export 'src/utils/solid_notifications.dart';

export 'src/widgets/solid_format_info_card.dart';

Expand Down
71 changes: 60 additions & 11 deletions lib/src/handlers/solid_auth_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import 'package:flutter/material.dart';
import 'package:solidpod/solidpod.dart' show getWebId;

import 'package:solidui/src/constants/solid_config.dart';
import 'package:solidui/src/widgets/solid_default_login.dart';
import 'package:solidui/src/widgets/solid_login.dart';
import 'package:solidui/src/widgets/solid_logout_dialog.dart' show logoutPopup;

/// Configuration for Solid authentication handling.
Expand Down Expand Up @@ -61,6 +61,7 @@ class SolidAuthConfig {
final String? appDirectory;

/// App image for the default login page.

final AssetImage? appImage;

/// App logo for the default login page.
Expand Down Expand Up @@ -114,6 +115,36 @@ class SolidAuthHandler {
_config = config;
}

/// Configure default values without overwriting existing configuration.
/// This is used by SolidLogin to provide fallback values while preserving
/// app-specific settings like onSecurityKeyReset.

void configureDefaults(SolidAuthConfig defaults) {
if (_config == null) {
// No existing config, use defaults
_config = defaults;
} else {
// Merge: keep existing non-null values, fill in missing ones from defaults
_config = SolidAuthConfig(
returnTo: _config!.returnTo ?? defaults.returnTo,
loginPageBuilder:
_config!.loginPageBuilder ?? defaults.loginPageBuilder,
defaultServerUrl:
_config!.defaultServerUrl ?? defaults.defaultServerUrl,
appTitle: _config!.appTitle ?? defaults.appTitle,
appDirectory: _config!.appDirectory ?? defaults.appDirectory,
appImage: _config!.appImage ?? defaults.appImage,
appLogo: _config!.appLogo ?? defaults.appLogo,
appLink: _config!.appLink ?? defaults.appLink,
loginSuccessWidget:
_config!.loginSuccessWidget ?? defaults.loginSuccessWidget,
// IMPORTANT: Preserve app's security key reset callback
onSecurityKeyReset:
_config!.onSecurityKeyReset ?? defaults.onSecurityKeyReset,
);
}
}

/// Handle logout functionality with confirmation popup.

Future<void> handleLogout(BuildContext context) async {
Expand All @@ -130,9 +161,12 @@ class SolidAuthHandler {
// No additional navigation needed.
}

/// Handle login functionality by navigating to login page.
/// Handle login functionality - navigates to login page.
/// Works consistently across all platforms (web, mobile, desktop).

Future<void> handleLogin(BuildContext context) async {
// Navigate to login page using standard Flutter navigation
// This works across all platforms and maintains proper widget lifecycle
Navigator.pushReplacement(
context,
MaterialPageRoute(
Expand All @@ -142,23 +176,38 @@ class SolidAuthHandler {
}

/// Build the login page widget.
///
/// Returns the actual login input page (SolidLogin), not the success page.
/// This is used when guest users want to authenticate, or after logout.

Widget _buildLoginPage(BuildContext context) {
if (_config?.loginPageBuilder != null) {
return _config!.loginPageBuilder!(context);
}

// Use default login page.
// Use the login input page, not the success page
// The loginSuccessWidget (child) will be shown after successful authentication
final mainAppWidget = _config?.loginSuccessWidget ??
const Center(child: Text('Authentication required'));

return SolidDefaultLogin(
appTitle: _config?.appTitle ?? 'Solid App',
// Use ValueKey to identify this as a fresh login page instance
// This works with didUpdateWidget() to reset state when needed
return SolidLogin(
key: const ValueKey('login_page'),
appDirectory: _config?.appDirectory ?? 'solid_app',
defaultServerUrl:
_config?.defaultServerUrl ?? SolidConfig.defaultServerUrl,
appImage: _config?.appImage,
appLogo: _config?.appLogo,
appLink: _config?.appLink,
loginSuccessWidget: _config?.loginSuccessWidget,
webID: _config?.defaultServerUrl ?? SolidConfig.defaultServerUrl,
// Use provided images or fallback to SolidLogin's defaults from solidpod package
image: _config?.appImage ??
const AssetImage(
'assets/images/default_image.jpg',
package: 'solidpod',
),
logo: _config?.appLogo ??
const AssetImage(
'assets/images/default_logo.png',
package: 'solidpod',
),
child: mainAppWidget,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ ElevatedButton resCreateFormSubmission(
Widget child,
) {
// Use MediaQuery to determine the screen width and adjust the font size accordingly.

final screenWidth = MediaQuery.of(context).size.width;
final isSmallDevice =
screenWidth < 360; // A threshold for small devices, can be adjusted.
Expand Down
9 changes: 5 additions & 4 deletions lib/src/utils/file_operations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,10 @@ class FileOperations {

final fileName = extractResourceName(fileUrl);

// Skip non-TTL files. Include both .enc.ttl and .ttl files.
// Skip ACL files and metadata files, but include all other file types
// (TTL, JSON, CSV, TXT, etc.)

if (!fileName.endsWith('.enc.ttl') && !fileName.endsWith('.ttl')) {
if (fileName.endsWith('.acl') || fileName.endsWith('.meta')) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add an empty line after the comment. Ref: https://survivor.togaware.com/gnulinux/flutter-style-comments.html

continue;
}

Expand Down Expand Up @@ -182,12 +183,12 @@ class FileOperations {

static Future<int> getDirectoryFileCount(String dirPath) async {
try {
// Get directory contents and count files.
// Get directory contents and count files (exclude ACL and metadata files).

final dirUrl = await getDirUrl(dirPath);
final resources = await getResourcesInContainer(dirUrl);
return resources.files
.where((f) => f.endsWith('.enc.ttl') || f.endsWith('.ttl'))
.where((f) => !f.endsWith('.acl') && !f.endsWith('.meta'))
.length;
} catch (e) {
debugPrint('Error counting files in directory: $e');
Expand Down
3 changes: 3 additions & 0 deletions lib/src/widgets/build_message_container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ Container buildMsgBox(
String msg,
) {
// Zheyuan might need to use isRTL in the future

// ignore: unused_local_variable
var isRTL = false;

Expand All @@ -127,10 +128,12 @@ Container buildMsgBox(
}

// Determine device type for layout adjustments

final isMobile = size.width <= 730;
final isTablet = size.width > 730 && size.width <= 1050;

// Minimal horizontal padding for all devices

final horizontalPadding =
size.width * 0.01; // Adjust this value to increase or decrease padding

Expand Down
18 changes: 12 additions & 6 deletions lib/src/widgets/solid_dynamic_login_status.dart
Original file line number Diff line number Diff line change
Expand Up @@ -135,24 +135,30 @@ class _SolidDynamicLoginStatusState extends State<SolidDynamicLoginStatus> {
_currentWebId != null && _currentWebId!.isNotEmpty;

if (isCurrentlyLoggedIn) {
// Logout scenario - don't recheck status as page will reload
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add an empty line after the comment. Ref: https://survivor.togaware.com/gnulinux/flutter-style-comments.html


if (widget.onTap != null) {
widget.onTap!.call();
} else {
SolidAuthHandler.instance.handleLogout(context);
}
} else {
// Login scenario - can delay status check
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add an empty line after the comment. Ref: https://survivor.togaware.com/gnulinux/flutter-style-comments.html


if (widget.onLogin != null) {
widget.onLogin!.call();
} else {
SolidAuthHandler.instance.handleLogin(context);
}
}

Future.delayed(const Duration(milliseconds: 500), () {
if (mounted) {
_checkLoginStatus();
}
});
// Only refresh status for login scenario
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add an empty line after the comment. Ref: https://survivor.togaware.com/gnulinux/flutter-style-comments.html


Future.delayed(const Duration(milliseconds: 500), () {
if (mounted) {
_checkLoginStatus();
}
});
}
}

@override
Expand Down
4 changes: 2 additions & 2 deletions lib/src/widgets/solid_file_browser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,10 @@ class SolidFileBrowserState extends State<SolidFileBrowser> {
currentDirDirectoryCount = directories.length;
});

// Count files in current directory.
// Count files in current directory (exclude ACL and metadata files).

currentDirFileCount = resources.files
.where((f) => f.endsWith('.enc.ttl') || f.endsWith('.ttl'))
.where((f) => !f.endsWith('.acl') && !f.endsWith('.meta'))
.length;

// Get file counts for all subdirectories.
Expand Down
6 changes: 6 additions & 0 deletions lib/src/widgets/solid_format_info_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class SolidFormatInfoCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header with icon and title.

Row(
children: [
Icon(
Expand All @@ -98,6 +99,7 @@ class SolidFormatInfoCard extends StatelessWidget {
),

// Description if provided.

if (config.description != null) ...[
const SizedBox(height: 8),
Text(
Expand All @@ -111,6 +113,7 @@ class SolidFormatInfoCard extends StatelessWidget {
const SizedBox(height: 12),

// Required fields section.

Text(
'Required Fields:',
style: TextStyle(
Expand All @@ -137,6 +140,7 @@ class SolidFormatInfoCard extends StatelessWidget {
),

// Optional fields section.

if (config.optionalFields.isNotEmpty) ...[
const SizedBox(height: 12),
Text(
Expand Down Expand Up @@ -166,6 +170,7 @@ class SolidFormatInfoCard extends StatelessWidget {
],

// Format type indicator.

const SizedBox(height: 12),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
Expand All @@ -188,6 +193,7 @@ class SolidFormatInfoCard extends StatelessWidget {
),

// Note text.

const SizedBox(height: 8),
Text(
config.isJson
Expand Down
Loading