diff --git a/lib/common/widgets/color_palette.dart b/lib/common/widgets/color_palette.dart index 12020efb4..3dda88cfb 100644 --- a/lib/common/widgets/color_palette.dart +++ b/lib/common/widgets/color_palette.dart @@ -55,18 +55,19 @@ class ColorPalette extends StatelessWidget { ], ); } - return Container( - width: 50, - height: 50, - padding: const EdgeInsets.all(6), - decoration: showBgColor - ? BoxDecoration( - color: colorScheme.onInverseSurface, - borderRadius: StyleString.mdRadius, - ) - : null, - child: child, - ); + if (showBgColor) { + return Container( + width: 50, + height: 50, + padding: const EdgeInsets.all(6), + decoration: BoxDecoration( + color: colorScheme.onInverseSurface, + borderRadius: StyleString.mdRadius, + ), + child: child, + ); + } + return child; } static Widget _coloredBox(Color color) => Expanded( diff --git a/lib/common/widgets/dialog/dialog.dart b/lib/common/widgets/dialog/dialog.dart index edd9a713f..23ffcf14f 100644 --- a/lib/common/widgets/dialog/dialog.dart +++ b/lib/common/widgets/dialog/dialog.dart @@ -11,34 +11,32 @@ Future showConfirmDialog({ assert(content is String? || content is Widget); return await showDialog( context: context, - builder: (context) { - return AlertDialog( - title: Text(title), - content: content is String - ? Text(content) - : content is Widget - ? content - : null, - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), + builder: (context) => AlertDialog( + title: Text(title), + content: content is String + ? Text(content) + : content is Widget + ? content + : null, + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, ), ), - TextButton( - onPressed: () { - Get.back(result: true); - onConfirm?.call(); - }, - child: const Text('确认'), - ), - ], - ); - }, + ), + TextButton( + onPressed: () { + Get.back(result: true); + onConfirm?.call(); + }, + child: const Text('确认'), + ), + ], + ), ) ?? false; } diff --git a/lib/common/widgets/dialog/report.dart b/lib/common/widgets/dialog/report.dart index cdc66e49d..feb54660e 100644 --- a/lib/common/widgets/dialog/report.dart +++ b/lib/common/widgets/dialog/report.dart @@ -19,116 +19,114 @@ Future autoWrapReportDialog( late final key = GlobalKey>(); return showDialog( context: context, - builder: (context) { - return AlertDialog( - title: const Text('举报'), - titlePadding: const .only(left: 22, top: 16, right: 22), - contentPadding: const .symmetric(vertical: 5), - actionsPadding: const .only(left: 16, right: 16, bottom: 10), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Flexible( - child: SingleChildScrollView( - child: AnimatedSize( - duration: const Duration(milliseconds: 200), - child: Builder( - builder: (context) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Padding( - padding: .only(left: 22, right: 22, bottom: 5), - child: Text('请选择举报的理由:'), + builder: (context) => AlertDialog( + title: const Text('举报'), + titlePadding: const .only(left: 22, top: 16, right: 22), + contentPadding: const .symmetric(vertical: 5), + actionsPadding: const .only(left: 16, right: 16, bottom: 10), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Flexible( + child: SingleChildScrollView( + child: AnimatedSize( + duration: const Duration(milliseconds: 200), + child: Builder( + builder: (context) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: .only(left: 22, right: 22, bottom: 5), + child: Text('请选择举报的理由:'), + ), + RadioGroup( + onChanged: (value) { + reasonType = value; + (context as Element).markNeedsBuild(); + }, + groupValue: reasonType, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: options.entries.map((entry) { + return WrapRadioOptionsGroup( + groupTitle: entry.key, + options: entry.value, + ); + }).toList(), ), - RadioGroup( - onChanged: (value) { - reasonType = value; - (context as Element).markNeedsBuild(); - }, - groupValue: reasonType, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: options.entries.map((entry) { - return WrapRadioOptionsGroup( - groupTitle: entry.key, - options: entry.value, - ); - }).toList(), - ), - ), - if (reasonType == 0) - Padding( - padding: const .only(left: 22, top: 5, right: 22), - child: TextFormField( - key: key, - autofocus: true, - minLines: 2, - maxLines: 4, - initialValue: reasonDesc, - decoration: const InputDecoration( - labelText: '为帮助审核人员更快处理,请补充问题类型和出现位置等详细信息', - border: OutlineInputBorder(), - contentPadding: .all(10), - labelStyle: TextStyle(fontSize: 14), - floatingLabelStyle: TextStyle(fontSize: 14), - ), - onChanged: (value) => reasonDesc = value, - validator: (value) => - value.isNullOrEmpty ? '理由不能为空' : null, + ), + if (reasonType == 0) + Padding( + padding: const .only(left: 22, top: 5, right: 22), + child: TextFormField( + key: key, + autofocus: true, + minLines: 2, + maxLines: 4, + initialValue: reasonDesc, + decoration: const InputDecoration( + labelText: '为帮助审核人员更快处理,请补充问题类型和出现位置等详细信息', + border: OutlineInputBorder(), + contentPadding: .all(10), + labelStyle: TextStyle(fontSize: 14), + floatingLabelStyle: TextStyle(fontSize: 14), ), + onChanged: (value) => reasonDesc = value, + validator: (value) => + value.isNullOrEmpty ? '理由不能为空' : null, ), - ], - ), + ), + ], ), ), ), ), - if (ban) - Padding( - padding: const EdgeInsets.only(left: 14, top: 6), - child: CheckBoxText( - text: '拉黑该用户', - onChanged: (value) => banUid = value, - ), + ), + if (ban) + Padding( + padding: const EdgeInsets.only(left: 14, top: 6), + child: CheckBoxText( + text: '拉黑该用户', + onChanged: (value) => banUid = value, ), - ], - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle(color: ColorScheme.of(context).outline), ), - ), - TextButton( - onPressed: () async { - if (reasonType == null || - (reasonType == 0 && key.currentState?.validate() != true)) { - return; - } - SmartDialog.showLoading(); - try { - final res = await onSuccess(reasonType!, reasonDesc, banUid); - SmartDialog.dismiss(); - if (res.isSuccess) { - Get.back(); - SmartDialog.showToast('举报成功'); - } else { - res.toast(); - } - } catch (e, s) { - SmartDialog.dismiss(); - SmartDialog.showToast('提交失败:$e'); - Utils.reportError(e, s); - } - }, - child: const Text('确定'), - ), ], - ); - }, + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle(color: ColorScheme.of(context).outline), + ), + ), + TextButton( + onPressed: () async { + if (reasonType == null || + (reasonType == 0 && key.currentState?.validate() != true)) { + return; + } + SmartDialog.showLoading(); + try { + final res = await onSuccess(reasonType!, reasonDesc, banUid); + SmartDialog.dismiss(); + if (res.isSuccess) { + Get.back(); + SmartDialog.showToast('举报成功'); + } else { + res.toast(); + } + } catch (e, s) { + SmartDialog.dismiss(); + SmartDialog.showToast('提交失败:$e'); + Utils.reportError(e, s); + } + }, + child: const Text('确定'), + ), + ], + ), ); } diff --git a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart index d5fab11da..f0ff69414 100644 --- a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart +++ b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart @@ -415,79 +415,77 @@ class _InteractiveviewerGalleryState extends State HapticFeedback.mediumImpact(); showDialog( context: context, - builder: (context) { - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.symmetric(vertical: 12), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (PlatformUtils.isMobile) - ListTile( - onTap: () { - Get.back(); - ImageUtils.onShareImg(item.url); - }, - dense: true, - title: const Text('分享', style: TextStyle(fontSize: 14)), - ), + builder: (context) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (PlatformUtils.isMobile) ListTile( onTap: () { Get.back(); - Utils.copyText(item.url); + ImageUtils.onShareImg(item.url); }, dense: true, - title: const Text('复制链接', style: TextStyle(fontSize: 14)), + title: const Text('分享', style: TextStyle(fontSize: 14)), ), + ListTile( + onTap: () { + Get.back(); + Utils.copyText(item.url); + }, + dense: true, + title: const Text('复制链接', style: TextStyle(fontSize: 14)), + ), + ListTile( + onTap: () { + Get.back(); + ImageUtils.downloadImg([item.url]); + }, + dense: true, + title: const Text('保存图片', style: TextStyle(fontSize: 14)), + ), + if (PlatformUtils.isDesktop) ListTile( onTap: () { Get.back(); - ImageUtils.downloadImg([item.url]); + PageUtils.launchURL(item.url); }, dense: true, - title: const Text('保存图片', style: TextStyle(fontSize: 14)), + title: const Text('网页打开', style: TextStyle(fontSize: 14)), + ) + else if (widget.sources.length > 1) + ListTile( + onTap: () { + Get.back(); + ImageUtils.downloadImg( + widget.sources.map((item) => item.url).toList(), + ); + }, + dense: true, + title: const Text('保存全部图片', style: TextStyle(fontSize: 14)), ), - if (PlatformUtils.isDesktop) - ListTile( - onTap: () { - Get.back(); - PageUtils.launchURL(item.url); - }, - dense: true, - title: const Text('网页打开', style: TextStyle(fontSize: 14)), - ) - else if (widget.sources.length > 1) - ListTile( - onTap: () { - Get.back(); - ImageUtils.downloadImg( - widget.sources.map((item) => item.url).toList(), - ); - }, - dense: true, - title: const Text('保存全部图片', style: TextStyle(fontSize: 14)), + if (item.sourceType == SourceType.livePhoto) + ListTile( + onTap: () { + Get.back(); + ImageUtils.downloadLivePhoto( + url: item.url, + liveUrl: item.liveUrl!, + width: item.width!, + height: item.height!, + ); + }, + dense: true, + title: Text( + '保存${Platform.isIOS ? ' Live Photo' : '视频'}', + style: const TextStyle(fontSize: 14), ), - if (item.sourceType == SourceType.livePhoto) - ListTile( - onTap: () { - Get.back(); - ImageUtils.downloadLivePhoto( - url: item.url, - liveUrl: item.liveUrl!, - width: item.width!, - height: item.height!, - ); - }, - dense: true, - title: Text( - '保存${Platform.isIOS ? ' Live Photo' : '视频'}', - style: const TextStyle(fontSize: 14), - ), - ), - ], - ), - ); - }, + ), + ], + ), + ), ); } diff --git a/lib/common/widgets/video_popup_menu.dart b/lib/common/widgets/video_popup_menu.dart index fd08d17ed..60f001617 100644 --- a/lib/common/widgets/video_popup_menu.dart +++ b/lib/common/widgets/video_popup_menu.dart @@ -102,19 +102,17 @@ class VideoPopupMenu extends StatelessWidget { if (res != null && context.mounted) { showDialog( context: context, - builder: (context) { - return Dialog( - child: Padding( - padding: const .symmetric(vertical: 14), - child: AiConclusionPanel.buildContent( - context, - Theme.of(context), - res, - tap: false, - ), + builder: (context) => Dialog( + child: Padding( + padding: const .symmetric(vertical: 14), + child: AiConclusionPanel.buildContent( + context, + Theme.of(context), + res, + tap: false, ), - ); - }, + ), + ), ); } }, @@ -242,74 +240,70 @@ class VideoPopupMenu extends StatelessWidget { } else { showDialog( context: context, - builder: (context) { - return AlertDialog( - content: SingleChildScrollView( - child: Column( - children: [ - const SizedBox(height: 5), - const Text("web端暂不支持精细选择"), - const SizedBox(height: 5), - Wrap( - spacing: 5.0, - runSpacing: 2.0, - children: [ - FilledButton.tonal( - onPressed: () async { - Get.back(); - SmartDialog.showLoading( - msg: '正在提交', - ); - final res = - await VideoHttp.dislikeVideo( - bvid: videoItem.bvid!, - type: true, - ); - SmartDialog.dismiss(); - if (res.isSuccess) { - SmartDialog.showToast('点踩成功'); - onRemove?.call(); - } else { - res.toast(); - } - }, - style: FilledButton.styleFrom( - visualDensity: - VisualDensity.compact, - ), - child: const Text("点踩"), + builder: (context) => AlertDialog( + content: SingleChildScrollView( + child: Column( + children: [ + const SizedBox(height: 5), + const Text("web端暂不支持精细选择"), + const SizedBox(height: 5), + Wrap( + spacing: 5.0, + runSpacing: 2.0, + children: [ + FilledButton.tonal( + onPressed: () async { + Get.back(); + SmartDialog.showLoading( + msg: '正在提交', + ); + final res = + await VideoHttp.dislikeVideo( + bvid: videoItem.bvid!, + type: true, + ); + SmartDialog.dismiss(); + if (res.isSuccess) { + SmartDialog.showToast('点踩成功'); + onRemove?.call(); + } else { + res.toast(); + } + }, + style: FilledButton.styleFrom( + visualDensity: VisualDensity.compact, ), - FilledButton.tonal( - onPressed: () async { - Get.back(); - SmartDialog.showLoading( - msg: '正在提交', - ); - final res = - await VideoHttp.dislikeVideo( - bvid: videoItem.bvid!, - type: false, - ); - SmartDialog.dismiss(); - SmartDialog.showToast( - res.isSuccess - ? '取消踩' - : res.toString(), - ); - }, - style: FilledButton.styleFrom( - visualDensity: - VisualDensity.compact, - ), - child: const Text("撤销"), + child: const Text("点踩"), + ), + FilledButton.tonal( + onPressed: () async { + Get.back(); + SmartDialog.showLoading( + msg: '正在提交', + ); + final res = + await VideoHttp.dislikeVideo( + bvid: videoItem.bvid!, + type: false, + ); + SmartDialog.dismiss(); + SmartDialog.showToast( + res.isSuccess + ? '取消踩' + : res.toString(), + ); + }, + style: FilledButton.styleFrom( + visualDensity: VisualDensity.compact, ), - ], - ), - ], - ), + child: const Text("撤销"), + ), + ], + ), + ], ), - ); - }, + ), + ), ); } }, diff --git a/lib/pages/about/view.dart b/lib/pages/about/view.dart index 1a9571fa4..77d3089cc 100644 --- a/lib/pages/about/view.dart +++ b/lib/pages/about/view.dart @@ -69,20 +69,18 @@ class _AboutPageState extends State { void _showDialog() => showDialog( context: context, - builder: (context) { - return AlertDialog( - constraints: StyleString.dialogFixedConstraints, - content: TextField( - autofocus: true, - onSubmitted: (value) { - Get.back(); - if (value.isNotEmpty) { - PageUtils.handleWebview(value, inApp: true); - } - }, - ), - ); - }, + builder: (context) => AlertDialog( + constraints: StyleString.dialogFixedConstraints, + content: TextField( + autofocus: true, + onSubmitted: (value) { + Get.back(); + if (value.isNotEmpty) { + PageUtils.handleWebview(value, inApp: true); + } + }, + ), + ), ); @override @@ -454,61 +452,59 @@ Future showImportExportDialog( showDialog( context: context, - builder: (context) { - return AlertDialog( - title: Text('输入$title'), - constraints: StyleString.dialogFixedConstraints, - content: TextFormField( - key: key, - minLines: 4, - maxLines: 12, - autofocus: true, - decoration: const InputDecoration( - border: OutlineInputBorder(), - errorMaxLines: 3, - ), - validator: (value) { - if (forceErrorText != null) return forceErrorText; - try { - json = jsonDecode(value!) as T; - return null; - } catch (e) { - if (e is FormatException) {} - return '解析json失败:$e'; - } - }, + builder: (context) => AlertDialog( + title: Text('输入$title'), + constraints: StyleString.dialogFixedConstraints, + content: TextFormField( + key: key, + minLines: 4, + maxLines: 12, + autofocus: true, + decoration: const InputDecoration( + border: OutlineInputBorder(), + errorMaxLines: 3, ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), + validator: (value) { + if (forceErrorText != null) return forceErrorText; + try { + json = jsonDecode(value!) as T; + return null; + } catch (e) { + if (e is FormatException) {} + return '解析json失败:$e'; + } + }, + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, ), ), - TextButton( - onPressed: () async { - if (key.currentState?.validate() == true) { - try { - if (await fromJson(json)) { - Get.back(); - SmartDialog.showToast('导入成功'); - return; - } - } catch (e) { - forceErrorText = '导入失败:$e'; + ), + TextButton( + onPressed: () async { + if (key.currentState?.validate() == true) { + try { + if (await fromJson(json)) { + Get.back(); + SmartDialog.showToast('导入成功'); + return; } - key.currentState?.validate(); - forceErrorText = null; + } catch (e) { + forceErrorText = '导入失败:$e'; } - }, - child: const Text('确定'), - ), - ], - ); - }, + key.currentState?.validate(); + forceErrorText = null; + } + }, + child: const Text('确定'), + ), + ], + ), ); }, ), diff --git a/lib/pages/audio/controller.dart b/lib/pages/audio/controller.dart index a8db88c4c..c41907e00 100644 --- a/lib/pages/audio/controller.dart +++ b/lib/pages/audio/controller.dart @@ -475,65 +475,44 @@ class AudioController extends GetxController } void actionShareVideo(BuildContext context) { + final audioUrl = isVideo + ? '${HttpString.baseUrl}/video/${IdUtils.av2bv(oid.toInt())}' + : '${HttpString.baseUrl}/audio/au$oid'; showDialog( context: context, - builder: (_) { - final audioUrl = isVideo - ? '${HttpString.baseUrl}/video/${IdUtils.av2bv(oid.toInt())}' - : '${HttpString.baseUrl}/audio/au$oid'; - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.symmetric(vertical: 12), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - dense: true, - title: const Text( - '复制链接', - style: TextStyle(fontSize: 14), - ), - onTap: () { - Get.back(); - Utils.copyText(audioUrl); - }, + builder: (_) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + dense: true, + title: const Text( + '复制链接', + style: TextStyle(fontSize: 14), ), - ListTile( - dense: true, - title: const Text( - '其它app打开', - style: TextStyle(fontSize: 14), - ), - onTap: () { - Get.back(); - PageUtils.launchURL(audioUrl); - }, + onTap: () { + Get.back(); + Utils.copyText(audioUrl); + }, + ), + ListTile( + dense: true, + title: const Text( + '其它app打开', + style: TextStyle(fontSize: 14), ), - if (PlatformUtils.isMobile) - ListTile( - dense: true, - title: const Text( - '分享视频', - style: TextStyle(fontSize: 14), - ), - onTap: () { - Get.back(); - if (audioItem.value case DetailItem( - :final arc, - :final owner, - )) { - Utils.shareText( - '${arc.title} ' - 'UP主: ${owner.name}' - ' - $audioUrl', - ); - } - }, - ), + onTap: () { + Get.back(); + PageUtils.launchURL(audioUrl); + }, + ), + if (PlatformUtils.isMobile) ListTile( dense: true, title: const Text( - '分享至动态', + '分享视频', style: TextStyle(fontSize: 14), ), onTap: () { @@ -542,57 +521,76 @@ class AudioController extends GetxController :final arc, :final owner, )) { - showModalBottomSheet( - context: context, - isScrollControlled: true, - useSafeArea: true, - builder: (context) => RepostPanel( - rid: oid.toInt(), - dynType: isVideo ? 8 : 256, - pic: arc.cover, - title: arc.title, - uname: owner.name, - ), + Utils.shareText( + '${arc.title} ' + 'UP主: ${owner.name}' + ' - $audioUrl', ); } }, ), - if (isVideo) - ListTile( - dense: true, - title: const Text( - '分享至消息', - style: TextStyle(fontSize: 14), - ), - onTap: () { - Get.back(); - if (audioItem.value case DetailItem( - :final arc, - :final owner, - )) { - try { - PageUtils.pmShare( - context, - content: { - "id": oid.toString(), - "title": arc.title, - "headline": arc.title, - "source": 5, - "thumb": arc.cover, - "author": owner.name, - "author_id": owner.mid.toString(), - }, - ); - } catch (e) { - SmartDialog.showToast(e.toString()); - } - } - }, + ListTile( + dense: true, + title: const Text( + '分享至动态', + style: TextStyle(fontSize: 14), + ), + onTap: () { + Get.back(); + if (audioItem.value case DetailItem( + :final arc, + :final owner, + )) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + useSafeArea: true, + builder: (context) => RepostPanel( + rid: oid.toInt(), + dynType: isVideo ? 8 : 256, + pic: arc.cover, + title: arc.title, + uname: owner.name, + ), + ); + } + }, + ), + if (isVideo) + ListTile( + dense: true, + title: const Text( + '分享至消息', + style: TextStyle(fontSize: 14), ), - ], - ), - ); - }, + onTap: () { + Get.back(); + if (audioItem.value case DetailItem( + :final arc, + :final owner, + )) { + try { + PageUtils.pmShare( + context, + content: { + "id": oid.toString(), + "title": arc.title, + "headline": arc.title, + "source": 5, + "thumb": arc.cover, + "author": owner.name, + "author_id": owner.mid.toString(), + }, + ); + } catch (e) { + SmartDialog.showToast(e.toString()); + } + } + }, + ), + ], + ), + ), ); } diff --git a/lib/pages/danmaku_block/view.dart b/lib/pages/danmaku_block/view.dart index da5e6a0d0..5d85af8df 100644 --- a/lib/pages/danmaku_block/view.dart +++ b/lib/pages/danmaku_block/view.dart @@ -150,59 +150,57 @@ class _DanmakuBlockPageState extends State { final isUid = type == DmBlockType.uid; showDialog( context: context, - builder: (context) { - return AlertDialog( - title: Text('${itemId != null ? "编辑" : "添加新的"}${type.label}规则'), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(hintText), - TextFormField( - autofocus: true, - initialValue: filter, - onChanged: (value) => filter = value, - keyboardType: isUid ? TextInputType.number : null, - inputFormatters: isUid - ? [FilteringTextInputFormatter.digitsOnly] - : null, - ), - ], - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - child: const Text('确定'), - onPressed: () async { - if (filter != initFilter) { - Get.back(); - if (itemId != null) { - await _controller.danmakuFilterDel( - type.index, - itemIndex!, - itemId, - ); - } - await _controller.danmakuFilterAdd( - filter: filter, - type: type.index, - ); - } else { - SmartDialog.showToast( - '输入内容${filter.isEmpty ? "不能为空" : "与上次相同"}', - ); - } - }, + builder: (context) => AlertDialog( + title: Text('${itemId != null ? "编辑" : "添加新的"}${type.label}规则'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(hintText), + TextFormField( + autofocus: true, + initialValue: filter, + onChanged: (value) => filter = value, + keyboardType: isUid ? TextInputType.number : null, + inputFormatters: isUid + ? [FilteringTextInputFormatter.digitsOnly] + : null, ), ], - ); - }, + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + child: const Text('确定'), + onPressed: () async { + if (filter != initFilter) { + Get.back(); + if (itemId != null) { + await _controller.danmakuFilterDel( + type.index, + itemIndex!, + itemId, + ); + } + await _controller.danmakuFilterAdd( + filter: filter, + type: type.index, + ); + } else { + SmartDialog.showToast( + '输入内容${filter.isEmpty ? "不能为空" : "与上次相同"}', + ); + } + }, + ), + ], + ), ); } } diff --git a/lib/pages/download/detail/widgets/item.dart b/lib/pages/download/detail/widgets/item.dart index 1b8006830..644f3ebce 100644 --- a/lib/pages/download/detail/widgets/item.dart +++ b/lib/pages/download/detail/widgets/item.dart @@ -61,51 +61,49 @@ class DetailItem extends StatelessWidget { void onLongPress() => canDel && !enableMultiSelect ? showDialog( context: context, - builder: (context) { - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.symmetric(vertical: 12), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - onTap: () { - Get.back(); - showConfirmDialog( - context: context, - title: '确定删除该视频?', - onConfirm: onDelete, - ); - }, - dense: true, - title: const Text( - '删除', - style: TextStyle(fontSize: 14), - ), + builder: (context) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + onTap: () { + Get.back(); + showConfirmDialog( + context: context, + title: '确定删除该视频?', + onConfirm: onDelete, + ); + }, + dense: true, + title: const Text( + '删除', + style: TextStyle(fontSize: 14), ), - ListTile( - onTap: () async { - Get.back(); - final res = await downloadService.downloadDanmaku( - entry: entry, - isUpdate: true, - ); - if (res) { - SmartDialog.showToast('更新成功'); - } else { - SmartDialog.showToast('更新失败'); - } - }, - dense: true, - title: const Text( - '更新弹幕', - style: TextStyle(fontSize: 14), - ), + ), + ListTile( + onTap: () async { + Get.back(); + final res = await downloadService.downloadDanmaku( + entry: entry, + isUpdate: true, + ); + if (res) { + SmartDialog.showToast('更新成功'); + } else { + SmartDialog.showToast('更新失败'); + } + }, + dense: true, + title: const Text( + '更新弹幕', + style: TextStyle(fontSize: 14), ), - ], - ), - ); - }, + ), + ], + ), + ), ) : null; diff --git a/lib/pages/download/view.dart b/lib/pages/download/view.dart index 349b6b968..a58a616cb 100644 --- a/lib/pages/download/view.dart +++ b/lib/pages/download/view.dart @@ -235,62 +235,60 @@ class _DownloadPageState extends State { ? null : showDialog( context: context, - builder: (context) { - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.symmetric(vertical: 12), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - onTap: () { - Get.back(); - showConfirmDialog( - context: context, - title: '确定删除?', - onConfirm: () async { - await GStorage.watchProgress.deleteAll( - pageInfo.entries.map((e) => e.cid.toString()), - ); - _downloadService.deletePage( - pageDirPath: pageInfo.dirPath, - ); - }, - ); - }, - dense: true, - title: const Text( - '删除', - style: TextStyle(fontSize: 14), - ), + builder: (context) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + onTap: () { + Get.back(); + showConfirmDialog( + context: context, + title: '确定删除?', + onConfirm: () async { + await GStorage.watchProgress.deleteAll( + pageInfo.entries.map((e) => e.cid.toString()), + ); + _downloadService.deletePage( + pageDirPath: pageInfo.dirPath, + ); + }, + ); + }, + dense: true, + title: const Text( + '删除', + style: TextStyle(fontSize: 14), ), - ListTile( - onTap: () async { - Get.back(); - final res = await Future.wait( - pageInfo.entries.map( - (e) => _downloadService.downloadDanmaku( - entry: e, - isUpdate: true, - ), + ), + ListTile( + onTap: () async { + Get.back(); + final res = await Future.wait( + pageInfo.entries.map( + (e) => _downloadService.downloadDanmaku( + entry: e, + isUpdate: true, ), - ); - if (res.every((e) => e)) { - SmartDialog.showToast('更新成功'); - } else { - SmartDialog.showToast('更新失败'); - } - }, - dense: true, - title: const Text( - '更新弹幕', - style: TextStyle(fontSize: 14), - ), + ), + ); + if (res.every((e) => e)) { + SmartDialog.showToast('更新成功'); + } else { + SmartDialog.showToast('更新失败'); + } + }, + dense: true, + title: const Text( + '更新弹幕', + style: TextStyle(fontSize: 14), ), - ], - ), - ); - }, + ), + ], + ), + ), ); final first = pageInfo.entries.first; return Material( diff --git a/lib/pages/fav_create/view.dart b/lib/pages/fav_create/view.dart index a2951cdc4..9c3569a99 100644 --- a/lib/pages/fav_create/view.dart +++ b/lib/pages/fav_create/view.dart @@ -192,40 +192,38 @@ class _CreateFavPageState extends State { if (_cover?.isNotEmpty == true) { showDialog( context: context, - builder: (_) { - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const .symmetric(vertical: 12), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - dense: true, - onTap: () { - Get.back(); - _pickImg(context, theme); - }, - title: const Text( - '替换封面', - style: TextStyle(fontSize: 14), - ), + builder: (_) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const .symmetric(vertical: 12), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + dense: true, + onTap: () { + Get.back(); + _pickImg(context, theme); + }, + title: const Text( + '替换封面', + style: TextStyle(fontSize: 14), ), - ListTile( - dense: true, - onTap: () { - Get.back(); - _cover = null; - (context as Element).markNeedsBuild(); - }, - title: const Text( - '移除封面', - style: TextStyle(fontSize: 14), - ), + ), + ListTile( + dense: true, + onTap: () { + Get.back(); + _cover = null; + (context as Element).markNeedsBuild(); + }, + title: const Text( + '移除封面', + style: TextStyle(fontSize: 14), ), - ], - ), - ); - }, + ), + ], + ), + ), ); } else { _pickImg(context, theme); diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index 881fdb162..0c545a9b8 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -217,28 +217,26 @@ class FavVideoCardH extends StatelessWidget { iconColor: theme.colorScheme.outline, onPressed: () => showDialog( context: context, - builder: (context) { - return AlertDialog( - title: const Text('提示'), - content: const Text('要取消收藏吗?'), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle(color: theme.colorScheme.outline), - ), + builder: (context) => AlertDialog( + title: const Text('提示'), + content: const Text('要取消收藏吗?'), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle(color: theme.colorScheme.outline), ), - TextButton( - onPressed: () { - Get.back(); - ctr!.onCancelFav(index!, item.id!, item.type!); - }, - child: const Text('确定取消'), - ), - ], - ); - }, + ), + TextButton( + onPressed: () { + Get.back(); + ctr!.onCancelFav(index!, item.id!, item.type!); + }, + child: const Text('确定取消'), + ), + ], + ), ), ), ), diff --git a/lib/pages/follow/view.dart b/lib/pages/follow/view.dart index e755f5d32..018b37283 100644 --- a/lib/pages/follow/view.dart +++ b/lib/pages/follow/view.dart @@ -176,64 +176,62 @@ class _FollowPageState extends State { void _onHandleTag(int index, MemberTagItemModel item) { showDialog( context: context, - builder: (context) { - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.symmetric(vertical: 12), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - onTap: () { - Get.back(); - String tagName = item.name!; - showConfirmDialog( - context: context, - title: '编辑分组名称', - content: TextFormField( - autofocus: true, - initialValue: tagName, - onChanged: (value) => tagName = value, - inputFormatters: [ - LengthLimitingTextInputFormatter(16), - ], - decoration: const InputDecoration( - border: OutlineInputBorder(), - ), + builder: (context) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + onTap: () { + Get.back(); + String tagName = item.name!; + showConfirmDialog( + context: context, + title: '编辑分组名称', + content: TextFormField( + autofocus: true, + initialValue: tagName, + onChanged: (value) => tagName = value, + inputFormatters: [ + LengthLimitingTextInputFormatter(16), + ], + decoration: const InputDecoration( + border: OutlineInputBorder(), ), - onConfirm: () { - if (tagName.isNotEmpty) { - _followController.onUpdateTag(item, tagName); - } - }, - ); - }, - dense: true, - title: const Text( - '修改名称', - style: TextStyle(fontSize: 14), - ), + ), + onConfirm: () { + if (tagName.isNotEmpty) { + _followController.onUpdateTag(item, tagName); + } + }, + ); + }, + dense: true, + title: const Text( + '修改名称', + style: TextStyle(fontSize: 14), ), - ListTile( - onTap: () { - Get.back(); - showConfirmDialog( - context: context, - title: '删除分组', - content: '删除后,该分组下的用户依旧保留?', - onConfirm: () => _followController.onDelTag(item.tagid!), - ); - }, - dense: true, - title: const Text( - '删除分组', - style: TextStyle(fontSize: 14), - ), + ), + ListTile( + onTap: () { + Get.back(); + showConfirmDialog( + context: context, + title: '删除分组', + content: '删除后,该分组下的用户依旧保留?', + onConfirm: () => _followController.onDelTag(item.tagid!), + ); + }, + dense: true, + title: const Text( + '删除分组', + style: TextStyle(fontSize: 14), ), - ], - ), - ); - }, + ), + ], + ), + ), ); } diff --git a/lib/pages/history/base_controller.dart b/lib/pages/history/base_controller.dart index e44f2ca37..1e7d919ac 100644 --- a/lib/pages/history/base_controller.dart +++ b/lib/pages/history/base_controller.dart @@ -18,81 +18,77 @@ class HistoryBaseController extends GetxController { void onClearHistory(BuildContext context, VoidCallback onSuccess) { showDialog( context: context, - builder: (context) { - return AlertDialog( - title: const Text('提示'), - content: const Text('啊叻?你要清空历史记录功能吗?'), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), + builder: (context) => AlertDialog( + title: const Text('提示'), + content: const Text('啊叻?你要清空历史记录功能吗?'), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), ), - TextButton( - onPressed: () async { - Get.back(); - SmartDialog.showLoading(msg: '请求中'); - final res = await UserHttp.clearHistory(account: account); - SmartDialog.dismiss(); - if (res.isSuccess) { - SmartDialog.showToast('清空观看历史'); - onSuccess(); - } else { - res.toast(); - } - }, - child: const Text('确认清空'), - ), - ], - ); - }, + ), + TextButton( + onPressed: () async { + Get.back(); + SmartDialog.showLoading(msg: '请求中'); + final res = await UserHttp.clearHistory(account: account); + SmartDialog.dismiss(); + if (res.isSuccess) { + SmartDialog.showToast('清空观看历史'); + onSuccess(); + } else { + res.toast(); + } + }, + child: const Text('确认清空'), + ), + ], + ), ); } // 暂停观看历史 void onPauseHistory(BuildContext context) { + final pauseStatus = !this.pauseStatus.value; showDialog( context: context, - builder: (context) { - final pauseStatus = !this.pauseStatus.value; - return AlertDialog( - title: const Text('提示'), - content: Text(pauseStatus ? '啊叻?你要暂停历史记录功能吗?' : '啊叻?要恢复历史记录功能吗?'), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), + builder: (context) => AlertDialog( + title: const Text('提示'), + content: Text(pauseStatus ? '啊叻?你要暂停历史记录功能吗?' : '啊叻?要恢复历史记录功能吗?'), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), ), - TextButton( - onPressed: () async { - SmartDialog.showLoading(msg: '请求中'); - final res = await UserHttp.pauseHistory( + ), + TextButton( + onPressed: () async { + SmartDialog.showLoading(msg: '请求中'); + final res = await UserHttp.pauseHistory( + pauseStatus, + account: account, + ); + SmartDialog.dismiss(); + if (res.isSuccess) { + SmartDialog.showToast(pauseStatus ? '暂停观看历史' : '恢复观看历史'); + this.pauseStatus.value = pauseStatus; + GStorage.localCache.put( + LocalCacheKey.historyPause, pauseStatus, - account: account, ); - SmartDialog.dismiss(); - if (res.isSuccess) { - SmartDialog.showToast(pauseStatus ? '暂停观看历史' : '恢复观看历史'); - this.pauseStatus.value = pauseStatus; - GStorage.localCache.put( - LocalCacheKey.historyPause, - pauseStatus, - ); - } else { - res.toast(); - } - Get.back(); - }, - child: Text(pauseStatus ? '确认暂停' : '确认恢复'), - ), - ], - ); - }, + } else { + res.toast(); + } + Get.back(); + }, + child: Text(pauseStatus ? '确认暂停' : '确认恢复'), + ), + ], + ), ); } } diff --git a/lib/pages/hot/controller.dart b/lib/pages/hot/controller.dart index 8b35af31f..a74f01897 100644 --- a/lib/pages/hot/controller.dart +++ b/lib/pages/hot/controller.dart @@ -2,13 +2,9 @@ import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/models/model_hot_video_item.dart'; import 'package:PiliPlus/pages/common/common_list_controller.dart'; -import 'package:PiliPlus/utils/storage_pref.dart'; -import 'package:get/get.dart'; class HotController extends CommonListController, HotVideoItemModel> { - final RxBool showHotRcmd = Pref.showHotRcmd.obs; - @override void onInit() { super.onInit(); diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart index 3ee7e3f45..ff7cc74d7 100644 --- a/lib/pages/hot/view.dart +++ b/lib/pages/hot/view.dart @@ -11,6 +11,7 @@ import 'package:PiliPlus/pages/home/controller.dart'; import 'package:PiliPlus/pages/hot/controller.dart'; import 'package:PiliPlus/pages/rank/view.dart'; import 'package:PiliPlus/utils/grid.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -67,65 +68,59 @@ class _HotPageState extends CommonPageState controller: controller.scrollController, slivers: [ SliverToBoxAdapter( - child: Obx( - () => controller.showHotRcmd.value - ? Padding( - padding: const EdgeInsets.only( - left: 12, - top: 12, - right: 12, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - _buildEntranceItem( - iconUrl: - 'http://i0.hdslb.com/bfs/archive/a3f11218aaf4521b4967db2ae164ecd3052586b9.png', - title: '排行榜', - onTap: () { - try { - HomeController homeController = - Get.find(); - int index = homeController.tabs.indexOf( - HomeTabType.rank, + child: Pref.showHotRcmd + ? Padding( + padding: const .only(left: 12, top: 12, right: 12), + child: Row( + mainAxisAlignment: .spaceEvenly, + children: [ + _buildEntranceItem( + iconUrl: + 'https://i0.hdslb.com/bfs/archive/a3f11218aaf4521b4967db2ae164ecd3052586b9.png', + title: '排行榜', + onTap: () { + try { + HomeController homeController = + Get.find(); + int index = homeController.tabs.indexOf( + HomeTabType.rank, + ); + if (index != -1) { + homeController.tabController.animateTo( + index, ); - if (index != -1) { - homeController.tabController.animateTo( - index, - ); - } else { - Get.to( - Scaffold( - resizeToAvoidBottomInset: false, - appBar: AppBar( - title: const Text('排行榜'), - ), - body: const ViewSafeArea( - child: RankPage(), - ), + } else { + Get.to( + Scaffold( + resizeToAvoidBottomInset: false, + appBar: AppBar( + title: const Text('排行榜'), ), - ); - } - } catch (_) {} - }, - ), - _buildEntranceItem( - iconUrl: - 'https://i0.hdslb.com/bfs/archive/552ebe8c4794aeef30ebd1568b59ad35f15e21ad.png', - title: '每周必看', - onTap: () => Get.toNamed('/popularSeries'), - ), - _buildEntranceItem( - iconUrl: - 'https://i0.hdslb.com/bfs/archive/3693ec9335b78ca57353ac0734f36a46f3d179a9.png', - title: '入站必刷', - onTap: () => Get.toNamed('/popularPrecious'), - ), - ], - ), - ) - : const SizedBox.shrink(), - ), + body: const ViewSafeArea( + child: RankPage(), + ), + ), + ); + } + } catch (_) {} + }, + ), + _buildEntranceItem( + iconUrl: + 'https://i0.hdslb.com/bfs/archive/552ebe8c4794aeef30ebd1568b59ad35f15e21ad.png', + title: '每周必看', + onTap: () => Get.toNamed('/popularSeries'), + ), + _buildEntranceItem( + iconUrl: + 'https://i0.hdslb.com/bfs/archive/3693ec9335b78ca57353ac0734f36a46f3d179a9.png', + title: '入站必刷', + onTap: () => Get.toNamed('/popularPrecious'), + ), + ], + ), + ) + : const SizedBox.shrink(), ), SliverPadding( padding: const EdgeInsets.only(top: 7, bottom: 100), diff --git a/lib/pages/later/controller.dart b/lib/pages/later/controller.dart index e5bdcc1ee..3799a8098 100644 --- a/lib/pages/later/controller.dart +++ b/lib/pages/later/controller.dart @@ -53,34 +53,32 @@ mixin BaseLaterController ) { showDialog( context: context, - builder: (context) { - return AlertDialog( - title: const Text('提示'), - content: const Text('即将移除该视频,确定是否移除'), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), + builder: (context) => AlertDialog( + title: const Text('提示'), + content: const Text('即将移除该视频,确定是否移除'), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), ), - TextButton( - onPressed: () async { - Get.back(); - final res = await UserHttp.toViewDel(aids: aid.toString()); - if (res.isSuccess) { - loadingState - ..value.data!.removeAt(index) - ..refresh(); - updateCount?.call(1); - } - }, - child: const Text('确认移除'), - ), - ], - ); - }, + ), + TextButton( + onPressed: () async { + Get.back(); + final res = await UserHttp.toViewDel(aids: aid.toString()); + if (res.isSuccess) { + loadingState + ..value.data!.removeAt(index) + ..refresh(); + updateCount?.call(1); + } + }, + child: const Text('确认移除'), + ), + ], + ), ); } } diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index c6077c2bd..a83112859 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -271,67 +271,65 @@ class _LoginPageState extends State { //https://passport.bilibili.com/passport/findPassword showDialog( context: context, - builder: (context) { - return SimpleDialog( - clipBehavior: Clip.hardEdge, - title: const Text('忘记密码?'), - contentPadding: const EdgeInsets.fromLTRB( - 0.0, - 2.0, - 0.0, - 16.0, + builder: (context) => SimpleDialog( + clipBehavior: Clip.hardEdge, + title: const Text('忘记密码?'), + contentPadding: const EdgeInsets.fromLTRB( + 0.0, + 2.0, + 0.0, + 16.0, + ), + children: [ + const Padding( + padding: EdgeInsets.fromLTRB(25, 0, 25, 10), + child: Text("试试扫码、手机号登录,或选择"), ), - children: [ - const Padding( - padding: EdgeInsets.fromLTRB(25, 0, 25, 10), - child: Text("试试扫码、手机号登录,或选择"), + ListTile( + title: const Text( + '找回密码(手机版)', ), - ListTile( - title: const Text( - '找回密码(手机版)', - ), - leading: const Icon(Icons.smartphone_outlined), - subtitle: const Text( - 'https://passport.bilibili.com/h5-app/passport/login/findPassword', - ), - dense: false, - onTap: () => Get - ..back() - ..toNamed( - '/webview', - parameters: { - 'url': - 'https://passport.bilibili.com/h5-app/passport/login/findPassword', - 'type': 'url', - 'pageTitle': '忘记密码', - }, - ), + leading: const Icon(Icons.smartphone_outlined), + subtitle: const Text( + 'https://passport.bilibili.com/h5-app/passport/login/findPassword', ), - ListTile( - title: const Text( - '找回密码(电脑版)', + dense: false, + onTap: () => Get + ..back() + ..toNamed( + '/webview', + parameters: { + 'url': + 'https://passport.bilibili.com/h5-app/passport/login/findPassword', + 'type': 'url', + 'pageTitle': '忘记密码', + }, ), - leading: const Icon(Icons.desktop_windows_outlined), - subtitle: const Text( - 'https://passport.bilibili.com/pc/passport/findPassword', - ), - dense: false, - onTap: () => Get - ..back() - ..toNamed( - '/webview', - parameters: { - 'url': - 'https://passport.bilibili.com/pc/passport/findPassword', - 'type': 'url', - 'pageTitle': '忘记密码', - 'uaType': 'pc', - }, - ), + ), + ListTile( + title: const Text( + '找回密码(电脑版)', ), - ], - ); - }, + leading: const Icon(Icons.desktop_windows_outlined), + subtitle: const Text( + 'https://passport.bilibili.com/pc/passport/findPassword', + ), + dense: false, + onTap: () => Get + ..back() + ..toNamed( + '/webview', + parameters: { + 'url': + 'https://passport.bilibili.com/pc/passport/findPassword', + 'type': 'url', + 'pageTitle': '忘记密码', + 'uaType': 'pc', + }, + ), + ), + ], + ), ); }, child: const Text('忘记密码'), diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index 7129efcea..f5e7e009c 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -148,28 +148,26 @@ class MemberController extends CommonDataController } showDialog( context: context, - builder: (context) { - return AlertDialog( - title: const Text('提示'), - content: Text(relation.value != 128 ? '确定拉黑UP主?' : '从黑名单移除UP主'), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '点错了', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), + builder: (context) => AlertDialog( + title: const Text('提示'), + content: Text(relation.value != 128 ? '确定拉黑UP主?' : '从黑名单移除UP主'), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '点错了', + style: TextStyle(color: Theme.of(context).colorScheme.outline), ), - TextButton( - onPressed: () { - Get.back(); - _onBlock(); - }, - child: const Text('确认'), - ), - ], - ); - }, + ), + TextButton( + onPressed: () { + Get.back(); + _onBlock(); + }, + child: const Text('确认'), + ), + ], + ), ); } diff --git a/lib/pages/member_opus/view.dart b/lib/pages/member_opus/view.dart index 2f2121d57..1f4bc7b6e 100644 --- a/lib/pages/member_opus/view.dart +++ b/lib/pages/member_opus/view.dart @@ -71,40 +71,38 @@ class _MemberOpusState extends State child: FloatingActionButton.extended( onPressed: () => showDialog( context: context, - builder: (context) { - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.symmetric(vertical: 12), - content: Column( - mainAxisSize: MainAxisSize.min, - children: _controller.filter! - .map( - (e) => ListTile( - onTap: () { - if (e == _controller.type.value) { - return; - } - Get.back(); - _controller - ..type.value = e - ..onReload(); - }, - tileColor: e == _controller.type.value - ? Theme.of( - context, - ).colorScheme.onInverseSurface - : null, - dense: true, - title: Text( - e.text ?? e.tabName!, - style: const TextStyle(fontSize: 14), - ), + builder: (context) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + content: Column( + mainAxisSize: MainAxisSize.min, + children: _controller.filter! + .map( + (e) => ListTile( + onTap: () { + if (e == _controller.type.value) { + return; + } + Get.back(); + _controller + ..type.value = e + ..onReload(); + }, + tileColor: e == _controller.type.value + ? Theme.of( + context, + ).colorScheme.onInverseSurface + : null, + dense: true, + title: Text( + e.text ?? e.tabName!, + style: const TextStyle(fontSize: 14), ), - ) - .toList(), - ), - ); - }, + ), + ) + .toList(), + ), + ), ), icon: const Icon(size: 20, Icons.sort), label: Obx( diff --git a/lib/pages/popular_series/view.dart b/lib/pages/popular_series/view.dart index 2e5cda24a..3b402773d 100644 --- a/lib/pages/popular_series/view.dart +++ b/lib/pages/popular_series/view.dart @@ -127,50 +127,44 @@ class _PopularSeriesPageState extends State with GridMixin { ); showDialog( context: context, - builder: (context) { - return Dialog( - clipBehavior: Clip.hardEdge, - child: SizedBox( - width: width, - height: width, - child: ListView.builder( - controller: controller, - padding: const EdgeInsets.symmetric(vertical: 12), - itemCount: seriesList.length, - itemExtent: 44, - itemBuilder: (context, index) { - final item = seriesList[index]; - final isCurr = index == currIndex; - return ListTile( - dense: true, - minTileHeight: 44, - tileColor: isCurr - ? Theme.of(context).highlightColor - : null, - onTap: () { - Get.back(); - if (!isCurr) { - _controller - ..number = item.number! - ..onReload(); - } - }, - title: Text( - item.name!, - style: const TextStyle(fontSize: 14), - ), - trailing: isCurr - ? const Icon(Icons.check, size: 18) - : null, - contentPadding: const EdgeInsetsGeometry.symmetric( - horizontal: 16, - ), - ); - }, - ), + builder: (context) => Dialog( + clipBehavior: Clip.hardEdge, + child: SizedBox( + width: width, + height: width, + child: ListView.builder( + controller: controller, + padding: const EdgeInsets.symmetric(vertical: 12), + itemCount: seriesList.length, + itemExtent: 44, + itemBuilder: (context, index) { + final item = seriesList[index]; + final isCurr = index == currIndex; + return ListTile( + dense: true, + minTileHeight: 44, + tileColor: isCurr ? Theme.of(context).highlightColor : null, + onTap: () { + Get.back(); + if (!isCurr) { + _controller + ..number = item.number! + ..onReload(); + } + }, + title: Text( + item.name!, + style: const TextStyle(fontSize: 14), + ), + trailing: isCurr ? const Icon(Icons.check, size: 18) : null, + contentPadding: const EdgeInsetsGeometry.symmetric( + horizontal: 16, + ), + ); + }, ), - ); - }, + ), + ), ).whenComplete(controller.dispose); }, child: Text.rich( diff --git a/lib/pages/setting/models/extra_settings.dart b/lib/pages/setting/models/extra_settings.dart index 9abd13381..98891467a 100644 --- a/lib/pages/setting/models/extra_settings.dart +++ b/lib/pages/setting/models/extra_settings.dart @@ -21,7 +21,6 @@ import 'package:PiliPlus/models/dynamics/result.dart' show DynamicsDataModel, ItemModulesModel; import 'package:PiliPlus/pages/common/slide/common_slide_page.dart'; import 'package:PiliPlus/pages/home/controller.dart'; -import 'package:PiliPlus/pages/hot/controller.dart'; import 'package:PiliPlus/pages/main/controller.dart'; import 'package:PiliPlus/pages/setting/models/model.dart'; import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart'; @@ -67,56 +66,7 @@ List get extraSettings => [ title: '缓存路径', getSubtitle: () => downloadPath, leading: const Icon(Icons.storage), - onTap: (context, setState) { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.symmetric(vertical: 12), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - onTap: () { - Get.back(); - Utils.copyText(downloadPath); - }, - dense: true, - title: const Text('复制', style: TextStyle(fontSize: 14)), - ), - ListTile( - onTap: () { - Get.back(); - final defPath = defDownloadPath; - if (downloadPath == defPath) return; - downloadPath = defPath; - setState(); - Get.find().initDownloadList(); - GStorage.setting.delete(SettingBoxKey.downloadPath); - }, - dense: true, - title: const Text('重置', style: TextStyle(fontSize: 14)), - ), - ListTile( - onTap: () async { - Get.back(); - final path = await FilePicker.platform.getDirectoryPath(); - if (path == null || path == downloadPath) return; - downloadPath = path; - setState(); - Get.find().initDownloadList(); - GStorage.setting.put(SettingBoxKey.downloadPath, path); - }, - dense: true, - title: const Text('设置新路径', style: TextStyle(fontSize: 14)), - ), - ], - ), - ); - }, - ); - }, + onTap: _showDownPathDialog, ), ], SwitchModel( @@ -148,49 +98,7 @@ List get extraSettings => [ setKey: SettingBoxKey.checkDynamic, defaultVal: true, onChanged: (value) => Get.find().checkDynamic = value, - onTap: (context) { - String dynamicPeriod = Pref.dynamicPeriod.toString(); - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text('检查周期'), - content: TextFormField( - autofocus: true, - initialValue: dynamicPeriod, - keyboardType: TextInputType.number, - onChanged: (value) => dynamicPeriod = value, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - decoration: const InputDecoration(suffixText: 'min'), - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - TextButton( - onPressed: () { - try { - final val = int.parse(dynamicPeriod); - Get.back(); - GStorage.setting.put(SettingBoxKey.dynamicPeriod, val); - Get.find().dynamicPeriod = val * 60 * 1000; - } catch (e) { - SmartDialog.showToast(e.toString()); - } - }, - child: const Text('确定'), - ), - ], - ); - }, - ); - }, + onTap: _showDynDialog, ), SwitchModel( title: '显示视频分段信息', @@ -254,107 +162,21 @@ List get extraSettings => [ title: '评论折叠行数', subtitle: '0行为不折叠', leading: const Icon(Icons.compress), - getTrailing: () => Text( + getTrailing: (theme) => Text( '${ReplyItemGrpc.replyLengthLimit}行', - style: Get.theme.textTheme.titleSmall, + style: theme.textTheme.titleSmall, ), - onTap: (context, setState) { - String replyLengthLimit = ReplyItemGrpc.replyLengthLimit.toString(); - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text('评论折叠行数'), - content: TextFormField( - autofocus: true, - initialValue: replyLengthLimit, - keyboardType: TextInputType.number, - onChanged: (value) => replyLengthLimit = value, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - decoration: const InputDecoration(suffixText: '行'), - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - TextButton( - onPressed: () async { - Get.back(); - int length = int.tryParse(replyLengthLimit) ?? 6; - ReplyItemGrpc.replyLengthLimit = length == 0 ? null : length; - await GStorage.setting.put( - SettingBoxKey.replyLengthLimit, - length, - ); - setState(); - }, - child: const Text('确定'), - ), - ], - ); - }, - ); - }, + onTap: _showReplyLengthDialog, ), NormalModel( title: '弹幕行高', subtitle: '默认1.6', leading: const Icon(CustomIcons.dm_settings), - getTrailing: () => Text( + getTrailing: (theme) => Text( Pref.danmakuLineHeight.toString(), - style: Get.theme.textTheme.titleSmall, + style: theme.textTheme.titleSmall, ), - onTap: (context, setState) { - String danmakuLineHeight = Pref.danmakuLineHeight.toString(); - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text('弹幕行高'), - content: TextFormField( - autofocus: true, - initialValue: danmakuLineHeight, - keyboardType: const .numberWithOptions(decimal: true), - onChanged: (value) => danmakuLineHeight = value, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), - ], - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - TextButton( - onPressed: () async { - Get.back(); - await GStorage.setting.put( - SettingBoxKey.danmakuLineHeight, - max( - 1.0, - double.tryParse(danmakuLineHeight)?.toPrecision(1) ?? 1.6, - ), - ); - setState(); - }, - child: const Text('确定'), - ), - ], - ); - }, - ); - }, + onTap: _showDmHeightDialog, ), const SwitchModel( title: '显示视频警告/争议信息', @@ -414,104 +236,20 @@ List get extraSettings => [ NormalModel( title: '横向滑动阈值', getSubtitle: () => '当前:「${Pref.touchSlopH}」', - onTap: (context, setState) { - String initialValue = Pref.touchSlopH.toString(); - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text('横向滑动阈值'), - content: TextFormField( - autofocus: true, - initialValue: initialValue, - keyboardType: const .numberWithOptions(decimal: true), - onChanged: (value) => initialValue = value, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), - ], - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - TextButton( - onPressed: () async { - try { - final val = double.parse(initialValue); - Get.back(); - touchSlopH = val; - await GStorage.setting.put(SettingBoxKey.touchSlopH, val); - setState(); - } catch (e) { - SmartDialog.showToast(e.toString()); - } - }, - child: const Text('确定'), - ), - ], - ); - }, - ); - }, + onTap: _showTouchSlopDialog, leading: const Icon(Icons.pan_tool_alt_outlined), ), NormalModel( title: '刷新滑动距离', leading: const Icon(Icons.refresh), getSubtitle: () => '当前滑动距离: ${Pref.refreshDragPercentage}x', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SlideDialog( - title: '刷新滑动距离', - min: 0.1, - max: 0.5, - divisions: 8, - precise: 2, - value: Pref.refreshDragPercentage, - suffix: 'x', - ); - }, - ); - if (result != null) { - kDragContainerExtentPercentage = result; - await GStorage.setting.put(SettingBoxKey.refreshDragPercentage, result); - Get.forceAppUpdate(); - setState(); - } - }, + onTap: _showRefreshDragDialog, ), NormalModel( title: '刷新指示器高度', leading: const Icon(Icons.height), getSubtitle: () => '当前指示器高度: ${Pref.refreshDisplacement}', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SlideDialog( - title: '刷新指示器高度', - min: 10.0, - max: 100.0, - divisions: 9, - value: Pref.refreshDisplacement, - ); - }, - ); - if (result != null) { - displacement = result; - await GStorage.setting.put(SettingBoxKey.refreshDisplacement, result); - Get.forceAppUpdate(); - setState(); - } - }, + onTap: _showRefreshDialog, ), const SwitchModel( title: '显示会员彩色弹幕', @@ -526,17 +264,13 @@ List get extraSettings => [ setKey: SettingBoxKey.mergeDanmaku, defaultVal: false, ), - SwitchModel( + const SwitchModel( title: '显示热门推荐', subtitle: '热门页面显示每周必看等推荐内容入口', - leading: const Icon(Icons.local_fire_department_outlined), + leading: Icon(Icons.local_fire_department_outlined), setKey: SettingBoxKey.showHotRcmd, defaultVal: false, - onChanged: (value) { - try { - Get.find().showHotRcmd.value = value; - } catch (_) {} - }, + needReboot: true, ), if (kDebugMode || Platform.isAndroid) NormalModel( @@ -562,27 +296,7 @@ List get extraSettings => [ leading: const Icon(Icons.stay_current_landscape_outlined), getSubtitle: () => '当前:「${Pref.superResolutionType.title}」\n默认设置对番剧生效, 其他视频默认关闭\n超分辨率需要启用硬件解码, 若启用硬件解码后仍然不生效, 尝试切换硬件解码器为 auto-copy', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '超分辨率', - value: Pref.superResolutionType, - values: SuperResolutionType.values - .map((e) => (e, e.title)) - .toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put( - SettingBoxKey.superResolutionType, - result.index, - ); - setState(); - } - }, + onTap: _showSuperResolutionDialog, ), const SwitchModel( title: '提前初始化播放器', @@ -795,54 +509,12 @@ List get extraSettings => [ } catch (_) {} }, ), - SwitchModel( + const SwitchModel( title: '快速收藏', subtitle: '点击设置默认收藏夹\n点按收藏至默认,长按选择文件夹', - leading: const Icon(Icons.bookmark_add_outlined), + leading: Icon(Icons.bookmark_add_outlined), setKey: SettingBoxKey.enableQuickFav, - onTap: (context) async { - if (Accounts.main.isLogin) { - final res = await FavHttp.allFavFolders(Accounts.main.mid); - if (res case Success(:final response)) { - final list = response.list; - if (list == null || list.isEmpty) { - return; - } - final quickFavId = Pref.quickFavId; - if (!context.mounted) return; - showDialog( - context: context, - builder: (context) => AlertDialog( - clipBehavior: Clip.hardEdge, - title: const Text('选择默认收藏夹'), - contentPadding: const EdgeInsets.only(top: 5, bottom: 18), - content: SingleChildScrollView( - child: RadioGroup( - onChanged: (value) { - Get.back(); - GStorage.setting.put(SettingBoxKey.quickFavId, value); - SmartDialog.showToast('设置成功'); - }, - groupValue: quickFavId, - child: Column( - children: list.map((item) { - return RadioListTile( - toggleable: true, - dense: true, - title: Text(item.title), - value: item.id, - ); - }).toList(), - ), - ), - ), - ), - ); - } else { - res.toast(); - } - } - }, + onTap: _showFavDialog, defaultVal: false, ), SwitchModel( @@ -881,104 +553,29 @@ List get extraSettings => [ defaultVal: false, needReboot: true, ), - NormalModel( + const NormalModel( title: '连接重试次数', subtitle: '为0时禁用', - leading: const Icon(Icons.repeat), - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SlideDialog( - title: '连接重试次数', - min: 0, - max: 8, - divisions: 8, - precise: 0, - value: Pref.retryCount.toDouble(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.retryCount, result.toInt()); - setState(); - SmartDialog.showToast('重启生效'); - } - }, + leading: Icon(Icons.repeat), + onTap: _showReplyCountDialog, ), - NormalModel( + const NormalModel( title: '连接重试间隔', subtitle: '实际间隔 = 间隔 * 第x次重试', - leading: const Icon(Icons.more_time_outlined), - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SlideDialog( - title: '连接重试间隔', - min: 0, - max: 1000, - divisions: 10, - precise: 0, - value: Pref.retryDelay.toDouble(), - suffix: 'ms', - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.retryDelay, result.toInt()); - setState(); - SmartDialog.showToast('重启生效'); - } - }, + leading: Icon(Icons.more_time_outlined), + onTap: _showReplyDelayDialog, ), NormalModel( title: '评论展示', leading: const Icon(Icons.whatshot_outlined), getSubtitle: () => '当前优先展示「${Pref.replySortType.title}」', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '评论展示', - value: Pref.replySortType, - values: ReplySortType.values.map((e) => (e, e.title)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.replySortType, result.index); - setState(); - } - }, + onTap: _showReplySortDialog, ), NormalModel( title: '动态展示', leading: const Icon(Icons.dynamic_feed_rounded), getSubtitle: () => '当前优先展示「${Pref.defaultDynamicType.label}」', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '动态展示', - value: Pref.defaultDynamicType, - values: DynamicsTabType.values - .take(4) - .map((e) => (e, e.label)) - .toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put( - SettingBoxKey.defaultDynamicType, - result.index, - ); - setState(); - } - }, + onTap: _showDefDynDialog, ), SwitchModel( title: '显示动态互动内容', @@ -992,22 +589,7 @@ List get extraSettings => [ title: '用户页默认展示TAB', leading: const Icon(Icons.tab), getSubtitle: () => '当前优先展示「${Pref.memberTab.title}」', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '用户页默认展示TAB', - value: Pref.memberTab, - values: MemberTabType.values.map((e) => (e, e.title)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.memberTab, result.index); - setState(); - } - }, + onTap: _showMemberTabDialog, ), SwitchModel( title: '显示UP主页小店TAB', @@ -1016,80 +598,12 @@ List get extraSettings => [ defaultVal: false, onChanged: (value) => MemberTabType.showMemberShop = value, ), - SwitchModel( - onTap: (context) { - String systemProxyHost = Pref.systemProxyHost; - String systemProxyPort = Pref.systemProxyPort; - - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text('设置代理'), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 6), - TextFormField( - initialValue: systemProxyHost, - decoration: const InputDecoration( - isDense: true, - labelText: '请输入Host,使用 . 分割', - border: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(6)), - ), - ), - onChanged: (e) => systemProxyHost = e, - ), - const SizedBox(height: 10), - TextFormField( - initialValue: systemProxyPort, - keyboardType: TextInputType.number, - decoration: const InputDecoration( - isDense: true, - labelText: '请输入Port', - border: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(6)), - ), - ), - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - onChanged: (e) => systemProxyPort = e, - ), - ], - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - TextButton( - onPressed: () { - Get.back(); - GStorage.setting.put( - SettingBoxKey.systemProxyHost, - systemProxyHost, - ); - GStorage.setting.put( - SettingBoxKey.systemProxyPort, - systemProxyPort, - ); - }, - child: const Text('确认'), - ), - ], - ); - }, - ); - }, - leading: const Icon(Icons.airplane_ticket_outlined), + const SwitchModel( + leading: Icon(Icons.airplane_ticket_outlined), title: '设置代理', subtitle: '设置代理 host:port', setKey: SettingBoxKey.enableSystemProxy, + onTap: _showProxyDialog, ), const SwitchModel( title: '自动清除缓存', @@ -1104,50 +618,8 @@ List get extraSettings => [ final num = Pref.maxCacheSize; return '当前最大缓存大小: 「${num == 0 ? '无限' : CacheManager.formatSize(Pref.maxCacheSize)}」'; }, - onTap: (context, setState) { - showDialog( - context: context, - builder: (context) { - String valueStr = ''; - return AlertDialog( - title: const Text('最大缓存大小'), - content: TextField( - autofocus: true, - onChanged: (value) => valueStr = value, - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), - ], - decoration: const InputDecoration(suffixText: 'MB'), - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - TextButton( - onPressed: () async { - Get.back(); - num value = num.tryParse(valueStr) ?? 0; - await GStorage.setting.put( - SettingBoxKey.maxCacheSize, - value * 1024 * 1024, - ); - setState(); - }, - child: const Text('确定'), - ), - ], - ); - }, - ); - }, leading: const Icon(Icons.delete_outlined), + onTap: _showCacheDialog, ), SwitchModel( title: '检查更新', @@ -1171,7 +643,7 @@ Future audioNormalization( final key = fallback ? SettingBoxKey.fallbackNormalization : SettingBoxKey.audioNormalization; - final result = await showDialog( + final res = await showDialog( context: context, builder: (context) { String audioNormalization = fallback @@ -1205,57 +677,601 @@ Future audioNormalization( ); }, ); - if (result != null && context.mounted) { - if (result == '3') { + if (res != null && context.mounted) { + if (res == '3') { String param = ''; await showDialog( context: context, - builder: (context) { - return AlertDialog( - title: const Text('自定义参数'), - content: Column( - mainAxisSize: MainAxisSize.min, - spacing: 16, - children: [ - const Text('等同于 --lavfi-complex="[aid1] 参数 [ao]"'), - TextField( - autofocus: true, - onChanged: (value) => param = value, - ), - ], - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: ColorScheme.of(context).outline, - ), - ), - ), - TextButton( - onPressed: () { - Get.back(); - GStorage.setting.put(key, param); - if (!fallback && - PlPlayerController.loudnormRegExp.hasMatch(param)) { - audioNormalization(context, setState, fallback: true); - } - setState(); - }, - child: const Text('确定'), + builder: (context) => AlertDialog( + title: const Text('自定义参数'), + content: Column( + mainAxisSize: MainAxisSize.min, + spacing: 16, + children: [ + const Text('等同于 --lavfi-complex="[aid1] 参数 [ao]"'), + TextField( + autofocus: true, + onChanged: (value) => param = value, ), ], - ); - }, + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: ColorScheme.of(context).outline, + ), + ), + ), + TextButton( + onPressed: () { + Get.back(); + GStorage.setting.put(key, param); + if (!fallback && + PlPlayerController.loudnormRegExp.hasMatch(param)) { + audioNormalization(context, setState, fallback: true); + } + setState(); + }, + child: const Text('确定'), + ), + ], + ), ); } else { - GStorage.setting.put(key, result); - if (result == '2') { + GStorage.setting.put(key, res); + if (res == '2') { audioNormalization(context, setState, fallback: true); } setState(); } } } + +void _showDownPathDialog(BuildContext context, VoidCallback setState) { + showDialog( + context: context, + builder: (context) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + onTap: () { + Get.back(); + Utils.copyText(downloadPath); + }, + dense: true, + title: const Text('复制', style: TextStyle(fontSize: 14)), + ), + ListTile( + onTap: () { + Get.back(); + final defPath = defDownloadPath; + if (downloadPath == defPath) return; + downloadPath = defPath; + setState(); + Get.find().initDownloadList(); + GStorage.setting.delete(SettingBoxKey.downloadPath); + }, + dense: true, + title: const Text('重置', style: TextStyle(fontSize: 14)), + ), + ListTile( + onTap: () async { + Get.back(); + final path = await FilePicker.platform.getDirectoryPath(); + if (path == null || path == downloadPath) return; + downloadPath = path; + setState(); + Get.find().initDownloadList(); + GStorage.setting.put(SettingBoxKey.downloadPath, path); + }, + dense: true, + title: const Text('设置新路径', style: TextStyle(fontSize: 14)), + ), + ], + ), + ), + ); +} + +void _showDynDialog(BuildContext context) { + String dynamicPeriod = Pref.dynamicPeriod.toString(); + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('检查周期'), + content: TextFormField( + autofocus: true, + initialValue: dynamicPeriod, + keyboardType: TextInputType.number, + onChanged: (value) => dynamicPeriod = value, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + decoration: const InputDecoration(suffixText: 'min'), + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + TextButton( + onPressed: () { + try { + final val = int.parse(dynamicPeriod); + Get.back(); + GStorage.setting.put(SettingBoxKey.dynamicPeriod, val); + Get.find().dynamicPeriod = val * 60 * 1000; + } catch (e) { + SmartDialog.showToast(e.toString()); + } + }, + child: const Text('确定'), + ), + ], + ), + ); +} + +void _showReplyLengthDialog(BuildContext context, VoidCallback setState) { + String replyLengthLimit = ReplyItemGrpc.replyLengthLimit.toString(); + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('评论折叠行数'), + content: TextFormField( + autofocus: true, + initialValue: replyLengthLimit, + keyboardType: TextInputType.number, + onChanged: (value) => replyLengthLimit = value, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + decoration: const InputDecoration(suffixText: '行'), + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + TextButton( + onPressed: () async { + Get.back(); + int length = int.tryParse(replyLengthLimit) ?? 6; + ReplyItemGrpc.replyLengthLimit = length == 0 ? null : length; + await GStorage.setting.put( + SettingBoxKey.replyLengthLimit, + length, + ); + setState(); + }, + child: const Text('确定'), + ), + ], + ), + ); +} + +void _showDmHeightDialog(BuildContext context, VoidCallback setState) { + String danmakuLineHeight = Pref.danmakuLineHeight.toString(); + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('弹幕行高'), + content: TextFormField( + autofocus: true, + initialValue: danmakuLineHeight, + keyboardType: const .numberWithOptions(decimal: true), + onChanged: (value) => danmakuLineHeight = value, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), + ], + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + TextButton( + onPressed: () async { + Get.back(); + await GStorage.setting.put( + SettingBoxKey.danmakuLineHeight, + max( + 1.0, + double.tryParse(danmakuLineHeight)?.toPrecision(1) ?? 1.6, + ), + ); + setState(); + }, + child: const Text('确定'), + ), + ], + ), + ); +} + +void _showTouchSlopDialog(BuildContext context, VoidCallback setState) { + String initialValue = Pref.touchSlopH.toString(); + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('横向滑动阈值'), + content: TextFormField( + autofocus: true, + initialValue: initialValue, + keyboardType: const .numberWithOptions(decimal: true), + onChanged: (value) => initialValue = value, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), + ], + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + TextButton( + onPressed: () async { + try { + final val = double.parse(initialValue); + Get.back(); + touchSlopH = val; + await GStorage.setting.put(SettingBoxKey.touchSlopH, val); + setState(); + } catch (e) { + SmartDialog.showToast(e.toString()); + } + }, + child: const Text('确定'), + ), + ], + ), + ); +} + +Future _showRefreshDragDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SlideDialog( + title: '刷新滑动距离', + min: 0.1, + max: 0.5, + divisions: 8, + precise: 2, + value: Pref.refreshDragPercentage, + suffix: 'x', + ), + ); + if (res != null) { + kDragContainerExtentPercentage = res; + await GStorage.setting.put(SettingBoxKey.refreshDragPercentage, res); + Get.forceAppUpdate(); + setState(); + } +} + +Future _showRefreshDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SlideDialog( + title: '刷新指示器高度', + min: 10.0, + max: 100.0, + divisions: 9, + value: Pref.refreshDisplacement, + ), + ); + if (res != null) { + displacement = res; + await GStorage.setting.put(SettingBoxKey.refreshDisplacement, res); + Get.forceAppUpdate(); + setState(); + } +} + +Future _showSuperResolutionDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '超分辨率', + value: Pref.superResolutionType, + values: SuperResolutionType.values.map((e) => (e, e.title)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put( + SettingBoxKey.superResolutionType, + res.index, + ); + setState(); + } +} + +Future _showFavDialog(BuildContext context) async { + if (Accounts.main.isLogin) { + final res = await FavHttp.allFavFolders(Accounts.main.mid); + if (res case Success(:final response)) { + final list = response.list; + if (list == null || list.isEmpty) { + return; + } + final quickFavId = Pref.quickFavId; + if (!context.mounted) return; + showDialog( + context: context, + builder: (context) => AlertDialog( + clipBehavior: Clip.hardEdge, + title: const Text('选择默认收藏夹'), + contentPadding: const EdgeInsets.only(top: 5, bottom: 18), + content: SingleChildScrollView( + child: RadioGroup( + onChanged: (value) { + Get.back(); + GStorage.setting.put(SettingBoxKey.quickFavId, value); + SmartDialog.showToast('设置成功'); + }, + groupValue: quickFavId, + child: Column( + children: list + .map( + (item) => RadioListTile( + toggleable: true, + dense: true, + title: Text(item.title), + value: item.id, + ), + ) + .toList(), + ), + ), + ), + ), + ); + } else { + res.toast(); + } + } +} + +Future _showReplyCountDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SlideDialog( + title: '连接重试次数', + min: 0, + max: 8, + divisions: 8, + precise: 0, + value: Pref.retryCount.toDouble(), + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.retryCount, res.toInt()); + setState(); + SmartDialog.showToast('重启生效'); + } +} + +Future _showReplyDelayDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SlideDialog( + title: '连接重试间隔', + min: 0, + max: 1000, + divisions: 10, + precise: 0, + value: Pref.retryDelay.toDouble(), + suffix: 'ms', + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.retryDelay, res.toInt()); + setState(); + SmartDialog.showToast('重启生效'); + } +} + +Future _showReplySortDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '评论展示', + value: Pref.replySortType, + values: ReplySortType.values.map((e) => (e, e.title)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.replySortType, res.index); + setState(); + } +} + +Future _showDefDynDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '动态展示', + value: Pref.defaultDynamicType, + values: DynamicsTabType.values.take(4).map((e) => (e, e.label)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put( + SettingBoxKey.defaultDynamicType, + res.index, + ); + setState(); + } +} + +Future _showMemberTabDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '用户页默认展示TAB', + value: Pref.memberTab, + values: MemberTabType.values.map((e) => (e, e.title)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.memberTab, res.index); + setState(); + } +} + +void _showProxyDialog(BuildContext context) { + String systemProxyHost = Pref.systemProxyHost; + String systemProxyPort = Pref.systemProxyPort; + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('设置代理'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 6), + TextFormField( + initialValue: systemProxyHost, + decoration: const InputDecoration( + isDense: true, + labelText: '请输入Host,使用 . 分割', + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + ), + onChanged: (e) => systemProxyHost = e, + ), + const SizedBox(height: 10), + TextFormField( + initialValue: systemProxyPort, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + isDense: true, + labelText: '请输入Port', + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + ), + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + onChanged: (e) => systemProxyPort = e, + ), + ], + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + TextButton( + onPressed: () { + Get.back(); + GStorage.setting.put( + SettingBoxKey.systemProxyHost, + systemProxyHost, + ); + GStorage.setting.put( + SettingBoxKey.systemProxyPort, + systemProxyPort, + ); + }, + child: const Text('确认'), + ), + ], + ), + ); +} + +void _showCacheDialog(BuildContext context, VoidCallback setState) { + String valueStr = ''; + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('最大缓存大小'), + content: TextField( + autofocus: true, + onChanged: (value) => valueStr = value, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), + ], + decoration: const InputDecoration(suffixText: 'MB'), + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + TextButton( + onPressed: () async { + Get.back(); + num value = num.tryParse(valueStr) ?? 0; + await GStorage.setting.put( + SettingBoxKey.maxCacheSize, + value * 1024 * 1024, + ); + setState(); + }, + child: const Text('确定'), + ), + ], + ), + ); +} diff --git a/lib/pages/setting/models/model.dart b/lib/pages/setting/models/model.dart index 2b3663890..169afcb25 100644 --- a/lib/pages/setting/models/model.dart +++ b/lib/pages/setting/models/model.dart @@ -35,7 +35,7 @@ class NormalModel extends SettingsModel { final String? title; final ValueGetter? getTitle; final ValueGetter? getSubtitle; - final Widget Function()? getTrailing; + final Widget Function(ThemeData theme)? getTrailing; final void Function(BuildContext context, VoidCallback setState)? onTap; const NormalModel({ @@ -125,49 +125,47 @@ SettingsModel getBanWordModel({ String editValue = banWord; showDialog( context: context, - builder: (context) { - return AlertDialog( - constraints: StyleString.dialogFixedConstraints, - title: Text(title), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('使用|隔开,如:尝试|测试'), - TextFormField( - autofocus: true, - initialValue: editValue, - textInputAction: TextInputAction.newline, - minLines: 1, - maxLines: 4, - onChanged: (value) => editValue = value, - ), - ], - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - TextButton( - child: const Text('保存'), - onPressed: () { - Get.back(); - banWord = editValue; - setState(); - onChanged(RegExp(banWord, caseSensitive: false)); - SmartDialog.showToast('已保存'); - GStorage.setting.put(key, banWord); - }, + builder: (context) => AlertDialog( + constraints: StyleString.dialogFixedConstraints, + title: Text(title), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('使用|隔开,如:尝试|测试'), + TextFormField( + autofocus: true, + initialValue: editValue, + textInputAction: TextInputAction.newline, + minLines: 1, + maxLines: 4, + onChanged: (value) => editValue = value, ), ], - ); - }, + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + TextButton( + child: const Text('保存'), + onPressed: () { + Get.back(); + banWord = editValue; + setState(); + onChanged(RegExp(banWord, caseSensitive: false)); + SmartDialog.showToast('已保存'); + GStorage.setting.put(key, banWord); + }, + ), + ], + ), ); }, ); @@ -197,57 +195,53 @@ SettingsModel getVideoFilterSelectModel({ onTap: (context, setState) async { var result = await showDialog( context: context, - builder: (context) { - return SelectDialog( - title: '选择$title${isFilter ? '(0即不过滤)' : ''}', - value: value, - values: - (values - ..addIf(!values.contains(value), value) - ..sort()) - .map( - (e) => (e, suffix == null ? e.toString() : '$e $suffix'), - ) - .toList() - ..add((-1, '自定义')), - ); - }, + builder: (context) => SelectDialog( + title: '选择$title${isFilter ? '(0即不过滤)' : ''}', + value: value, + values: + (values + ..addIf(!values.contains(value), value) + ..sort()) + .map( + (e) => (e, suffix == null ? e.toString() : '$e $suffix'), + ) + .toList() + ..add((-1, '自定义')), + ), ); if (result != null) { if (result == -1 && context.mounted) { + String valueStr = ''; await showDialog( context: context, - builder: (context) { - String valueStr = ''; - return AlertDialog( - title: Text('自定义$title'), - content: TextField( - autofocus: true, - onChanged: (value) => valueStr = value, - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - decoration: InputDecoration(suffixText: suffix), - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), + builder: (context) => AlertDialog( + title: Text('自定义$title'), + content: TextField( + autofocus: true, + onChanged: (value) => valueStr = value, + keyboardType: TextInputType.number, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + decoration: InputDecoration(suffixText: suffix), + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, ), ), - TextButton( - onPressed: () { - Get.back(); - result = int.tryParse(valueStr) ?? 0; - }, - child: const Text('确定'), - ), - ], - ); - }, + ), + TextButton( + onPressed: () { + Get.back(); + result = int.tryParse(valueStr) ?? 0; + }, + child: const Text('确定'), + ), + ], + ), ); } if (result != -1) { @@ -275,9 +269,9 @@ SettingsModel getPopupMenuModel({ subtitle: subtitle, leading: leading, // onTap: (context, setState) => globalKey.currentState?.showButtonMenu(), - getTrailing: () => Builder( + getTrailing: (theme) => Builder( builder: (context) { - final color = ColorScheme.of(context).secondary; + final color = theme.colorScheme.secondary; final v = values[GStorage.setting.get(key, defaultValue: defaultIndex)]; return PopupMenuButton( // key: globalKey, diff --git a/lib/pages/setting/models/play_settings.dart b/lib/pages/setting/models/play_settings.dart index 18a28999e..1bf6dc201 100644 --- a/lib/pages/setting/models/play_settings.dart +++ b/lib/pages/setting/models/play_settings.dart @@ -120,25 +120,7 @@ List get playSettings => [ title: '自动启用字幕', leading: const Icon(Icons.closed_caption_outlined), getSubtitle: () => '当前选择偏好:${Pref.subtitlePreferenceV2.desc}', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '字幕选择偏好', - value: Pref.subtitlePreferenceV2, - values: SubtitlePrefType.values.map((e) => (e, e.desc)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put( - SettingBoxKey.subtitlePreferenceV2, - result.index, - ); - setState(); - } - }, + onTap: _showSubtitleDialog, ), if (PlatformUtils.isDesktop) SwitchModel( @@ -162,22 +144,7 @@ List get playSettings => [ title: 'SuperChat (醒目留言) 显示类型', leading: const Icon(Icons.live_tv), getSubtitle: () => '当前:「${Pref.superChatType.title}」', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: 'SuperChat (醒目留言) 显示类型', - value: Pref.superChatType, - values: SuperChatType.values.map((e) => (e, e.title)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.superChatType, result.index); - setState(); - } - }, + onTap: _showSuperChatDialog, ), const SwitchModel( title: '竖屏扩大展示', @@ -267,46 +234,13 @@ List get playSettings => [ title: '默认全屏方向', leading: const Icon(Icons.open_with_outlined), getSubtitle: () => '当前全屏方向:${Pref.fullScreenMode.desc}', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '默认全屏方向', - value: Pref.fullScreenMode, - values: FullScreenMode.values.map((e) => (e, e.desc)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.fullScreenMode, result.index); - setState(); - } - }, + onTap: _showFullScreenModeDialog, ), NormalModel( title: '底部进度条展示', leading: const Icon(Icons.border_bottom_outlined), getSubtitle: () => '当前展示方式:${Pref.btmProgressBehavior.desc}', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '底部进度条展示', - value: Pref.btmProgressBehavior, - values: BtmProgressBehavior.values.map((e) => (e, e.desc)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put( - SettingBoxKey.btmProgressBehavior, - result.index, - ); - setState(); - } - }, + onTap: _showProgressBehaviorDialog, ), if (PlatformUtils.isMobile) SwitchModel( @@ -327,3 +261,81 @@ List get playSettings => [ defaultVal: false, ), ]; + +Future _showSubtitleDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '字幕选择偏好', + value: Pref.subtitlePreferenceV2, + values: SubtitlePrefType.values.map((e) => (e, e.desc)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put( + SettingBoxKey.subtitlePreferenceV2, + res.index, + ); + setState(); + } +} + +Future _showSuperChatDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: 'SuperChat (醒目留言) 显示类型', + value: Pref.superChatType, + values: SuperChatType.values.map((e) => (e, e.title)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.superChatType, res.index); + setState(); + } +} + +Future _showFullScreenModeDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '默认全屏方向', + value: Pref.fullScreenMode, + values: FullScreenMode.values.map((e) => (e, e.desc)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.fullScreenMode, res.index); + setState(); + } +} + +Future _showProgressBehaviorDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '底部进度条展示', + value: Pref.btmProgressBehavior, + values: BtmProgressBehavior.values.map((e) => (e, e.desc)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put( + SettingBoxKey.btmProgressBehavior, + res.index, + ); + setState(); + } +} diff --git a/lib/pages/setting/models/privacy_settings.dart b/lib/pages/setting/models/privacy_settings.dart index 2974ffd1d..9aeaf3e75 100644 --- a/lib/pages/setting/models/privacy_settings.dart +++ b/lib/pages/setting/models/privacy_settings.dart @@ -35,20 +35,18 @@ List get privacySettings => [ onTap: (context, setState) { showDialog( context: context, - builder: (context) { - return AlertDialog( - title: const Text('账号模式详情'), - content: SingleChildScrollView( - child: _getAccountDetail(context), + builder: (context) => AlertDialog( + title: const Text('账号模式详情'), + content: SingleChildScrollView( + child: _getAccountDetail(context), + ), + actions: [ + TextButton( + onPressed: Get.back, + child: const Text('确认'), ), - actions: [ - TextButton( - onPressed: Get.back, - child: const Text('确认'), - ), - ], - ); - }, + ], + ), ); }, leading: const Icon(Icons.flag_outlined), diff --git a/lib/pages/setting/models/style_settings.dart b/lib/pages/setting/models/style_settings.dart index 5bb275a65..c5ab45830 100644 --- a/lib/pages/setting/models/style_settings.dart +++ b/lib/pages/setting/models/style_settings.dart @@ -82,32 +82,11 @@ List get styleSettings => [ subtitle: '点击设置', setKey: SettingBoxKey.appFontWeight, defaultVal: false, - onTap: (context) { - showDialog( - context: context, - builder: (context) { - return SlideDialog( - title: 'App字体字重', - value: Pref.appFontWeight.toDouble() + 1, - min: 1, - max: FontWeight.values.length.toDouble(), - divisions: FontWeight.values.length - 1, - ); - }, - ).then((res) async { - if (res != null) { - await GStorage.setting.put( - SettingBoxKey.appFontWeight, - res.toInt() - 1, - ); - Get.forceAppUpdate(); - } - }); - }, leading: const Icon(Icons.text_fields), onChanged: (value) { Get.forceAppUpdate(); }, + onTap: _showFontWeightDialog, ), NormalModel( title: '界面缩放', @@ -119,23 +98,7 @@ List get styleSettings => [ title: '页面过渡动画', leading: const Icon(Icons.animation), getSubtitle: () => '当前:${Pref.pageTransition.name}', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '页面过渡动画', - value: Pref.pageTransition, - values: Transition.values.map((e) => (e, e.name)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.pageTransition, result.index); - SmartDialog.showToast('重启生效'); - setState(); - } - }, + onTap: _showTransitionDialog, ), const SwitchModel( title: '优化平板导航栏', @@ -153,36 +116,11 @@ List get styleSettings => [ needReboot: true, ), NormalModel( - onTap: (context, setState) async { - final result = await showDialog<(double, double)>( - context: context, - builder: (context) { - return DualSlideDialog( - title: '列表最大列宽度(默认240dp)', - value1: Pref.recommendCardWidth, - value2: Pref.smallCardWidth, - description1: '主页推荐流', - description2: '其他', - min: 150.0, - max: 500.0, - divisions: 35, - suffix: 'dp', - ); - }, - ); - if (result != null) { - await GStorage.setting.putAll({ - SettingBoxKey.recommendCardWidth: result.$1, - SettingBoxKey.smallCardWidth: result.$2, - }); - SmartDialog.showToast('重启生效'); - setState(); - } - }, leading: const Icon(Icons.calendar_view_week_outlined), title: '列表宽度(dp)限制', getSubtitle: () => '当前: 主页${Pref.recommendCardWidth.toInt()}dp 其他${Pref.smallCardWidth.toInt()}dp,屏幕宽度:${MediaQuery.widthOf(Get.context!).toPrecision(2)}dp。宽度越小列数越多。', + onTap: _showCardWidthDialog, ), SwitchModel( title: '视频播放页使用深色主题', @@ -207,23 +145,7 @@ List get styleSettings => [ title: '动态页UP主显示位置', leading: const Icon(Icons.person_outlined), getSubtitle: () => '当前:${Pref.upPanelPosition.label}', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '动态页UP主显示位置', - value: Pref.upPanelPosition, - values: UpPanelPosition.values.map((e) => (e, e.label)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.upPanelPosition, result.index); - SmartDialog.showToast('重启生效'); - setState(); - } - }, + onTap: _showUpPosDialog, ), const SwitchModel( title: '动态页显示所有已关注UP主', @@ -240,90 +162,19 @@ List get styleSettings => [ needReboot: true, ), NormalModel( - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '动态未读标记', - value: Pref.dynamicBadgeType, - values: DynamicBadgeMode.values.map((e) => (e, e.desc)).toList(), - ); - }, - ); - if (result != null) { - final mainController = Get.find() - ..dynamicBadgeMode = DynamicBadgeMode.values[result.index]; - if (mainController.dynamicBadgeMode != DynamicBadgeMode.hidden) { - mainController.getUnreadDynamic(); - } - await GStorage.setting.put( - SettingBoxKey.dynamicBadgeMode, - result.index, - ); - SmartDialog.showToast('设置成功'); - setState(); - } - }, title: '动态未读标记', leading: const Icon(Icons.motion_photos_on_outlined), getSubtitle: () => '当前标记样式:${Pref.dynamicBadgeType.desc}', + onTap: _showDynBadgeDialog, ), NormalModel( - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '消息未读标记', - value: Pref.msgBadgeMode, - values: DynamicBadgeMode.values.map((e) => (e, e.desc)).toList(), - ); - }, - ); - if (result != null) { - final mainController = Get.find() - ..msgBadgeMode = DynamicBadgeMode.values[result.index]; - if (mainController.msgBadgeMode != DynamicBadgeMode.hidden) { - mainController.queryUnreadMsg(true); - } else { - mainController.msgUnReadCount.value = ''; - } - await GStorage.setting.put(SettingBoxKey.msgBadgeMode, result.index); - SmartDialog.showToast('设置成功'); - setState(); - } - }, title: '消息未读标记', leading: const Icon(MdiIcons.bellBadgeOutline), getSubtitle: () => '当前标记样式:${Pref.msgBadgeMode.desc}', + onTap: _showMsgBadgeDialog, ), NormalModel( - onTap: (context, setState) async { - final result = await showDialog>( - context: context, - builder: (context) { - return MultiSelectDialog( - title: '消息未读类型', - initValues: Pref.msgUnReadTypeV2, - values: {for (final i in MsgUnReadType.values) i: i.title}, - ); - }, - ); - if (result != null) { - final mainController = Get.find() - ..msgUnReadTypes = result; - if (mainController.msgBadgeMode != DynamicBadgeMode.hidden) { - mainController.queryUnreadMsg(); - } - await GStorage.setting.put( - SettingBoxKey.msgUnReadTypeV2, - result.map((item) => item.index).toList()..sort(), - ); - SmartDialog.showToast('设置成功'); - setState(); - } - }, + onTap: _showMsgUnReadDialog, title: '消息未读类型', leading: const Icon(MdiIcons.bellCogOutline), getSubtitle: () => @@ -345,164 +196,58 @@ List get styleSettings => [ defaultVal: PlatformUtils.isMobile, needReboot: true, ), - SwitchModel( + const SwitchModel( title: '顶/底栏滚动阈值', subtitle: '滚动多少像素后收起/展开顶底栏,默认50像素', - leading: const Icon(Icons.swipe_vertical), + leading: Icon(Icons.swipe_vertical), defaultVal: false, setKey: SettingBoxKey.enableScrollThreshold, needReboot: true, - onTap: (context) { - String scrollThreshold = Pref.scrollThreshold.toString(); - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text('滚动阈值'), - content: TextFormField( - autofocus: true, - initialValue: scrollThreshold, - keyboardType: const TextInputType.numberWithOptions( - decimal: true, - ), - onChanged: (value) { - scrollThreshold = value; - }, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), - ], - decoration: const InputDecoration(suffixText: 'px'), - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - TextButton( - onPressed: () { - Get.back(); - GStorage.setting.put( - SettingBoxKey.scrollThreshold, - max( - 10.0, - double.tryParse(scrollThreshold) ?? 50.0, - ), - ); - SmartDialog.showToast('重启生效'); - }, - child: const Text('确定'), - ), - ], - ); - }, - ); - }, + onTap: _showScrollDialog, ), NormalModel( - onTap: (context, setState) { - _showQualityDialog( - context: context, - title: '图片质量', - initValue: Pref.picQuality, - onChanged: (picQuality) async { - GlobalData().imgQuality = picQuality; - await GStorage.setting.put(SettingBoxKey.defaultPicQa, picQuality); - setState(); - }, - ); - }, + onTap: (context, setState) => _showQualityDialog( + context: context, + title: '图片质量', + initValue: Pref.picQuality, + onChanged: (picQuality) async { + GlobalData().imgQuality = picQuality; + await GStorage.setting.put(SettingBoxKey.defaultPicQa, picQuality); + setState(); + }, + ), title: '图片质量', subtitle: '选择合适的图片清晰度,上限100%', leading: const Icon(Icons.image_outlined), - getTrailing: () => Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Text( - '${Pref.picQuality}%', - style: Get.theme.textTheme.titleSmall, - ), + getTrailing: (theme) => Text( + '${Pref.picQuality}%', + style: theme.textTheme.titleSmall, ), ), - // preview quality NormalModel( - onTap: (context, setState) { - _showQualityDialog( - context: context, - title: '查看大图质量', - initValue: Pref.previewQ, - onChanged: (picQuality) async { - await GStorage.setting.put(SettingBoxKey.previewQuality, picQuality); - setState(); - }, - ); - }, + onTap: (context, setState) => _showQualityDialog( + context: context, + title: '查看大图质量', + initValue: Pref.previewQ, + onChanged: (picQuality) async { + await GStorage.setting.put(SettingBoxKey.previewQuality, picQuality); + setState(); + }, + ), title: '查看大图质量', subtitle: '选择合适的图片清晰度,上限100%', leading: const Icon(Icons.image_outlined), - getTrailing: () => Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Text( - '${Pref.previewQ}%', - style: Get.theme.textTheme.titleSmall, - ), + getTrailing: (theme) => Text( + '${Pref.previewQ}%', + style: theme.textTheme.titleSmall, ), ), NormalModel( - onTap: (context, setState) { - final reduceLuxColor = Pref.reduceLuxColor; - showDialog( - context: context, - builder: (context) => AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.symmetric(vertical: 16), - title: const Text('Color Picker'), - content: SlideColorPicker( - color: reduceLuxColor ?? Colors.white, - onChanged: (Color? color) { - if (color != null && color != reduceLuxColor) { - if (color == Colors.white) { - NetworkImgLayer.reduceLuxColor = null; - GStorage.setting.delete(SettingBoxKey.reduceLuxColor); - SmartDialog.showToast('设置成功'); - setState(); - } else { - void onConfirm() { - NetworkImgLayer.reduceLuxColor = color; - GStorage.setting.put( - SettingBoxKey.reduceLuxColor, - color.toARGB32(), - ); - SmartDialog.showToast('设置成功'); - setState(); - } - - if (color.computeLuminance() < 0.2) { - showConfirmDialog( - context: context, - title: - '确认使用#${(color.toARGB32() & 0xFFFFFF).toRadixString(16).toUpperCase().padLeft(6)}?', - content: '所选颜色过于昏暗,可能会影响图片观看', - onConfirm: onConfirm, - ); - } else { - onConfirm(); - } - } - } - }, - ), - ), - ); - }, + onTap: _showReduceColorDialog, title: '深色下图片颜色叠加', subtitle: '显示颜色=图片原色x所选颜色,大图查看不受影响', leading: const Icon(Icons.format_color_fill_outlined), - getTrailing: () => Container( - padding: const EdgeInsets.only(right: 8.0), + getTrailing: (theme) => Container( width: 20, height: 20, decoration: BoxDecoration( @@ -512,58 +257,17 @@ List get styleSettings => [ ), ), NormalModel( - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SlideDialog( - title: 'Toast不透明度', - value: CustomToast.toastOpacity, - min: 0.0, - max: 1.0, - divisions: 10, - ); - }, - ); - if (result != null) { - CustomToast.toastOpacity = result; - await GStorage.setting.put(SettingBoxKey.defaultToastOp, result); - SmartDialog.showToast('设置成功'); - setState(); - } - }, leading: const Icon(Icons.opacity_outlined), title: '气泡提示不透明度', subtitle: '自定义气泡提示(Toast)不透明度', - getTrailing: () => Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Text( - CustomToast.toastOpacity.toStringAsFixed(1), - style: Get.theme.textTheme.titleSmall, - ), + getTrailing: (theme) => Text( + CustomToast.toastOpacity.toStringAsFixed(1), + style: theme.textTheme.titleSmall, ), + onTap: _showToastDialog, ), NormalModel( - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '主题模式', - value: Pref.themeType, - values: ThemeType.values.map((e) => (e, e.desc)).toList(), - ); - }, - ); - if (result != null) { - try { - Get.find().themeType.value = result; - } catch (_) {} - GStorage.setting.put(SettingBoxKey.themeMode, result.index); - Get.changeThemeMode(result.toThemeMode); - setState(); - } - }, + onTap: _showThemeTypeDialog, leading: const Icon(Icons.flashlight_on_outlined), title: '主题模式', getSubtitle: () => '当前模式:${Pref.themeType.desc}', @@ -584,39 +288,23 @@ List get styleSettings => [ leading: const Icon(Icons.color_lens_outlined), title: '应用主题', getSubtitle: () => '当前主题:${Pref.dynamicColor ? '动态取色' : '指定颜色'}', - getTrailing: () => Pref.dynamicColor - ? Icon(Icons.color_lens_rounded, color: Get.theme.colorScheme.primary) + getTrailing: (theme) => Pref.dynamicColor + ? Icon(Icons.color_lens_rounded, color: theme.colorScheme.primary) : SizedBox.square( - dimension: 32, + dimension: 20, child: ColorPalette( colorScheme: colorThemeTypes[Pref.customColor].color - .asColorSchemeSeed(Pref.schemeVariant, Get.theme.brightness), + .asColorSchemeSeed(Pref.schemeVariant, theme.brightness), selected: false, showBgColor: false, ), ), ), NormalModel( - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '首页启动页', - value: Pref.defaultHomePage, - values: NavigationBarType.values.map((e) => (e, e.label)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.defaultHomePage, result.index); - SmartDialog.showToast('设置成功,重启生效'); - setState(); - } - }, leading: const Icon(Icons.home_outlined), title: '默认启动页', getSubtitle: () => '当前启动页:${Pref.defaultHomePage.label}', + onTap: _showDefHomeDialog, ), const NormalModel( title: '滑动动画弹簧参数', @@ -720,96 +408,92 @@ void _showUiScaleDialog( showDialog( context: context, - builder: (context) { - return AlertDialog( - title: const Text('界面缩放'), - contentPadding: const EdgeInsets.fromLTRB(24, 20, 24, 12), - content: StatefulBuilder( - onDispose: textController.dispose, - builder: (context, setDialogState) { - return Column( - spacing: 20, - mainAxisSize: MainAxisSize.min, - children: [ - Slider( - padding: .zero, - value: uiScale, - min: minUiScale, - max: maxUiScale, - secondaryTrackValue: 1.0, - divisions: ((maxUiScale - minUiScale) * 20).toInt(), - label: textController.text, - onChanged: (value) => setDialogState(() { - uiScale = value.toPrecision(2); - textController.text = uiScale.toStringAsFixed(2); - }), - ), - TextFormField( - controller: textController, - keyboardType: const TextInputType.numberWithOptions( - decimal: true, - ), - inputFormatters: [ - LengthLimitingTextInputFormatter(4), - FilteringTextInputFormatter.allow(RegExp(r'[\d.]+')), - ], - decoration: const InputDecoration( - labelText: '缩放比例', - hintText: '0.50 - 2.00', - border: OutlineInputBorder(), - ), - onChanged: (value) { - final parsed = double.tryParse(value); - if (parsed != null && - parsed >= minUiScale && - parsed <= maxUiScale) { - setDialogState(() { - uiScale = parsed; - }); - } - }, - ), - ], - ); - }, - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - GStorage.setting.delete(SettingBoxKey.uiScale).whenComplete(() { - setState(); - Get.appUpdate(); - ScaledWidgetsFlutterBinding.instance.setScaleFactor(1.0); - }); - }, - child: const Text('重置'), - ), - TextButton( - onPressed: () => Navigator.pop(context), - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, + builder: (context) => AlertDialog( + title: const Text('界面缩放'), + contentPadding: const EdgeInsets.fromLTRB(24, 20, 24, 12), + content: StatefulBuilder( + onDispose: textController.dispose, + builder: (context, setDialogState) => Column( + spacing: 20, + mainAxisSize: MainAxisSize.min, + children: [ + Slider( + padding: .zero, + value: uiScale, + min: minUiScale, + max: maxUiScale, + secondaryTrackValue: 1.0, + divisions: ((maxUiScale - minUiScale) * 20).toInt(), + label: textController.text, + onChanged: (value) => setDialogState(() { + uiScale = value.toPrecision(2); + textController.text = uiScale.toStringAsFixed(2); + }), + ), + TextFormField( + controller: textController, + keyboardType: const TextInputType.numberWithOptions( + decimal: true, ), + inputFormatters: [ + LengthLimitingTextInputFormatter(4), + FilteringTextInputFormatter.allow(RegExp(r'[\d.]+')), + ], + decoration: const InputDecoration( + labelText: '缩放比例', + hintText: '0.50 - 2.00', + border: OutlineInputBorder(), + ), + onChanged: (value) { + final parsed = double.tryParse(value); + if (parsed != null && + parsed >= minUiScale && + parsed <= maxUiScale) { + setDialogState(() { + uiScale = parsed; + }); + } + }, + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + GStorage.setting.delete(SettingBoxKey.uiScale).whenComplete(() { + setState(); + Get.appUpdate(); + ScaledWidgetsFlutterBinding.instance.setScaleFactor(1.0); + }); + }, + child: const Text('重置'), + ), + TextButton( + onPressed: () => Navigator.pop(context), + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, ), ), - TextButton( - onPressed: () { - Navigator.pop(context); - GStorage.setting.put(SettingBoxKey.uiScale, uiScale).whenComplete( - () { - setState(); - Get.appUpdate(); - ScaledWidgetsFlutterBinding.instance.setScaleFactor(uiScale); - }, - ); - }, - child: const Text('确定'), - ), - ], - ); - }, + ), + TextButton( + onPressed: () { + Navigator.pop(context); + GStorage.setting.put(SettingBoxKey.uiScale, uiScale).whenComplete( + () { + setState(); + Get.appUpdate(); + ScaledWidgetsFlutterBinding.instance.setScaleFactor(uiScale); + }, + ); + }, + child: const Text('确定'), + ), + ], + ), ); } @@ -817,52 +501,49 @@ void _showSpringDurationDialog(BuildContext context) { String initialValue = '500'; showDialog( context: context, - builder: (context) { - return AlertDialog( - title: const Text('滑动时间'), - content: TextFormField( - autofocus: true, - keyboardType: .number, - initialValue: initialValue, - onChanged: (value) => initialValue = value, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - decoration: const InputDecoration(suffixText: 'ms'), + builder: (context) => AlertDialog( + title: const Text('滑动时间'), + content: TextFormField( + autofocus: true, + keyboardType: .number, + initialValue: initialValue, + onChanged: (value) => initialValue = value, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + decoration: const InputDecoration(suffixText: 'ms'), + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - onPressed: () { - try { - final milliseconds = int.parse(initialValue); - Get.back(); - final springDescription = - SpringDescription.withDurationAndBounce( - duration: Duration(milliseconds: milliseconds), - ); - GStorage.setting.put( - SettingBoxKey.springDescription, - [ - springDescription.mass, - springDescription.stiffness, - springDescription.damping, - ], - ); - SmartDialog.showToast('设置成功,重启生效'); - } catch (e) { - SmartDialog.showToast(e.toString()); - } - }, - child: const Text('确定'), - ), - ], - ); - }, + TextButton( + onPressed: () { + try { + final milliseconds = int.parse(initialValue); + Get.back(); + final springDescription = SpringDescription.withDurationAndBounce( + duration: Duration(milliseconds: milliseconds), + ); + GStorage.setting.put( + SettingBoxKey.springDescription, + [ + springDescription.mass, + springDescription.stiffness, + springDescription.damping, + ], + ); + SmartDialog.showToast('设置成功,重启生效'); + } catch (e) { + SmartDialog.showToast(e.toString()); + } + }, + child: const Text('确定'), + ), + ], + ), ); } @@ -872,74 +553,396 @@ void _showSpringDialog(BuildContext context, _) { .toList(); showDialog( context: context, - builder: (context) { - return AlertDialog( - title: Row( - mainAxisAlignment: .spaceBetween, - children: [ - const Text('弹簧参数'), - TextButton( - style: TextButton.styleFrom( - visualDensity: .compact, - tapTargetSize: .shrinkWrap, - ), - onPressed: () { - Get.back(); - _showSpringDurationDialog(context); - }, - child: const Text('滑动时间'), - ), - ], - ), - content: Column( - mainAxisSize: MainAxisSize.min, - children: List.generate( - 3, - (index) => TextFormField( - autofocus: index == 0, - initialValue: springDescription[index], - keyboardType: const .numberWithOptions(decimal: true), - onChanged: (value) => springDescription[index] = value, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), - ], - decoration: InputDecoration( - labelText: const ['mass', 'stiffness', 'damping'][index], - ), - ), - ), - ), - actions: [ + builder: (context) => AlertDialog( + title: Row( + mainAxisAlignment: .spaceBetween, + children: [ + const Text('弹簧参数'), TextButton( + style: TextButton.styleFrom( + visualDensity: .compact, + tapTargetSize: .shrinkWrap, + ), onPressed: () { Get.back(); - GStorage.setting.delete(SettingBoxKey.springDescription); - SmartDialog.showToast('重置成功,重启生效'); + _showSpringDurationDialog(context); }, - child: const Text('重置'), - ), - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - onPressed: () { - try { - final res = springDescription.map(double.parse).toList(); - Get.back(); - GStorage.setting.put(SettingBoxKey.springDescription, res); - SmartDialog.showToast('设置成功,重启生效'); - } catch (e) { - SmartDialog.showToast(e.toString()); - } - }, - child: const Text('确定'), + child: const Text('滑动时间'), ), ], - ); - }, + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: List.generate( + 3, + (index) => TextFormField( + autofocus: index == 0, + initialValue: springDescription[index], + keyboardType: const .numberWithOptions(decimal: true), + onChanged: (value) => springDescription[index] = value, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), + ], + decoration: InputDecoration( + labelText: const ['mass', 'stiffness', 'damping'][index], + ), + ), + ), + ), + actions: [ + TextButton( + onPressed: () { + Get.back(); + GStorage.setting.delete(SettingBoxKey.springDescription); + SmartDialog.showToast('重置成功,重启生效'); + }, + child: const Text('重置'), + ), + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () { + try { + final res = springDescription.map(double.parse).toList(); + Get.back(); + GStorage.setting.put(SettingBoxKey.springDescription, res); + SmartDialog.showToast('设置成功,重启生效'); + } catch (e) { + SmartDialog.showToast(e.toString()); + } + }, + child: const Text('确定'), + ), + ], + ), ); } + +Future _showFontWeightDialog(BuildContext context) async { + final res = await showDialog( + context: context, + builder: (context) => SlideDialog( + title: 'App字体字重', + value: Pref.appFontWeight.toDouble() + 1, + min: 1, + max: FontWeight.values.length.toDouble(), + divisions: FontWeight.values.length - 1, + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.appFontWeight, res.toInt() - 1); + Get.forceAppUpdate(); + } +} + +Future _showTransitionDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '页面过渡动画', + value: Pref.pageTransition, + values: Transition.values.map((e) => (e, e.name)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.pageTransition, res.index); + SmartDialog.showToast('重启生效'); + setState(); + } +} + +Future _showCardWidthDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog<(double, double)>( + context: context, + builder: (context) => DualSlideDialog( + title: '列表最大列宽度(默认240dp)', + value1: Pref.recommendCardWidth, + value2: Pref.smallCardWidth, + description1: '主页推荐流', + description2: '其他', + min: 150.0, + max: 500.0, + divisions: 35, + suffix: 'dp', + ), + ); + if (res != null) { + await GStorage.setting.putAll({ + SettingBoxKey.recommendCardWidth: res.$1, + SettingBoxKey.smallCardWidth: res.$2, + }); + SmartDialog.showToast('重启生效'); + setState(); + } +} + +Future _showUpPosDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '动态页UP主显示位置', + value: Pref.upPanelPosition, + values: UpPanelPosition.values.map((e) => (e, e.label)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.upPanelPosition, res.index); + SmartDialog.showToast('重启生效'); + setState(); + } +} + +Future _showDynBadgeDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '动态未读标记', + value: Pref.dynamicBadgeType, + values: DynamicBadgeMode.values.map((e) => (e, e.desc)).toList(), + ), + ); + if (res != null) { + final mainController = Get.find() + ..dynamicBadgeMode = DynamicBadgeMode.values[res.index]; + if (mainController.dynamicBadgeMode != DynamicBadgeMode.hidden) { + mainController.getUnreadDynamic(); + } + await GStorage.setting.put( + SettingBoxKey.dynamicBadgeMode, + res.index, + ); + SmartDialog.showToast('设置成功'); + setState(); + } +} + +Future _showMsgBadgeDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '消息未读标记', + value: Pref.msgBadgeMode, + values: DynamicBadgeMode.values.map((e) => (e, e.desc)).toList(), + ), + ); + if (res != null) { + final mainController = Get.find() + ..msgBadgeMode = DynamicBadgeMode.values[res.index]; + if (mainController.msgBadgeMode != DynamicBadgeMode.hidden) { + mainController.queryUnreadMsg(true); + } else { + mainController.msgUnReadCount.value = ''; + } + await GStorage.setting.put(SettingBoxKey.msgBadgeMode, res.index); + SmartDialog.showToast('设置成功'); + setState(); + } +} + +Future _showMsgUnReadDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog>( + context: context, + builder: (context) => MultiSelectDialog( + title: '消息未读类型', + initValues: Pref.msgUnReadTypeV2, + values: {for (final i in MsgUnReadType.values) i: i.title}, + ), + ); + if (res != null) { + final mainController = Get.find()..msgUnReadTypes = res; + if (mainController.msgBadgeMode != DynamicBadgeMode.hidden) { + mainController.queryUnreadMsg(); + } + await GStorage.setting.put( + SettingBoxKey.msgUnReadTypeV2, + res.map((item) => item.index).toList()..sort(), + ); + SmartDialog.showToast('设置成功'); + setState(); + } +} + +void _showScrollDialog(BuildContext context) { + String scrollThreshold = Pref.scrollThreshold.toString(); + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('滚动阈值'), + content: TextFormField( + autofocus: true, + initialValue: scrollThreshold, + keyboardType: const TextInputType.numberWithOptions( + decimal: true, + ), + onChanged: (value) { + scrollThreshold = value; + }, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), + ], + decoration: const InputDecoration(suffixText: 'px'), + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + TextButton( + onPressed: () { + Get.back(); + GStorage.setting.put( + SettingBoxKey.scrollThreshold, + max( + 10.0, + double.tryParse(scrollThreshold) ?? 50.0, + ), + ); + SmartDialog.showToast('重启生效'); + }, + child: const Text('确定'), + ), + ], + ), + ); +} + +void _showReduceColorDialog( + BuildContext context, + VoidCallback setState, +) { + final reduceLuxColor = Pref.reduceLuxColor; + showDialog( + context: context, + builder: (context) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 16), + title: const Text('Color Picker'), + content: SlideColorPicker( + color: reduceLuxColor ?? Colors.white, + onChanged: (Color? color) { + if (color != null && color != reduceLuxColor) { + if (color == Colors.white) { + NetworkImgLayer.reduceLuxColor = null; + GStorage.setting.delete(SettingBoxKey.reduceLuxColor); + SmartDialog.showToast('设置成功'); + setState(); + } else { + void onConfirm() { + NetworkImgLayer.reduceLuxColor = color; + GStorage.setting.put( + SettingBoxKey.reduceLuxColor, + color.toARGB32(), + ); + SmartDialog.showToast('设置成功'); + setState(); + } + + if (color.computeLuminance() < 0.2) { + showConfirmDialog( + context: context, + title: + '确认使用#${(color.toARGB32() & 0xFFFFFF).toRadixString(16).toUpperCase().padLeft(6)}?', + content: '所选颜色过于昏暗,可能会影响图片观看', + onConfirm: onConfirm, + ); + } else { + onConfirm(); + } + } + } + }, + ), + ), + ); +} + +Future _showToastDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SlideDialog( + title: 'Toast不透明度', + value: CustomToast.toastOpacity, + min: 0.0, + max: 1.0, + divisions: 10, + ), + ); + if (res != null) { + CustomToast.toastOpacity = res; + await GStorage.setting.put(SettingBoxKey.defaultToastOp, res); + SmartDialog.showToast('设置成功'); + setState(); + } +} + +Future _showThemeTypeDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '主题模式', + value: Pref.themeType, + values: ThemeType.values.map((e) => (e, e.desc)).toList(), + ), + ); + if (res != null) { + try { + Get.find().themeType.value = res; + } catch (_) {} + GStorage.setting.put(SettingBoxKey.themeMode, res.index); + Get.changeThemeMode(res.toThemeMode); + setState(); + } +} + +Future _showDefHomeDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '首页启动页', + value: Pref.defaultHomePage, + values: NavigationBarType.values.map((e) => (e, e.label)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.defaultHomePage, res.index); + SmartDialog.showToast('设置成功,重启生效'); + setState(); + } +} diff --git a/lib/pages/setting/models/video_settings.dart b/lib/pages/setting/models/video_settings.dart index 9b05e2959..b54e22c00 100644 --- a/lib/pages/setting/models/video_settings.dart +++ b/lib/pages/setting/models/video_settings.dart @@ -38,13 +38,16 @@ List get videoSettings => [ title: 'B站定向流量支持', subtitle: '若套餐含B站定向流量,则会自动使用。可查阅运营商的流量记录确认。', leading: const Icon(Icons.perm_data_setting_outlined), - getTrailing: () => IgnorePointer( + getTrailing: (theme) => IgnorePointer( child: Transform.scale( - alignment: Alignment.centerRight, scale: 0.8, + alignment: Alignment.centerRight, child: Switch( value: true, onChanged: (_) {}, + thumbIcon: WidgetStateProperty.all( + const Icon(Icons.lock_outline_rounded), + ), ), ), ), @@ -54,68 +57,13 @@ List get videoSettings => [ leading: const Icon(MdiIcons.cloudPlusOutline), getSubtitle: () => '当前使用:${VideoUtils.cdnService.desc},部分 CDN 可能失效,如无法播放请尝试切换', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return const CdnSelectDialog(); - }, - ); - if (result != null) { - VideoUtils.cdnService = result; - await GStorage.setting.put(SettingBoxKey.CDNService, result.name); - setState(); - } - }, + onTap: _showCDNDialog, ), NormalModel( title: '直播 CDN 设置', leading: const Icon(MdiIcons.cloudPlusOutline), getSubtitle: () => '当前使用:${Pref.liveCdnUrl ?? "默认"}', - onTap: (context, setState) async { - String? result = await showDialog( - context: context, - builder: (context) { - String host = Pref.liveCdnUrl ?? ''; - return AlertDialog( - title: const Text('输入CDN host'), - content: TextFormField( - initialValue: host, - autofocus: true, - onChanged: (value) => host = value, - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: ColorScheme.of(context).outline, - ), - ), - ), - TextButton( - onPressed: () => Get.back(result: host), - child: const Text('确定'), - ), - ], - ); - }, - ); - if (result != null) { - if (result.isEmpty) { - result = null; - await GStorage.setting.delete(SettingBoxKey.liveCdnUrl); - } else { - if (!result.startsWith('http')) { - result = 'https://$result'; - } - await GStorage.setting.put(SettingBoxKey.liveCdnUrl, result); - } - VideoUtils.liveCdnUrl = result; - setState(); - } - }, + onTap: _showLiveCDNDialog, ), const SwitchModel( title: 'CDN 测速', @@ -130,221 +78,69 @@ List get videoSettings => [ leading: const Icon(MdiIcons.musicNotePlus), setKey: SettingBoxKey.disableAudioCDN, defaultVal: false, - onChanged: (value) { - VideoUtils.disableAudioCDN = value; - }, + onChanged: (value) => VideoUtils.disableAudioCDN = value, ), NormalModel( title: '默认画质', leading: const Icon(Icons.video_settings_outlined), getSubtitle: () => '当前画质:${VideoQuality.fromCode(Pref.defaultVideoQa).desc}', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '默认画质', - value: Pref.defaultVideoQa, - values: VideoQuality.values.map((e) => (e.code, e.desc)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.defaultVideoQa, result); - setState(); - } - }, + onTap: _showVideoQaDialog, ), NormalModel( title: '蜂窝网络画质', leading: const Icon(Icons.video_settings_outlined), getSubtitle: () => '当前画质:${VideoQuality.fromCode(Pref.defaultVideoQaCellular).desc}', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '蜂窝网络画质', - value: Pref.defaultVideoQaCellular, - values: VideoQuality.values.map((e) => (e.code, e.desc)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put( - SettingBoxKey.defaultVideoQaCellular, - result, - ); - setState(); - } - }, + onTap: _showVideoCellularQaDialog, ), NormalModel( title: '默认音质', leading: const Icon(Icons.music_video_outlined), getSubtitle: () => '当前音质:${AudioQuality.fromCode(Pref.defaultAudioQa).desc}', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '默认音质', - value: Pref.defaultAudioQa, - values: AudioQuality.values.map((e) => (e.code, e.desc)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.defaultAudioQa, result); - setState(); - } - }, + onTap: _showAudioQaDialog, ), NormalModel( title: '蜂窝网络音质', leading: const Icon(Icons.music_video_outlined), getSubtitle: () => '当前音质:${AudioQuality.fromCode(Pref.defaultAudioQaCellular).desc}', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '蜂窝网络音质', - value: Pref.defaultAudioQaCellular, - values: AudioQuality.values.map((e) => (e.code, e.desc)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put( - SettingBoxKey.defaultAudioQaCellular, - result, - ); - setState(); - } - }, + onTap: _showAudioCellularQaDialog, ), NormalModel( title: '直播默认画质', leading: const Icon(Icons.video_settings_outlined), getSubtitle: () => '当前画质:${LiveQuality.fromCode(Pref.liveQuality)?.desc}', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '直播默认画质', - value: Pref.liveQuality, - values: LiveQuality.values.map((e) => (e.code, e.desc)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.liveQuality, result); - setState(); - } - }, + onTap: _showLiveQaDialog, ), NormalModel( title: '蜂窝网络直播默认画质', leading: const Icon(Icons.video_settings_outlined), getSubtitle: () => '当前画质:${LiveQuality.fromCode(Pref.liveQualityCellular)?.desc}', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '蜂窝网络直播默认画质', - value: Pref.liveQualityCellular, - values: LiveQuality.values.map((e) => (e.code, e.desc)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.liveQualityCellular, result); - setState(); - } - }, + onTap: _showLiveCellularQaDialog, ), NormalModel( title: '首选解码格式', leading: const Icon(Icons.movie_creation_outlined), getSubtitle: () => '首选解码格式:${VideoDecodeFormatType.fromCode(Pref.defaultDecode).description},请根据设备支持情况与需求调整', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '默认解码格式', - value: Pref.defaultDecode, - values: VideoDecodeFormatType.values - .map((e) => (e.codes.first, e.description)) - .toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.defaultDecode, result); - setState(); - } - }, + onTap: _showDecodeDialog, ), NormalModel( title: '次选解码格式', getSubtitle: () => '非杜比视频次选:${VideoDecodeFormatType.fromCode(Pref.secondDecode).description},仍无则选择首个提供的解码格式', leading: const Icon(Icons.swap_horizontal_circle_outlined), - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '次选解码格式', - value: Pref.secondDecode, - values: VideoDecodeFormatType.values - .map((e) => (e.codes.first, e.description)) - .toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.secondDecode, result); - setState(); - } - }, + onTap: _showSecondDecodeDialog, ), if (kDebugMode || Platform.isAndroid) NormalModel( title: '音频输出设备', leading: const Icon(Icons.speaker_outlined), getSubtitle: () => '当前:${Pref.audioOutput}', - onTap: (context, setState) async { - final result = await showDialog>( - context: context, - builder: (context) { - return OrderedMultiSelectDialog( - title: '音频输出设备', - initValues: Pref.audioOutput.split(','), - values: { - for (final e in AudioOutput.values) e.name: e.label, - }, - ); - }, - ); - if (result != null && result.isNotEmpty) { - await GStorage.setting.put( - SettingBoxKey.audioOutput, - result.join(','), - ); - setState(); - } - }, + onTap: _showAudioOutputDialog, ), const SwitchModel( title: '扩大缓冲区', @@ -358,58 +154,298 @@ List get videoSettings => [ title: '视频同步', leading: const Icon(Icons.view_timeline_outlined), getSubtitle: () => '当前:${Pref.videoSync}(此项即mpv的--video-sync)', - onTap: (context, setState) async { - final result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '视频同步', - value: Pref.videoSync, - values: const [ - 'audio', - 'display-resample', - 'display-resample-vdrop', - 'display-resample-desync', - 'display-tempo', - 'display-vdrop', - 'display-adrop', - 'display-desync', - 'desync', - ].map((e) => (e, e)).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.videoSync, result); - setState(); - } - }, + onTap: _showVideoSyncDialog, ), NormalModel( title: '硬解模式', leading: const Icon(Icons.memory_outlined), getSubtitle: () => '当前:${Pref.hardwareDecoding}(此项即mpv的--hwdec)', - onTap: (context, setState) async { - final result = await showDialog>( - context: context, - builder: (context) { - return OrderedMultiSelectDialog( - title: '硬解模式', - initValues: Pref.hardwareDecoding.split(','), - values: { - for (final e in HwDecType.values) - e.hwdec: '${e.hwdec}\n${e.desc}', - }, - ); - }, - ); - if (result != null && result.isNotEmpty) { - await GStorage.setting.put( - SettingBoxKey.hardwareDecoding, - result.join(','), - ); - setState(); - } - }, + onTap: _showHwDecDialog, ), ]; + +Future _showCDNDialog(BuildContext context, VoidCallback setState) async { + final res = await showDialog( + context: context, + builder: (context) => const CdnSelectDialog(), + ); + if (res != null) { + VideoUtils.cdnService = res; + await GStorage.setting.put(SettingBoxKey.CDNService, res.name); + setState(); + } +} + +Future _showLiveCDNDialog( + BuildContext context, + VoidCallback setState, +) async { + String host = Pref.liveCdnUrl ?? ''; + String? res = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('输入CDN host'), + content: TextFormField( + initialValue: host, + autofocus: true, + onChanged: (value) => host = value, + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: ColorScheme.of(context).outline, + ), + ), + ), + TextButton( + onPressed: () => Get.back(result: host), + child: const Text('确定'), + ), + ], + ), + ); + if (res != null) { + if (res.isEmpty) { + res = null; + await GStorage.setting.delete(SettingBoxKey.liveCdnUrl); + } else { + if (!res.startsWith('http')) { + res = 'https://$res'; + } + await GStorage.setting.put(SettingBoxKey.liveCdnUrl, res); + } + VideoUtils.liveCdnUrl = res; + setState(); + } +} + +Future _showVideoQaDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '默认画质', + value: Pref.defaultVideoQa, + values: VideoQuality.values.map((e) => (e.code, e.desc)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.defaultVideoQa, res); + setState(); + } +} + +Future _showVideoCellularQaDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '蜂窝网络画质', + value: Pref.defaultVideoQaCellular, + values: VideoQuality.values.map((e) => (e.code, e.desc)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put( + SettingBoxKey.defaultVideoQaCellular, + res, + ); + setState(); + } +} + +Future _showAudioQaDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '默认音质', + value: Pref.defaultAudioQa, + values: AudioQuality.values.map((e) => (e.code, e.desc)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.defaultAudioQa, res); + setState(); + } +} + +Future _showAudioCellularQaDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '蜂窝网络音质', + value: Pref.defaultAudioQaCellular, + values: AudioQuality.values.map((e) => (e.code, e.desc)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put( + SettingBoxKey.defaultAudioQaCellular, + res, + ); + setState(); + } +} + +Future _showLiveQaDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '直播默认画质', + value: Pref.liveQuality, + values: LiveQuality.values.map((e) => (e.code, e.desc)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.liveQuality, res); + setState(); + } +} + +Future _showLiveCellularQaDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '蜂窝网络直播默认画质', + value: Pref.liveQualityCellular, + values: LiveQuality.values.map((e) => (e.code, e.desc)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.liveQualityCellular, res); + setState(); + } +} + +Future _showDecodeDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '默认解码格式', + value: Pref.defaultDecode, + values: VideoDecodeFormatType.values + .map((e) => (e.codes.first, e.description)) + .toList(), + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.defaultDecode, res); + setState(); + } +} + +Future _showSecondDecodeDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '次选解码格式', + value: Pref.secondDecode, + values: VideoDecodeFormatType.values + .map((e) => (e.codes.first, e.description)) + .toList(), + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.secondDecode, res); + setState(); + } +} + +Future _showAudioOutputDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog>( + context: context, + builder: (context) => OrderedMultiSelectDialog( + title: '音频输出设备', + initValues: Pref.audioOutput.split(','), + values: { + for (final e in AudioOutput.values) e.name: e.label, + }, + ), + ); + if (res != null && res.isNotEmpty) { + await GStorage.setting.put( + SettingBoxKey.audioOutput, + res.join(','), + ); + setState(); + } +} + +Future _showVideoSyncDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog( + context: context, + builder: (context) => SelectDialog( + title: '视频同步', + value: Pref.videoSync, + values: const [ + 'audio', + 'display-resample', + 'display-resample-vdrop', + 'display-resample-desync', + 'display-tempo', + 'display-vdrop', + 'display-adrop', + 'display-desync', + 'desync', + ].map((e) => (e, e)).toList(), + ), + ); + if (res != null) { + await GStorage.setting.put(SettingBoxKey.videoSync, res); + setState(); + } +} + +Future _showHwDecDialog( + BuildContext context, + VoidCallback setState, +) async { + final res = await showDialog>( + context: context, + builder: (context) => OrderedMultiSelectDialog( + title: '硬解模式', + initValues: Pref.hardwareDecoding.split(','), + values: { + for (final e in HwDecType.values) e.hwdec: '${e.hwdec}\n${e.desc}', + }, + ), + ); + if (res != null && res.isNotEmpty) { + await GStorage.setting.put( + SettingBoxKey.hardwareDecoding, + res.join(','), + ); + setState(); + } +} diff --git a/lib/pages/setting/pages/color_select.dart b/lib/pages/setting/pages/color_select.dart index 1f69165d1..4279994e0 100644 --- a/lib/pages/setting/pages/color_select.dart +++ b/lib/pages/setting/pages/color_select.dart @@ -61,13 +61,11 @@ class _ColorSelectPageState extends State { onTap: () async { final result = await showDialog( context: context, - builder: (context) { - return SelectDialog( - title: '主题模式', - value: ctr.themeType.value, - values: ThemeType.values.map((e) => (e, e.desc)).toList(), - ); - }, + builder: (context) => SelectDialog( + title: '主题模式', + value: ctr.themeType.value, + values: ThemeType.values.map((e) => (e, e.desc)).toList(), + ), ); if (result != null) { try { diff --git a/lib/pages/setting/pages/play_speed_set.dart b/lib/pages/setting/pages/play_speed_set.dart index 78b5fb50b..4b9336fb1 100644 --- a/lib/pages/setting/pages/play_speed_set.dart +++ b/lib/pages/setting/pages/play_speed_set.dart @@ -59,61 +59,59 @@ class _PlaySpeedPageState extends State { double? customSpeed; showDialog( context: context, - builder: (context) { - return AlertDialog( - title: const Text('添加倍速'), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 12), - TextField( - autofocus: true, - keyboardType: const TextInputType.numberWithOptions( - decimal: true, - ), - decoration: const InputDecoration( - labelText: '自定义倍速', - border: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(6)), - ), - ), - onChanged: (value) { - customSpeed = double.tryParse(value); - }, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), - ], + builder: (context) => AlertDialog( + title: const Text('添加倍速'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 12), + TextField( + autofocus: true, + keyboardType: const TextInputType.numberWithOptions( + decimal: true, ), - ], - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle(color: Theme.of(context).colorScheme.outline), + decoration: const InputDecoration( + labelText: '自定义倍速', + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(6)), + ), ), - ), - TextButton( - onPressed: () { - if (customSpeed == null) { - SmartDialog.showToast('输入倍数不合法'); - } else if (speedList.contains(customSpeed)) { - SmartDialog.showToast('该倍速已存在'); - } else { - Get.back(); - speedList - ..add(customSpeed!) - ..sort(); - video.put(VideoBoxKey.speedsList, speedList); - setState(() {}); - } + onChanged: (value) { + customSpeed = double.tryParse(value); }, - child: const Text('确认'), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), + ], ), ], - ); - }, + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () { + if (customSpeed == null) { + SmartDialog.showToast('输入倍数不合法'); + } else if (speedList.contains(customSpeed)) { + SmartDialog.showToast('该倍速已存在'); + } else { + Get.back(); + speedList + ..add(customSpeed!) + ..sort(); + video.put(VideoBoxKey.speedsList, speedList); + setState(() {}); + } + }, + child: const Text('确认'), + ), + ], + ), ); } diff --git a/lib/pages/setting/view.dart b/lib/pages/setting/view.dart index 5fed45165..fb94fa9c5 100644 --- a/lib/pages/setting/view.dart +++ b/lib/pages/setting/view.dart @@ -214,15 +214,13 @@ class _SettingPageState extends State { Future _logoutDialog(BuildContext context) async { final result = await showDialog>( context: context, - builder: (context) { - return MultiSelectDialog( - title: '选择要登出的账号uid', - initValues: const Iterable.empty(), - values: { - for (final i in Accounts.account.values) i: i.mid.toString(), - }, - ); - }, + builder: (context) => MultiSelectDialog( + title: '选择要登出的账号uid', + initValues: const Iterable.empty(), + values: { + for (final i in Accounts.account.values) i: i.mid.toString(), + }, + ), ); if (!context.mounted || result == null || result.isEmpty) return; Future logout() { diff --git a/lib/pages/setting/widgets/normal_item.dart b/lib/pages/setting/widgets/normal_item.dart index c0e8c0f4d..549da2c72 100644 --- a/lib/pages/setting/widgets/normal_item.dart +++ b/lib/pages/setting/widgets/normal_item.dart @@ -7,7 +7,7 @@ class NormalItem extends StatefulWidget { final String? subtitle; final ValueGetter? getSubtitle; final Widget? leading; - final ValueGetter? getTrailing; + final Widget Function(ThemeData theme)? getTrailing; final void Function(BuildContext context, VoidCallback setState)? onTap; final EdgeInsetsGeometry? contentPadding; final TextStyle? titleStyle; @@ -33,6 +33,15 @@ class _NormalItemState extends State { @override Widget build(BuildContext context) { late final theme = Theme.of(context); + Widget? subtitle; + if ((widget.subtitle ?? widget.getSubtitle?.call()) case final text?) { + subtitle = Text( + text, + style: theme.textTheme.labelMedium!.copyWith( + color: theme.colorScheme.outline, + ), + ); + } return ListTile( contentPadding: widget.contentPadding, onTap: widget.onTap == null @@ -42,16 +51,9 @@ class _NormalItemState extends State { widget.title ?? widget.getTitle!(), style: widget.titleStyle ?? theme.textTheme.titleMedium!, ), - subtitle: widget.subtitle != null || widget.getSubtitle != null - ? Text( - widget.subtitle ?? widget.getSubtitle!(), - style: theme.textTheme.labelMedium!.copyWith( - color: theme.colorScheme.outline, - ), - ) - : null, + subtitle: subtitle, leading: widget.leading, - trailing: widget.getTrailing?.call(), + trailing: widget.getTrailing?.call(theme), ); } diff --git a/lib/pages/setting/widgets/switch_item.dart b/lib/pages/setting/widgets/switch_item.dart index 6e31660ce..d4402c3c3 100644 --- a/lib/pages/setting/widgets/switch_item.dart +++ b/lib/pages/setting/widgets/switch_item.dart @@ -93,14 +93,14 @@ class _SetSwitchItemState extends State { @override Widget build(BuildContext context) { final theme = Theme.of(context); - TextStyle titleStyle = + final titleStyle = widget.titleStyle ?? theme.textTheme.titleMedium!.copyWith( color: widget.onTap != null && !val ? theme.colorScheme.outline : null, ); - TextStyle subTitleStyle = theme.textTheme.labelMedium!.copyWith( + final subTitleStyle = theme.textTheme.labelMedium!.copyWith( color: theme.colorScheme.outline, ); return ListTile( @@ -113,8 +113,8 @@ class _SetSwitchItemState extends State { : null, leading: widget.leading, trailing: Transform.scale( - alignment: Alignment.centerRight, scale: 0.8, + alignment: .centerRight, child: Switch( value: val, onChanged: switchChange, diff --git a/lib/pages/sponsor_block/view.dart b/lib/pages/sponsor_block/view.dart index 57e19b556..f4ca874a5 100644 --- a/lib/pages/sponsor_block/view.dart +++ b/lib/pages/sponsor_block/view.dart @@ -80,45 +80,43 @@ class _SponsorBlockPageState extends State { _textController.text = _blockLimit.toString(); showDialog( context: context, - builder: (_) { - return AlertDialog( - title: Text('最短片段时长', style: titleStyle), - content: TextFormField( - keyboardType: const TextInputType.numberWithOptions( - decimal: true, - ), - controller: _textController, - autofocus: true, - decoration: const InputDecoration(suffixText: 's'), - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), - ], + builder: (_) => AlertDialog( + title: Text('最短片段时长', style: titleStyle), + content: TextFormField( + keyboardType: const TextInputType.numberWithOptions( + decimal: true, ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: theme.colorScheme.outline, - ), + controller: _textController, + autofocus: true, + decoration: const InputDecoration(suffixText: 's'), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), + ], + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: theme.colorScheme.outline, ), ), - TextButton( - onPressed: () { - Get.back(); - _blockLimit = max( - 0.0, - double.tryParse(_textController.text) ?? 0.0, - ); - setting.put(SettingBoxKey.blockLimit, _blockLimit); - (context as Element).markNeedsBuild(); - }, - child: const Text('确定'), - ), - ], - ); - }, + ), + TextButton( + onPressed: () { + Get.back(); + _blockLimit = max( + 0.0, + double.tryParse(_textController.text) ?? 0.0, + ); + setting.put(SettingBoxKey.blockLimit, _blockLimit); + (context as Element).markNeedsBuild(); + }, + child: const Text('确定'), + ), + ], + ), ); }, title: Text('最短片段时长', style: titleStyle), @@ -322,49 +320,47 @@ class _SponsorBlockPageState extends State { _textController.text = _blockServer; showDialog( context: context, - builder: (_) { - return AlertDialog( - title: Text('服务器地址', style: titleStyle), - content: TextFormField( - keyboardType: TextInputType.url, - controller: _textController, - autofocus: true, + builder: (_) => AlertDialog( + title: Text('服务器地址', style: titleStyle), + content: TextFormField( + keyboardType: TextInputType.url, + controller: _textController, + autofocus: true, + ), + actions: [ + TextButton( + onPressed: () { + Get.back(); + _blockServer = HttpString.sponsorBlockBaseUrl; + setting.put(SettingBoxKey.blockServer, _blockServer); + Request.accountManager.blockServer = _blockServer; + (context as Element).markNeedsBuild(); + }, + child: const Text('重置'), ), - actions: [ - TextButton( - onPressed: () { - Get.back(); - _blockServer = HttpString.sponsorBlockBaseUrl; - setting.put(SettingBoxKey.blockServer, _blockServer); - Request.accountManager.blockServer = _blockServer; - (context as Element).markNeedsBuild(); - }, - child: const Text('重置'), - ), - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: theme.colorScheme.outline, - ), + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: theme.colorScheme.outline, ), ), - TextButton( - onPressed: () { - Get.back(); - _blockServer = _textController.text; - setting.put(SettingBoxKey.blockServer, _blockServer); - Request.accountManager.blockServer = _blockServer; - _checkServerStatus(); - _getUserInfo(); - (context as Element).markNeedsBuild(); - }, - child: const Text('确定'), - ), - ], - ); - }, + ), + TextButton( + onPressed: () { + Get.back(); + _blockServer = _textController.text; + setting.put(SettingBoxKey.blockServer, _blockServer); + Request.accountManager.blockServer = _blockServer; + _checkServerStatus(); + _getUserInfo(); + (context as Element).markNeedsBuild(); + }, + child: const Text('确定'), + ), + ], + ), ); }, title: Text( diff --git a/lib/pages/video/introduction/pgc/controller.dart b/lib/pages/video/introduction/pgc/controller.dart index 613b6e494..6426cc5f9 100644 --- a/lib/pages/video/introduction/pgc/controller.dart +++ b/lib/pages/video/introduction/pgc/controller.dart @@ -140,69 +140,68 @@ class PgcIntroController extends CommonIntroController { // 分享视频 @override void actionShareVideo(BuildContext context) { + String videoUrl = + '${HttpString.baseUrl}/bangumi/play/ep$epId${videoDetailCtr.playedTimePos}'; showDialog( context: context, - builder: (_) { - String videoUrl = - '${HttpString.baseUrl}/bangumi/play/ep$epId${videoDetailCtr.playedTimePos}'; - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.symmetric(vertical: 12), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ + builder: (_) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + dense: true, + title: const Text( + '复制链接', + style: TextStyle(fontSize: 14), + ), + onTap: () { + Get.back(); + Utils.copyText(videoUrl); + }, + ), + ListTile( + dense: true, + title: const Text( + '其它app打开', + style: TextStyle(fontSize: 14), + ), + onTap: () { + Get.back(); + PageUtils.launchURL(videoUrl); + }, + ), + if (PlatformUtils.isMobile) ListTile( dense: true, title: const Text( - '复制链接', + '分享视频', style: TextStyle(fontSize: 14), ), onTap: () { Get.back(); - Utils.copyText(videoUrl); + Utils.shareText(videoUrl); }, ), - ListTile( - dense: true, - title: const Text( - '其它app打开', - style: TextStyle(fontSize: 14), - ), - onTap: () { - Get.back(); - PageUtils.launchURL(videoUrl); - }, + ListTile( + dense: true, + title: const Text( + '分享至动态', + style: TextStyle(fontSize: 14), ), - if (PlatformUtils.isMobile) - ListTile( - dense: true, - title: const Text( - '分享视频', - style: TextStyle(fontSize: 14), - ), - onTap: () { - Get.back(); - Utils.shareText(videoUrl); - }, - ), - ListTile( - dense: true, - title: const Text( - '分享至动态', - style: TextStyle(fontSize: 14), - ), - onTap: () { - Get.back(); - EpisodeItem? item = pgcItem.episodes?.firstWhereOrNull( - (item) => item.epId == epId, - ); - showModalBottomSheet( - context: context, - isScrollControlled: true, - useSafeArea: true, - builder: (context) => RepostPanel( - rid: epId, - /** + onTap: () { + Get.back(); + EpisodeItem? item = pgcItem.episodes?.firstWhereOrNull( + (item) => item.epId == epId, + ); + showModalBottomSheet( + context: context, + isScrollControlled: true, + useSafeArea: true, + builder: (context) => RepostPanel( + rid: epId, + /** * 1:番剧 // 4097 2:电影 // 4098 3:纪录片 // 4101 @@ -211,67 +210,66 @@ class PgcIntroController extends CommonIntroController { 6:漫画 7:综艺 // 4099 */ - dynType: switch (pgcItem.type) { - 1 => 4097, - 2 => 4098, - 3 => 4101, - 4 => 4100, - 5 || 7 => 4099, - _ => -1, - }, - pic: pgcItem.cover, - title: - '${pgcItem.title}${item != null ? '\n${item.showTitle}' : ''}', - uname: '', - ), + dynType: switch (pgcItem.type) { + 1 => 4097, + 2 => 4098, + 3 => 4101, + 4 => 4100, + 5 || 7 => 4099, + _ => -1, + }, + pic: pgcItem.cover, + title: + '${pgcItem.title}${item != null ? '\n${item.showTitle}' : ''}', + uname: '', + ), + ); + }, + ), + ListTile( + dense: true, + title: const Text( + '分享至消息', + style: TextStyle(fontSize: 14), + ), + onTap: () { + Get.back(); + try { + EpisodeItem item = pgcItem.episodes!.firstWhere( + (item) => item.epId == epId, ); - }, - ), - ListTile( - dense: true, - title: const Text( - '分享至消息', - style: TextStyle(fontSize: 14), - ), - onTap: () { - Get.back(); - try { - EpisodeItem item = pgcItem.episodes!.firstWhere( - (item) => item.epId == epId, - ); - final title = - item.shareCopy ?? - '${pgcItem.title} ${item.showTitle ?? item.longTitle}'; - PageUtils.pmShare( - context, - content: { - "id": epId!.toString(), - "title": title, - "url": item.shareUrl, - "headline": title, - "source": 16, - "thumb": item.cover, - "source_desc": switch (pgcItem.type) { - 1 => '番剧', - 2 => '电影', - 3 => '纪录片', - 4 => '国创', - 5 => '电视剧', - 6 => '漫画', - 7 => '综艺', - _ => null, - }, + final title = + item.shareCopy ?? + '${pgcItem.title} ${item.showTitle ?? item.longTitle}'; + PageUtils.pmShare( + context, + content: { + "id": epId!.toString(), + "title": title, + "url": item.shareUrl, + "headline": title, + "source": 16, + "thumb": item.cover, + "source_desc": switch (pgcItem.type) { + 1 => '番剧', + 2 => '电影', + 3 => '纪录片', + 4 => '国创', + 5 => '电视剧', + 6 => '漫画', + 7 => '综艺', + _ => null, }, - ); - } catch (e) { - SmartDialog.showToast(e.toString()); - } - }, - ), - ], - ), - ); - }, + }, + ); + } catch (e) { + SmartDialog.showToast(e.toString()); + } + }, + ), + ], + ), + ), ); } diff --git a/lib/pages/video/introduction/ugc/controller.dart b/lib/pages/video/introduction/ugc/controller.dart index 07ee2a90e..8e63c6157 100644 --- a/lib/pages/video/introduction/ugc/controller.dart +++ b/lib/pages/video/introduction/ugc/controller.dart @@ -298,108 +298,106 @@ class UgcIntroController extends CommonIntroController with ReloadMixin { // 分享视频 @override void actionShareVideo(BuildContext context) { + final videoDetail = this.videoDetail.value; + String videoUrl = + '${HttpString.baseUrl}/video/$bvid${videoDetailCtr.playedTimePos}'; showDialog( context: context, - builder: (_) { - final videoDetail = this.videoDetail.value; - String videoUrl = - '${HttpString.baseUrl}/video/$bvid${videoDetailCtr.playedTimePos}'; - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.symmetric(vertical: 12), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - dense: true, - title: const Text( - '复制链接', - style: TextStyle(fontSize: 14), - ), - onTap: () { - Get.back(); - Utils.copyText(videoUrl); - }, + builder: (_) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + dense: true, + title: const Text( + '复制链接', + style: TextStyle(fontSize: 14), ), - ListTile( - dense: true, - title: const Text( - '其它app打开', - style: TextStyle(fontSize: 14), - ), - onTap: () { - Get.back(); - PageUtils.launchURL(videoUrl); - }, + onTap: () { + Get.back(); + Utils.copyText(videoUrl); + }, + ), + ListTile( + dense: true, + title: const Text( + '其它app打开', + style: TextStyle(fontSize: 14), ), - if (PlatformUtils.isMobile) - ListTile( - dense: true, - title: const Text( - '分享视频', - style: TextStyle(fontSize: 14), - ), - onTap: () { - Get.back(); - Utils.shareText( - '${videoDetail.title} ' - 'UP主: ${videoDetail.owner!.name!}' - ' - $videoUrl', - ); - }, - ), + onTap: () { + Get.back(); + PageUtils.launchURL(videoUrl); + }, + ), + if (PlatformUtils.isMobile) ListTile( dense: true, title: const Text( - '分享至动态', + '分享视频', style: TextStyle(fontSize: 14), ), onTap: () { Get.back(); - showModalBottomSheet( - context: context, - isScrollControlled: true, - useSafeArea: true, - builder: (context) => RepostPanel( - rid: videoDetail.aid, - dynType: 8, - pic: videoDetail.pic, - title: videoDetail.title, - uname: videoDetail.owner?.name, - ), + Utils.shareText( + '${videoDetail.title} ' + 'UP主: ${videoDetail.owner!.name!}' + ' - $videoUrl', ); }, ), - ListTile( - dense: true, - title: const Text( - '分享至消息', - style: TextStyle(fontSize: 14), - ), - onTap: () { - Get.back(); - try { - PageUtils.pmShare( - context, - content: { - "id": videoDetail.aid!.toString(), - "title": videoDetail.title!, - "headline": videoDetail.title!, - "source": 5, - "thumb": videoDetail.pic!, - "author": videoDetail.owner!.name!, - "author_id": videoDetail.owner!.mid!.toString(), - }, - ); - } catch (e) { - SmartDialog.showToast(e.toString()); - } - }, + ListTile( + dense: true, + title: const Text( + '分享至动态', + style: TextStyle(fontSize: 14), ), - ], - ), - ); - }, + onTap: () { + Get.back(); + showModalBottomSheet( + context: context, + isScrollControlled: true, + useSafeArea: true, + builder: (context) => RepostPanel( + rid: videoDetail.aid, + dynType: 8, + pic: videoDetail.pic, + title: videoDetail.title, + uname: videoDetail.owner?.name, + ), + ); + }, + ), + ListTile( + dense: true, + title: const Text( + '分享至消息', + style: TextStyle(fontSize: 14), + ), + onTap: () { + Get.back(); + try { + PageUtils.pmShare( + context, + content: { + "id": videoDetail.aid!.toString(), + "title": videoDetail.title!, + "headline": videoDetail.title!, + "source": 5, + "thumb": videoDetail.pic!, + "author": videoDetail.owner!.name!, + "author_id": videoDetail.owner!.mid!.toString(), + }, + ); + } catch (e) { + SmartDialog.showToast(e.toString()); + } + }, + ), + ], + ), + ), ); } diff --git a/lib/pages/video/post_panel/view.dart b/lib/pages/video/post_panel/view.dart index 7151a0936..605124005 100644 --- a/lib/pages/video/post_panel/view.dart +++ b/lib/pages/video/post_panel/view.dart @@ -104,36 +104,34 @@ class PostPanel extends CommonSlidePage { tooltip: '编辑', icon: const Icon(Icons.edit), onPressed: () async { + String initV = value; final res = await showDialog( context: context, - builder: (context) { - String initV = value; - return AlertDialog( - content: TextFormField( - initialValue: value, - autofocus: true, - onChanged: (value) => initV = value, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[\d:.]+')), - ], - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: theme.colorScheme.outline, - ), + builder: (context) => AlertDialog( + content: TextFormField( + initialValue: value, + autofocus: true, + onChanged: (value) => initV = value, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[\d:.]+')), + ], + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: theme.colorScheme.outline, ), ), - TextButton( - onPressed: () => Get.back(result: initV), - child: const Text('确定'), - ), - ], - ); - }, + ), + TextButton( + onPressed: () => Get.back(result: initV), + child: const Text('确定'), + ), + ], + ), ); if (res != null) { diff --git a/lib/pages/video/reply/widgets/reply_item_grpc.dart b/lib/pages/video/reply/widgets/reply_item_grpc.dart index f44057923..e4b1eac9f 100644 --- a/lib/pages/video/reply/widgets/reply_item_grpc.dart +++ b/lib/pages/video/reply/widgets/reply_item_grpc.dart @@ -976,17 +976,15 @@ class ReplyItemGrpc extends StatelessWidget { Get.back(); showDialog( context: context, - builder: (context) { - return Dialog( - child: Padding( - padding: const .symmetric(horizontal: 20, vertical: 16), - child: SelectableText( - message, - style: const TextStyle(fontSize: 15, height: 1.7), - ), + builder: (context) => Dialog( + child: Padding( + padding: const .symmetric(horizontal: 20, vertical: 16), + child: SelectableText( + message, + style: const TextStyle(fontSize: 15, height: 1.7), ), - ); - }, + ), + ), ); }, minLeadingWidth: 0, diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index 3c3e830ac..077b77d70 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -536,11 +536,9 @@ class HeaderControlState extends State Get.back(); final result = await showDialog( context: context, - builder: (context) { - return CdnSelectDialog( - sample: videoInfo.dash?.video?.firstOrNull, - ); - }, + builder: (context) => CdnSelectDialog( + sample: videoInfo.dash?.video?.firstOrNull, + ), ); if (result != null) { VideoUtils.cdnService = result; @@ -1230,68 +1228,66 @@ class HeaderControlState extends State void onExportSubtitle() { showDialog( context: context, - builder: (context) { - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12), - title: const Text('保存字幕'), - content: SingleChildScrollView( - child: Column( - children: videoDetailCtr.subtitles - .map( - (item) => ListTile( - dense: true, - onTap: () async { - Get.back(); - final url = item.subtitleUrl; - if (url == null || url.isEmpty) return; - try { - final res = await Request.dio.get( - url.http2https, - options: Options( - responseType: ResponseType.bytes, - headers: Constants.baseHeaders, - extra: {'account': const NoAccount()}, + builder: (context) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12), + title: const Text('保存字幕'), + content: SingleChildScrollView( + child: Column( + children: videoDetailCtr.subtitles + .map( + (item) => ListTile( + dense: true, + onTap: () async { + Get.back(); + final url = item.subtitleUrl; + if (url == null || url.isEmpty) return; + try { + final res = await Request.dio.get( + url.http2https, + options: Options( + responseType: ResponseType.bytes, + headers: Constants.baseHeaders, + extra: {'account': const NoAccount()}, + ), + ); + if (res.statusCode == 200) { + final bytes = Uint8List.fromList( + Request.responseBytesDecoder( + res.data!, + res.headers.map, ), ); - if (res.statusCode == 200) { - final bytes = Uint8List.fromList( - Request.responseBytesDecoder( - res.data!, - res.headers.map, - ), - ); - String name = - '${introController.videoDetail.value.title}-${videoDetailCtr.bvid}-${videoDetailCtr.cid.value}-${item.lanDoc}.json'; - if (Platform.isWindows) { - // Reserved characters may not be used in file names. See: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions - name = name.replaceAll( - RegExp(r'[<>:/\\|?*"]'), - '', - ); - } - Utils.saveBytes2File( - name: name, - bytes: bytes, - allowedExtensions: const ['json'], + String name = + '${introController.videoDetail.value.title}-${videoDetailCtr.bvid}-${videoDetailCtr.cid.value}-${item.lanDoc}.json'; + if (Platform.isWindows) { + // Reserved characters may not be used in file names. See: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions + name = name.replaceAll( + RegExp(r'[<>:/\\|?*"]'), + '', ); } - } catch (e, s) { - Utils.reportError(e, s); - SmartDialog.showToast(e.toString()); + Utils.saveBytes2File( + name: name, + bytes: bytes, + allowedExtensions: const ['json'], + ); } - }, - title: Text( - item.lanDoc!, - style: const TextStyle(fontSize: 14), - ), + } catch (e, s) { + Utils.reportError(e, s); + SmartDialog.showToast(e.toString()); + } + }, + title: Text( + item.lanDoc!, + style: const TextStyle(fontSize: 14), ), - ) - .toList(), - ), + ), + ) + .toList(), ), - ); - }, + ), + ), ); } diff --git a/lib/pages/whisper/widgets/item.dart b/lib/pages/whisper/widgets/item.dart index c1f452324..048dc23b8 100644 --- a/lib/pages/whisper/widgets/item.dart +++ b/lib/pages/whisper/widgets/item.dart @@ -56,51 +56,49 @@ class WhisperSessionItem extends StatelessWidget { : null, onLongPress: () => showDialog( context: context, - builder: (context) { - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.symmetric(vertical: 12), - content: DefaultTextStyle( - style: const TextStyle(fontSize: 14), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ + builder: (context) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + content: DefaultTextStyle( + style: const TextStyle(fontSize: 14), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + dense: true, + onTap: () { + Get.back(); + onSetTop(item.isPinned, item.id); + }, + title: Text(item.isPinned ? '移除置顶' : '置顶'), + ), + if (item.id.privateId.hasTalkerUid()) ListTile( dense: true, onTap: () { Get.back(); - onSetTop(item.isPinned, item.id); + onSetMute(item.isMuted, item.id.privateId.talkerUid); }, - title: Text(item.isPinned ? '移除置顶' : '置顶'), + title: Text('${item.isMuted ? '关闭' : '开启'}免打扰'), ), - if (item.id.privateId.hasTalkerUid()) - ListTile( - dense: true, - onTap: () { - Get.back(); - onSetMute(item.isMuted, item.id.privateId.talkerUid); - }, - title: Text('${item.isMuted ? '关闭' : '开启'}免打扰'), - ), - if (item.id.privateId.hasTalkerUid()) - ListTile( - dense: true, - onTap: () { - Get.back(); - showConfirmDialog( - context: context, - title: '确定删除该对话?', - onConfirm: () => - onRemove(item.id.privateId.talkerUid.toInt()), - ); - }, - title: const Text('删除'), - ), - ], - ), + if (item.id.privateId.hasTalkerUid()) + ListTile( + dense: true, + onTap: () { + Get.back(); + showConfirmDialog( + context: context, + title: '确定删除该对话?', + onConfirm: () => + onRemove(item.id.privateId.talkerUid.toInt()), + ); + }, + title: const Text('删除'), + ), + ], ), - ); - }, + ), + ), ), onSecondaryTapUp: PlatformUtils.isDesktop ? (details) => showMenu( diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index d141e770b..70bc69310 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -237,46 +237,44 @@ class _WhisperDetailPageState Feedback.forLongPress(context); showDialog( context: context, - builder: (context) { - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.symmetric(vertical: 12), - content: isOwner - ? ListTile( - onTap: () { - Get.back(); - _whisperDetailController.sendMsg( - message: '${item.msgKey}', - onClearText: editController.clear, - msgType: 5, - index: index, - ); - }, - dense: true, - title: const Text('撤回', style: TextStyle(fontSize: 14)), - ) - : ListTile( - onTap: () { - Get.back(); - autoWrapReportDialog( - context, - ban: false, - ReportOptions.imMsgReport, - (reasonType, reasonDesc, banUid) => - _whisperDetailController.onReport( - item, - reasonType, - reasonType == 0 - ? reasonDesc! - : ReportOptions.imMsgReport['']![reasonType]!, - ), - ); - }, - dense: true, - title: const Text('举报', style: TextStyle(fontSize: 14)), - ), - ); - }, + builder: (context) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + content: isOwner + ? ListTile( + onTap: () { + Get.back(); + _whisperDetailController.sendMsg( + message: '${item.msgKey}', + onClearText: editController.clear, + msgType: 5, + index: index, + ); + }, + dense: true, + title: const Text('撤回', style: TextStyle(fontSize: 14)), + ) + : ListTile( + onTap: () { + Get.back(); + autoWrapReportDialog( + context, + ban: false, + ReportOptions.imMsgReport, + (reasonType, reasonDesc, banUid) => + _whisperDetailController.onReport( + item, + reasonType, + reasonType == 0 + ? reasonDesc! + : ReportOptions.imMsgReport['']![reasonType]!, + ), + ); + }, + dense: true, + title: const Text('举报', style: TextStyle(fontSize: 14)), + ), + ), ); } diff --git a/lib/pages/whisper_settings/view.dart b/lib/pages/whisper_settings/view.dart index 12de7ab0d..61dd9d7a6 100644 --- a/lib/pages/whisper_settings/view.dart +++ b/lib/pages/whisper_settings/view.dart @@ -77,52 +77,50 @@ class _WhisperSettingsPageState extends State { String? selected; showDialog( context: context, - builder: (context) { - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.symmetric(vertical: 12), - content: Column( - mainAxisSize: MainAxisSize.min, - children: item.redirect.windowSelect.item.map( - (e) { - if (e.selected) { - selected ??= e.text; - } - return ListTile( - dense: true, - onTap: () async { - if (!e.selected) { - Get.back(); - for (final j in item.redirect.windowSelect.item) { - j.selected = false; - } - item.redirect.selectedSummary = e.text; - e.selected = true; - _controller.loadingState.refresh(); - final settings = {key: item}; - final res = await _controller.onSet(settings); - if (!res) { - for (final j in item.redirect.windowSelect.item) { - j.selected = j.text == selected; - } - item.redirect.selectedSummary = selected!; - _controller.loadingState.refresh(); - } + builder: (context) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + content: Column( + mainAxisSize: MainAxisSize.min, + children: item.redirect.windowSelect.item.map( + (e) { + if (e.selected) { + selected ??= e.text; + } + return ListTile( + dense: true, + onTap: () async { + if (!e.selected) { + Get.back(); + for (final j in item.redirect.windowSelect.item) { + j.selected = false; } - }, - title: Text( - e.text, - style: TextStyle( - fontSize: 14, - color: e.selected ? theme.colorScheme.primary : null, - ), + item.redirect.selectedSummary = e.text; + e.selected = true; + _controller.loadingState.refresh(); + final settings = {key: item}; + final res = await _controller.onSet(settings); + if (!res) { + for (final j in item.redirect.windowSelect.item) { + j.selected = j.text == selected; + } + item.redirect.selectedSummary = selected!; + _controller.loadingState.refresh(); + } + } + }, + title: Text( + e.text, + style: TextStyle( + fontSize: 14, + color: e.selected ? theme.colorScheme.primary : null, ), - ); - }, - ).toList(), - ), - ); - }, + ), + ); + }, + ).toList(), + ), + ), ); } else if (item.redirect.otherPage.hasUrl()) { if (item.redirect.title == '黑名单') { diff --git a/lib/services/shutdown_timer_service.dart b/lib/services/shutdown_timer_service.dart index 9d3fe40f3..51b96b1a0 100644 --- a/lib/services/shutdown_timer_service.dart +++ b/lib/services/shutdown_timer_service.dart @@ -52,21 +52,19 @@ class ShutdownTimerService with WidgetsBindingObserver { void _showTimeUpButPauseDialog() { SmartDialog.show( - builder: (BuildContext dialogContext) { - return AlertDialog( - title: const Text('定时关闭'), - content: const Text('时间到啦!'), - actions: [ - TextButton( - child: const Text('确认'), - onPressed: () { - cancelShutdownTimer(); - SmartDialog.dismiss(); - }, - ), - ], - ); - }, + builder: (dialogContext) => AlertDialog( + title: const Text('定时关闭'), + content: const Text('时间到啦!'), + actions: [ + TextButton( + child: const Text('确认'), + onPressed: () { + cancelShutdownTimer(); + SmartDialog.dismiss(); + }, + ), + ], + ), ); } diff --git a/lib/utils/image_utils.dart b/lib/utils/image_utils.dart index 88f8cd084..bfaf11f3a 100644 --- a/lib/utils/image_utils.dart +++ b/lib/utils/image_utils.dart @@ -58,21 +58,19 @@ abstract final class ImageUtils { if (status == PermissionStatus.denied || status == PermissionStatus.permanentlyDenied) { SmartDialog.show( - builder: (context) { - return AlertDialog( - title: const Text('提示'), - content: const Text('存储权限未授权'), - actions: [ - TextButton( - onPressed: () { - SmartDialog.dismiss(); - openAppSettings(); - }, - child: const Text('去授权'), - ), - ], - ); - }, + builder: (context) => AlertDialog( + title: const Text('提示'), + content: const Text('存储权限未授权'), + actions: [ + TextButton( + onPressed: () { + SmartDialog.dismiss(); + openAppSettings(); + }, + child: const Text('去授权'), + ), + ], + ), ); return false; } else { diff --git a/lib/utils/page_utils.dart b/lib/utils/page_utils.dart index ad46a2397..10f3ed3cf 100644 --- a/lib/utils/page_utils.dart +++ b/lib/utils/page_utils.dart @@ -130,11 +130,11 @@ abstract final class PageUtils { builder: (_, setState) { void onTap(int choice) { if (choice == -1) { + String duration = ''; showDialog( context: context, builder: (context) { - final ThemeData theme = Theme.of(context); - String duration = ''; + final theme = Theme.of(context); return AlertDialog( title: const Text('自定义时长'), content: TextField( diff --git a/lib/utils/request_utils.dart b/lib/utils/request_utils.dart index a215116fe..54fa8db97 100644 --- a/lib/utils/request_utils.dart +++ b/lib/utils/request_utils.dart @@ -131,110 +131,108 @@ abstract final class RequestUtils { } if (context.mounted) { + bool isSpecialFollowed = followStatus!['special'] == 1; + String text = isSpecialFollowed ? '移除特别关注' : '加入特别关注'; showDialog( context: context, - builder: (context) { - bool isSpecialFollowed = followStatus!['special'] == 1; - String text = isSpecialFollowed ? '移除特别关注' : '加入特别关注'; - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.symmetric(vertical: 12), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - dense: true, - onTap: () async { - Get.back(); - final res = await MemberHttp.specialAction( - fid: mid, - isAdd: !isSpecialFollowed, - ); - if (res.isSuccess) { - SmartDialog.showToast('$text成功'); - afterMod?.call(isSpecialFollowed ? 2 : -10); - } else { - res.toast(); - } - }, - title: Text( - text, - style: const TextStyle(fontSize: 14), - ), + builder: (context) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + dense: true, + onTap: () async { + Get.back(); + final res = await MemberHttp.specialAction( + fid: mid, + isAdd: !isSpecialFollowed, + ); + if (res.isSuccess) { + SmartDialog.showToast('$text成功'); + afterMod?.call(isSpecialFollowed ? 2 : -10); + } else { + res.toast(); + } + }, + title: Text( + text, + style: const TextStyle(fontSize: 14), ), - ListTile( - dense: true, - onTap: () async { - Get.back(); - final result = await showModalBottomSheet>( - context: context, - useSafeArea: true, - isScrollControlled: true, - constraints: BoxConstraints( - maxWidth: min(640, context.mediaQueryShortestSide), - ), - builder: (BuildContext context) { - final maxChildSize = - PlatformUtils.isMobile && - !context.mediaQuerySize.isPortrait - ? 1.0 - : 0.7; - return DraggableScrollableSheet( - minChildSize: 0, - maxChildSize: 1, - snap: true, - expand: false, - snapSizes: [maxChildSize], - initialChildSize: maxChildSize, - builder: - ( - BuildContext context, - ScrollController scrollController, - ) { - return GroupPanel( - mid: mid, - tags: followStatus!['tag'], - scrollController: scrollController, - ); - }, - ); - }, - ); - followStatus!['tag'] = result?.toList(); - if (result != null) { - afterMod?.call(result.contains(-10) ? -10 : 2); - } - }, - title: const Text( - '设置分组', - style: TextStyle(fontSize: 14), - ), + ), + ListTile( + dense: true, + onTap: () async { + Get.back(); + final result = await showModalBottomSheet>( + context: context, + useSafeArea: true, + isScrollControlled: true, + constraints: BoxConstraints( + maxWidth: min(640, context.mediaQueryShortestSide), + ), + builder: (BuildContext context) { + final maxChildSize = + PlatformUtils.isMobile && + !context.mediaQuerySize.isPortrait + ? 1.0 + : 0.7; + return DraggableScrollableSheet( + minChildSize: 0, + maxChildSize: 1, + snap: true, + expand: false, + snapSizes: [maxChildSize], + initialChildSize: maxChildSize, + builder: + ( + BuildContext context, + ScrollController scrollController, + ) { + return GroupPanel( + mid: mid, + tags: followStatus!['tag'], + scrollController: scrollController, + ); + }, + ); + }, + ); + followStatus!['tag'] = result?.toList(); + if (result != null) { + afterMod?.call(result.contains(-10) ? -10 : 2); + } + }, + title: const Text( + '设置分组', + style: TextStyle(fontSize: 14), ), - ListTile( - dense: true, - onTap: () async { - Get.back(); - final res = await VideoHttp.relationMod( - mid: mid, - act: 2, - reSrc: 11, - ); - if (res.isSuccess) { - SmartDialog.showToast('取消关注成功'); - afterMod?.call(0); - } else { - res.toast(); - } - }, - title: const Text( - '取消关注', - style: TextStyle(fontSize: 14), - ), + ), + ListTile( + dense: true, + onTap: () async { + Get.back(); + final res = await VideoHttp.relationMod( + mid: mid, + act: 2, + reSrc: 11, + ); + if (res.isSuccess) { + SmartDialog.showToast('取消关注成功'); + afterMod?.call(0); + } else { + res.toast(); + } + }, + title: const Text( + '取消关注', + style: TextStyle(fontSize: 14), ), - ], - ), - ); - }, + ), + ], + ), + ), ); } } diff --git a/lib/utils/theme_utils.dart b/lib/utils/theme_utils.dart index 103be2951..1715f566b 100644 --- a/lib/utils/theme_utils.dart +++ b/lib/utils/theme_utils.dart @@ -115,6 +115,8 @@ abstract final class ThemeUtils { selectionHandleColor: colorScheme.primary, ), switchTheme: const SwitchThemeData( + padding: .zero, + materialTapTargetSize: .shrinkWrap, thumbIcon: WidgetStateProperty.fromMap( { WidgetState.selected: Icon(Icons.done),