opt.: migrate fl_lib

This commit is contained in:
lollipopkit
2024-05-14 22:29:37 +08:00
parent 248430e5b0
commit 04dfede535
136 changed files with 686 additions and 3896 deletions

View File

@@ -1,11 +0,0 @@
import 'package:flutter/material.dart';
extension ContextX on BuildContext {
void pop<T extends Object?>([T? result]) {
Navigator.of(this).pop<T>(result);
}
bool get canPop => Navigator.of(this).canPop();
bool get isDark => Theme.of(this).brightness == Brightness.dark;
}

View File

@@ -1,232 +0,0 @@
import 'dart:async';
import 'package:choice/choice.dart';
import 'package:flutter/material.dart';
import 'package:toolbox/core/extension/context/common.dart';
import 'package:toolbox/core/extension/context/locale.dart';
import 'package:toolbox/data/res/store.dart';
import 'package:toolbox/view/widget/choice_chip.dart';
import 'package:toolbox/view/widget/tag.dart';
import 'package:toolbox/view/widget/val_builder.dart';
import '../../../data/res/ui.dart';
import '../../../view/widget/input_field.dart';
extension DialogX on BuildContext {
Future<T?> showRoundDialog<T>({
Widget? child,
List<Widget>? actions,
Widget? title,
bool barrierDismiss = true,
void Function(BuildContext)? onContext,
}) async {
return await showDialog<T>(
context: this,
barrierDismissible: barrierDismiss,
builder: (ctx) {
onContext?.call(ctx);
return AlertDialog(
title: title,
content: child,
actions: actions,
actionsPadding: const EdgeInsets.all(17),
);
},
);
}
Future<T> showLoadingDialog<T>({
required Future<T> Function() fn,
bool barrierDismiss = false,
}) async {
BuildContext? ctx;
showRoundDialog(
child: UIs.centerSizedLoading,
barrierDismiss: barrierDismiss,
onContext: (c) => ctx = c,
);
try {
return await fn();
} catch (e) {
rethrow;
} finally {
/// Wait for context to be unmounted
await Future.delayed(const Duration(milliseconds: 100));
if (ctx?.mounted == true) {
ctx?.pop();
}
}
}
static final _recoredPwd = <String, String>{};
/// Show a dialog to input password
///
/// [hostId] set it to null to skip remembering the password
Future<String?> showPwdDialog({
String? hostId,
String? title,
String? label,
}) async {
if (!mounted) return null;
return await showRoundDialog<String>(
title: Text(title ?? hostId ?? l10n.pwd),
child: Input(
controller: TextEditingController(text: _recoredPwd[hostId]),
autoFocus: true,
type: TextInputType.visiblePassword,
obscureText: true,
onSubmitted: (val) {
pop(val);
if (hostId != null && Stores.setting.rememberPwdInMem.fetch()) {
_recoredPwd[hostId] = val;
}
},
label: label ?? l10n.pwd,
),
);
}
Future<List<T>?> showPickDialog<T>({
required List<T?> items,
String Function(T)? name,
bool multi = true,
List<T>? initial,
bool clearable = false,
List<Widget>? actions,
}) async {
var vals = initial ?? <T>[];
final sure = await showRoundDialog<bool>(
title: Text(l10n.choose),
child: SingleChildScrollView(
child: Choice<T>(
onChanged: (value) => vals = value,
multiple: multi,
clearable: clearable,
value: vals,
builder: (state, _) {
return Wrap(
children: List<Widget>.generate(
items.length,
(index) {
final item = items[index];
if (item == null) return UIs.placeholder;
return ChoiceChipX<T>(
label: name?.call(item) ?? item.toString(),
state: state,
value: item,
);
},
),
);
},
),
),
actions: [
if (actions != null) ...actions,
TextButton(
onPressed: () => pop(true),
child: Text(l10n.ok),
),
],
);
if (sure == true && vals.isNotEmpty) {
return vals;
}
return null;
}
Future<T?> showPickSingleDialog<T>({
required List<T?> items,
String Function(T)? name,
T? initial,
bool clearable = false,
List<Widget>? actions,
}) async {
final vals = await showPickDialog<T>(
items: items,
name: name,
multi: false,
initial: initial == null ? null : [initial],
actions: actions,
);
if (vals != null && vals.isNotEmpty) {
return vals.first;
}
return null;
}
Future<List<T>?> showPickWithTagDialog<T>({
required List<T?> Function(String? tag) itemsBuilder,
required ValueNotifier<List<String>> tags,
String Function(T)? name,
List<T>? initial,
bool clearable = false,
bool multi = false,
List<Widget>? actions,
}) async {
var vals = initial ?? <T>[];
final tag = ValueNotifier<String?>(null);
final sure = await showRoundDialog<bool>(
title: Text(l10n.choose),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListenableBuilder(
listenable: tag,
builder: (_, __) => TagSwitcher(
tags: tags,
width: 300,
initTag: tag.value,
onTagChanged: (e) => tag.value = e,
),
),
const Divider(),
SingleChildScrollView(
child: ValBuilder(
listenable: tag,
builder: (val) {
final items = itemsBuilder(val);
return Choice<T>(
onChanged: (value) => vals = value,
multiple: multi,
clearable: clearable,
value: vals,
builder: (state, _) {
return Wrap(
children: List<Widget>.generate(
items.length,
(index) {
final item = items[index];
if (item == null) return UIs.placeholder;
return ChoiceChipX<T>(
label: name?.call(item) ?? item.toString(),
state: state,
value: item,
);
},
),
);
},
);
},
),
)
],
),
actions: [
if (actions != null) ...actions,
TextButton(
onPressed: () => pop(true),
child: Text(l10n.ok),
),
],
);
if (sure == true && vals.isNotEmpty) {
return vals;
}
return null;
}
}

View File

@@ -1,24 +0,0 @@
import 'package:flutter/material.dart';
extension SnackBarX on BuildContext {
void showSnackBar(String text) =>
ScaffoldMessenger.of(this).showSnackBar(SnackBar(
content: Text(text),
behavior: SnackBarBehavior.floating,
));
void showSnackBarWithAction(
String content,
String action,
GestureTapCallback onTap,
) {
ScaffoldMessenger.of(this).showSnackBar(SnackBar(
content: Text(content),
behavior: SnackBarBehavior.floating,
action: SnackBarAction(
label: action,
onPressed: onTap,
),
));
}
}