Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ The package is available from

## 1.1 Consolidate Android Login

+ Export resilient snackbar [1.0.47 20260917 jesscmoore]
+ Overflow menu in More button on mobile nav bar [1.0.46 20260915 jesscmoore]
+ Add internet connection retries [1.0.48 20260916 jesscmoore]
+ Overflow menu in More button on mobile nav bar [1.0.47 20260915 jesscmoore]
+ Plainer wording when a sign-in does not complete [1.0.46 20260918 gjw]
Expand Down
2 changes: 2 additions & 0 deletions lib/solidui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ export 'src/services/solid_backup_service.dart';
export 'src/widgets/solid_backup_dialog.dart';

export 'src/utils/snack_bar.dart';
export 'src/utils/resilient_snack_bar.dart';
export 'src/constants/ui_colors.dart';

export 'src/widgets/solid_file.dart';
export 'src/widgets/solid_file_browser.dart';
Expand Down
78 changes: 78 additions & 0 deletions lib/src/utils/resilient_snack_bar.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/// A [SnackBar] reporter that survives a momentarily-unregistered Scaffold.
///
/// Copyright (C) 2024-2026, Software Innovation Institute, ANU.
///
/// Licensed under the MIT License (the "License").
///
/// License: https://choosealicense.com/licenses/mit/.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
///
/// Authors: Jess Moore, Anushka Vidanage

library;

import 'package:flutter/material.dart';

/// Signature for the snack-bar reporter used to surface an async action's
/// outcome back to the caller's UI.

typedef NotifySnackBar = void Function(
String message,
Color backgroundColor, {
Duration duration,
});

/// Build a resilient [NotifySnackBar] bound to [messenger].
///
/// Showing a SnackBar requires a Scaffold registered with the messenger.
/// Depending on how the host embeds the form, and on the exact moment the
/// dialog is dismissed, the messenger can momentarily have no Scaffold,
/// which throws the "_scaffolds.isNotEmpty" assertion. A confirmation toast
/// is non-critical (the underlying action has already succeeded or failed
/// independently of this call), so the call is guarded and retried once on
/// the next frame rather than ever letting it crash the app.
///
/// Capture the closure returned here *before* any `await` in the caller, so
/// it keeps working even if the calling widget is unmounted mid-flow (e.g. a
/// purchase or permission-grant result that resolves after the dialog that
/// started it has already closed).

NotifySnackBar makeResilientSnackBar(ScaffoldMessengerState messenger) => (
String message,
Color backgroundColor, {
Duration duration = const Duration(seconds: 4),
}) {
SnackBar build() => SnackBar(
content: Text(message),
backgroundColor: backgroundColor,
duration: duration,
);
try {
messenger.showSnackBar(build());
} on Object catch (_) {
WidgetsBinding.instance.addPostFrameCallback((_) {
try {
messenger.showSnackBar(build());
} on Object catch (e) {
debugPrint('Could not show snackbar "$message": $e');
}
});
}
};
46 changes: 5 additions & 41 deletions lib/src/widgets/grant_permission_notify.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,48 +36,12 @@ import 'package:solidpod/solidpod.dart'
show RecipientNotReadyException, RecipientType, sendNotification;

import 'package:solidui/src/constants/ui_colors.dart' show ActionColors;
import 'package:solidui/src/utils/resilient_snack_bar.dart';

/// Signature for the snack-bar reporter used to surface notification
/// delivery outcomes back to the caller's UI.

typedef NotifySnackBar = void Function(
String message,
Color backgroundColor, {
Duration duration,
});

/// Build a resilient [NotifySnackBar] bound to [messenger].
///
/// Showing a SnackBar requires a Scaffold registered with the messenger.
/// Depending on how the host embeds the form, and on the exact moment the
/// dialog is dismissed, the messenger can momentarily have no Scaffold,
/// which throws the "_scaffolds.isNotEmpty" assertion. A confirmation toast
/// is non-critical (the grant has already succeeded), so the call is guarded
/// and retried once on the next frame rather than ever letting it crash the
/// app.

NotifySnackBar makeResilientSnackBar(ScaffoldMessengerState messenger) => (
String message,
Color backgroundColor, {
Duration duration = const Duration(seconds: 4),
}) {
SnackBar build() => SnackBar(
content: Text(message),
backgroundColor: backgroundColor,
duration: duration,
);
try {
messenger.showSnackBar(build());
} on Object catch (_) {
WidgetsBinding.instance.addPostFrameCallback((_) {
try {
messenger.showSnackBar(build());
} on Object catch (e) {
debugPrint('Could not show snackbar "$message": $e');
}
});
}
};
// Re-exported so existing importers of this file (e.g.
// grant_permission_form.dart) keep resolving `makeResilientSnackBar`/
// `NotifySnackBar` without changes now that they live in their own file.
export 'package:solidui/src/utils/resilient_snack_bar.dart';

/// Send a "resource shared" notification to each recipient WebID and
/// report delivery problems through [showSnack].
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: solidui
description: 'A UI library for building Solid applications with Flutter.'
version: 1.0.46
version: 1.0.47
homepage: https://github.com/anusii/solidui

# Scaffold a new Solid Pod file-browser app (a pod browser, with navigation
Expand Down
Loading