f14e6f1460
Refines the profile, provider, and configuration interactions, virtualizes the provider lists so a large subscription no longer builds every row, syncs updates through the path the list reads, and expands the subscription details with full usage. Paged sheets are scoped locally and sized to the page in view instead of the tallest one, so a sheet no longer reserves space it never uses. Changelog: Provider lists scroll smoothly, subscriptions show full usage details, and sheets size to the page in view
189 lines
5.2 KiB
Dart
189 lines
5.2 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:animations/animations.dart';
|
|
import 'package:fl_clash/common/common.dart';
|
|
import 'package:fl_clash/enum/enum.dart';
|
|
import 'package:fl_clash/models/models.dart';
|
|
import 'package:fl_clash/widgets/widgets.dart';
|
|
import 'package:material_ui/material_ui.dart';
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
|
|
class Dialogs {
|
|
Dialogs._();
|
|
|
|
BuildContext get _context => rootNavigatorKey.currentContext!;
|
|
|
|
Future<T?> showCommonDialog<T>({
|
|
required Widget child,
|
|
BuildContext? context,
|
|
bool? dismissible,
|
|
bool filter = true,
|
|
}) async {
|
|
return showModal<T>(
|
|
useRootNavigator: false,
|
|
context: context ?? _context,
|
|
configuration: FadeScaleTransitionConfiguration(
|
|
barrierColor: Colors.black38,
|
|
barrierDismissible: dismissible ?? true,
|
|
),
|
|
builder: (_) => child,
|
|
filter: filter ? commonFilter : null,
|
|
);
|
|
}
|
|
|
|
Future<bool?> showMessage({
|
|
required InlineSpan message,
|
|
BuildContext? context,
|
|
String? title,
|
|
String? confirmText,
|
|
String? cancelText,
|
|
bool cancelable = true,
|
|
bool? dismissible,
|
|
}) async {
|
|
return showCommonDialog<bool>(
|
|
context: context,
|
|
dismissible: dismissible,
|
|
child: Builder(
|
|
builder: (context) {
|
|
final appLocalizations = context.appLocalizations;
|
|
return CommonDialog(
|
|
title: title ?? appLocalizations.tip,
|
|
actions: [
|
|
if (cancelable)
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop(false);
|
|
},
|
|
child: Text(cancelText ?? appLocalizations.cancel),
|
|
),
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop(true);
|
|
},
|
|
child: Text(confirmText ?? appLocalizations.confirm),
|
|
),
|
|
],
|
|
child: Container(
|
|
width: 300,
|
|
constraints: const BoxConstraints(maxHeight: 200),
|
|
child: SingleChildScrollView(
|
|
child: SelectableText.rich(
|
|
TextSpan(
|
|
style: Theme.of(context).textTheme.labelLarge,
|
|
children: [message],
|
|
),
|
|
style: const TextStyle(overflow: TextOverflow.visible),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<bool?> showAllUpdatingMessagesDialog(
|
|
List<UpdatingMessage> messages,
|
|
) async {
|
|
return showCommonDialog<bool>(
|
|
child: Builder(
|
|
builder: (context) {
|
|
final appLocalizations = context.appLocalizations;
|
|
return CommonDialog(
|
|
backgroundColor: context.colorScheme.surfaceContainerLow,
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
|
title: appLocalizations.tip,
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop(true);
|
|
},
|
|
child: Text(appLocalizations.confirm),
|
|
),
|
|
],
|
|
child: generateSectionV3(
|
|
items: messages.map(
|
|
(message) => _UpdatingMessageItem(message: message),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<bool> showDisclaimer() async {
|
|
return await showCommonDialog<bool>(
|
|
dismissible: false,
|
|
child: CommonDialog(
|
|
title: currentAppLocalizations.disclaimer,
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.of(_context).pop<bool>(false);
|
|
},
|
|
child: Text(currentAppLocalizations.exit),
|
|
),
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.of(_context).pop<bool>(true);
|
|
},
|
|
child: Text(currentAppLocalizations.agree),
|
|
),
|
|
],
|
|
child: Text(currentAppLocalizations.disclaimerDesc),
|
|
),
|
|
) ??
|
|
false;
|
|
}
|
|
|
|
void showNotifier(
|
|
String text, {
|
|
MessageLevel level = MessageLevel.info,
|
|
MessageActionState? actionState,
|
|
}) {
|
|
rootNavigatorKey.currentContext?.showNotifier(
|
|
text,
|
|
level: level,
|
|
actionState: actionState,
|
|
);
|
|
}
|
|
|
|
Future<void> openUrl(String url) async {
|
|
final res = await showMessage(
|
|
message: TextSpan(text: url),
|
|
title: currentAppLocalizations.externalLink,
|
|
confirmText: currentAppLocalizations.go,
|
|
);
|
|
if (res != true) {
|
|
return;
|
|
}
|
|
unawaited(launchUrl(Uri.parse(url)));
|
|
}
|
|
}
|
|
|
|
class _UpdatingMessageItem extends StatelessWidget {
|
|
final UpdatingMessage message;
|
|
|
|
const _UpdatingMessageItem({required this.message});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return DecorationListItem(
|
|
minVerticalPadding: 12,
|
|
title: TooltipText(
|
|
text: Text(message.label, maxLines: 1, overflow: TextOverflow.ellipsis),
|
|
),
|
|
subtitle: TooltipText(
|
|
text: Text(
|
|
message.message,
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
final dialogs = Dialogs._();
|