refactor: Better future loading dialog without flickering

This commit is contained in:
krille-chan 2024-10-13 09:52:05 +02:00
commit 831adceeca
No known key found for this signature in database
37 changed files with 175 additions and 55 deletions

View file

@ -5,11 +5,11 @@ import 'package:flutter/services.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:go_router/go_router.dart';
import 'package:keyboard_shortcuts/keyboard_shortcuts.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'matrix.dart';
enum ChatPopupMenuActions { details, mute, unmute, leave, search }

View file

@ -0,0 +1,141 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:async/async.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
/// Displays a loading dialog which reacts to the given [future]. The dialog
/// will be dismissed and the value will be returned when the future completes.
/// If an error occured, then [onError] will be called and this method returns
/// null.
Future<Result<T>> showFutureLoadingDialog<T>({
required BuildContext context,
required Future<T> Function() future,
String? title,
String? backLabel,
String Function(dynamic exception)? onError,
bool barrierDismissible = false,
}) async {
final futureExec = future();
final resultFuture = ResultFuture(futureExec);
var i = 3;
do {
final result = resultFuture.result;
if (result != null) {
if (result.isError) break;
return result;
}
await Future.delayed(const Duration(milliseconds: 100));
i--;
} while (i > 0);
final result = await showAdaptiveDialog<Result<T>>(
context: context,
barrierDismissible: barrierDismissible,
builder: (BuildContext context) => LoadingDialog<T>(
future: futureExec,
title: title,
backLabel: backLabel,
onError: onError,
),
);
return result ??
Result.error(
Exception('FutureDialog canceled'),
StackTrace.current,
);
}
class LoadingDialog<T> extends StatefulWidget {
final String? title;
final String? backLabel;
final Future<T> future;
final String Function(dynamic exception)? onError;
const LoadingDialog({
super.key,
required this.future,
this.title,
this.onError,
this.backLabel,
});
@override
LoadingDialogState<T> createState() => LoadingDialogState<T>();
}
class LoadingDialogState<T> extends State<LoadingDialog> {
Object? exception;
StackTrace? stackTrace;
@override
void initState() {
super.initState();
widget.future.then(
(result) => Navigator.of(context).pop<Result<T>>(Result.value(result)),
onError: (e, s) => setState(() {
exception = e;
stackTrace = s;
}),
);
}
@override
Widget build(BuildContext context) {
final exception = this.exception;
final titleLabel = exception != null
? widget.onError?.call(exception) ??
exception.toLocalizedString(context)
: widget.title ?? L10n.of(context).loadingPleaseWait;
return AlertDialog.adaptive(
content: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (exception == null)
const CircularProgressIndicator.adaptive()
else
Icon(
Icons.error_outline_outlined,
color: Theme.of(context).colorScheme.error,
size: 48,
),
const SizedBox(width: 20),
Expanded(
child: Text(
titleLabel,
maxLines: 2,
textAlign: TextAlign.left,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
actions: exception == null
? null
: [
TextButton(
onPressed: () => Navigator.of(context).pop<Result<T>>(
Result.error(
exception,
stackTrace,
),
),
child: Text(widget.backLabel ?? L10n.of(context).close),
),
],
);
}
}
extension DeprecatedApiAccessExtension<T> on Result<T> {
T? get result => asValue?.value;
Object? get error => asError?.error;
}

View file

@ -8,7 +8,6 @@ import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:collection/collection.dart';
import 'package:desktop_notifications/desktop_notifications.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
@ -21,12 +20,12 @@ import 'package:url_launcher/url_launcher_string.dart';
import 'package:fluffychat/utils/client_manager.dart';
import 'package:fluffychat/utils/init_with_restore.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/uia_request_manager.dart';
import 'package:fluffychat/utils/voip_plugin.dart';
import 'package:fluffychat/widgets/fluffy_chat_app.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import '../config/app_config.dart';
import '../config/setting_keys.dart';
import '../pages/key_verification/key_verification_dialog.dart';
@ -234,16 +233,6 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
} else {
initSettings();
}
initLoadingDialog();
}
void initLoadingDialog() {
WidgetsBinding.instance.addPostFrameCallback((_) {
LoadingDialog.defaultTitle = L10n.of(context).loadingPleaseWait;
LoadingDialog.defaultBackLabel = L10n.of(context).close;
LoadingDialog.defaultOnError =
(e) => (e as Object?)!.toLocalizedString(context);
});
}
Future<void> initConfig() async {

View file

@ -2,13 +2,13 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:fluffychat/utils/url_launcher.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:fluffychat/widgets/matrix.dart';
class PublicRoomBottomSheet extends StatelessWidget {