import 'package:ducafe_ui_core/ducafe_ui_core.dart'; import 'package:flutter/material.dart'; import 'package:forui/forui.dart'; import 'package:pj_supermarket_cloud/common/index.dart'; class SettingGroup extends StatelessWidget { const SettingGroup({super.key, required this.title, required this.child}); final String title; final Widget child; @override Widget build(BuildContext context) { final theme = FTheme.of(context); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: theme.typography.lg.copyWith( color: XfjDesignTokens.ink, fontWeight: FontWeight.w800, ), ), SizedBox(height: 12.h), Container( clipBehavior: Clip.antiAlias, decoration: BoxDecoration( color: XfjDesignTokens.surface, border: Border.all(color: XfjDesignTokens.line), borderRadius: BorderRadius.circular(XfjDesignTokens.cardRadius), ), child: child, ), ], ); } } class SettingSegmentedSelector extends StatelessWidget { const SettingSegmentedSelector({ super.key, required this.label, required this.value, required this.options, required this.onChanged, this.showDivider = true, }); final String label; final T value; final Map options; final ValueChanged onChanged; final bool showDivider; @override Widget build(BuildContext context) { final theme = FTheme.of(context); return Container( constraints: BoxConstraints(minHeight: 72.h), padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 12.h), decoration: BoxDecoration( border: showDivider ? const Border(bottom: BorderSide(color: XfjDesignTokens.line)) : null, ), child: Row( children: [ SizedBox( width: 230.w, child: Text( label, maxLines: 1, overflow: TextOverflow.ellipsis, style: theme.typography.base.copyWith( color: XfjDesignTokens.ink, fontWeight: FontWeight.w600, ), ), ), SizedBox(width: 16.w), Expanded( child: Align( alignment: Alignment.centerRight, child: Container( padding: EdgeInsets.all(4.w), decoration: BoxDecoration( color: const Color(0xFFF0F2F5), borderRadius: BorderRadius.circular( XfjDesignTokens.controlRadius, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: options.entries.map((entry) { final active = value == entry.key; return Padding( padding: EdgeInsets.only( left: entry.key == options.keys.first ? 0 : 4.w, ), child: InkWell( onTap: () => onChanged(entry.key), borderRadius: BorderRadius.circular( XfjDesignTokens.controlRadius, ), child: Container( constraints: BoxConstraints( minWidth: 112.w, minHeight: 48.h, ), padding: EdgeInsets.symmetric( horizontal: 20.w * XfjDesignTokens.buttonSizeScale, vertical: 6.h * XfjDesignTokens.buttonSizeScale, ), alignment: Alignment.center, decoration: BoxDecoration( color: active ? Colors.white : Colors.transparent, borderRadius: BorderRadius.circular( XfjDesignTokens.controlRadius, ), boxShadow: active ? XfjDesignTokens.surfaceShadow : null, ), child: Text( entry.value, maxLines: 1, overflow: TextOverflow.ellipsis, style: theme.typography.base.copyWith( color: active ? XfjDesignTokens.primary : XfjDesignTokens.muted, fontWeight: FontWeight.w600, ), ), ), ), ); }).toList(), ), ), ), ), ], ), ); } } class SettingInfoRow extends StatelessWidget { const SettingInfoRow({ super.key, required this.icon, required this.label, this.value, this.actionLabel, this.onTap, this.showDivider = true, this.trailing, this.subtitle, }); final SvgAsset icon; final String label; final String? value; final String? actionLabel; final VoidCallback? onTap; final bool showDivider; final Widget? trailing; final String? subtitle; @override Widget build(BuildContext context) { final theme = FTheme.of(context); final hasAction = actionLabel != null; return InkWell( onTap: onTap, child: Container( constraints: BoxConstraints(minHeight: subtitle == null ? 68.h : 80.h), padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h), decoration: BoxDecoration( border: showDivider ? const Border(bottom: BorderSide(color: XfjDesignTokens.line)) : null, ), child: Row( children: [ Container( width: 40.w, height: 40.w, alignment: Alignment.center, decoration: BoxDecoration( color: XfjDesignTokens.control, borderRadius: BorderRadius.circular( XfjDesignTokens.controlRadius, ), ), child: FIcon(icon, color: XfjDesignTokens.muted, size: 22.sp), ), SizedBox(width: 16.w), SizedBox( width: subtitle == null ? 230.w : 520.w, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( label, maxLines: 1, overflow: TextOverflow.ellipsis, style: theme.typography.base.copyWith( color: XfjDesignTokens.ink, fontWeight: FontWeight.w600, ), ), if (subtitle != null) ...[ SizedBox(height: 4.h), Text( subtitle!, maxLines: 2, overflow: TextOverflow.ellipsis, style: theme.typography.sm.copyWith( color: XfjDesignTokens.muted, ), ), ], ], ), ), SizedBox(width: 16.w), Expanded( child: value == null ? const SizedBox.shrink() : Text( value!, maxLines: subtitle == null ? 1 : 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.right, style: theme.typography.base.copyWith( color: XfjDesignTokens.muted, fontFeatures: const [FontFeature.tabularFigures()], ), ), ), if (trailing != null) ...[SizedBox(width: 16.w), trailing!], if (hasAction) ...[ SizedBox(width: 16.w), SizedBox( width: 148.w, child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Flexible( child: Text( actionLabel!, maxLines: 1, overflow: TextOverflow.ellipsis, style: theme.typography.base.copyWith( color: XfjDesignTokens.primary, fontWeight: FontWeight.w600, ), ), ), SizedBox(width: 6.w), FIcon( actionLabel == '复制' ? FAssets.icons.copy : FAssets.icons.chevronRight, color: XfjDesignTokens.primary, size: 18.sp, ), ], ), ), ], ], ), ), ); } } class SettingNotice extends StatelessWidget { const SettingNotice({ super.key, required this.text, this.color = XfjDesignTokens.muted, this.background = XfjDesignTokens.control, this.borderColor, this.icon, }); final String text; final Color color; final Color background; final Color? borderColor; final SvgAsset? icon; @override Widget build(BuildContext context) { final theme = FTheme.of(context); return Container( width: double.infinity, padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: background, border: borderColor == null ? null : Border.all(color: borderColor!), borderRadius: BorderRadius.circular(XfjDesignTokens.controlRadius), ), child: Row( children: [ if (icon != null) ...[ FIcon(icon!, color: color, size: 20.sp), SizedBox(width: 10.w), ], Expanded( child: Text( text, style: theme.typography.sm.copyWith(color: color), ), ), ], ), ); } } class SettingEmpty extends StatelessWidget { const SettingEmpty({super.key, required this.message, this.icon}); final String message; final SvgAsset? icon; @override Widget build(BuildContext context) { final theme = FTheme.of(context); return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ FIcon( icon ?? FAssets.icons.inbox, color: XfjDesignTokens.muted, size: 48.sp, ), SizedBox(height: 12.h), Text( message, style: theme.typography.base.copyWith( color: XfjDesignTokens.muted, ), ), ], ), ); } } class SettingActionButton extends StatelessWidget { const SettingActionButton({ super.key, required this.label, required this.onTap, this.icon, this.primary = false, this.danger = false, this.enabled = true, }); final String label; final VoidCallback onTap; final SvgAsset? icon; final bool primary; final bool danger; final bool enabled; @override Widget build(BuildContext context) { final theme = FTheme.of(context); final foreground = danger ? XfjDesignTokens.danger : primary ? Colors.white : XfjDesignTokens.primary; final background = primary ? XfjDesignTokens.primary : danger ? const Color(0xFFFFF3F2) : XfjDesignTokens.surface; final sizeScale = XfjDesignTokens.buttonSizeScale; return Opacity( opacity: enabled ? 1 : 0.45, child: InkWell( onTap: enabled ? onTap : null, borderRadius: BorderRadius.circular(XfjDesignTokens.controlRadius), child: Container( constraints: BoxConstraints(minHeight: 48.h), padding: EdgeInsets.symmetric( horizontal: 18.w * sizeScale, vertical: 10.h * sizeScale, ), decoration: BoxDecoration( color: background, border: Border.all( color: primary ? XfjDesignTokens.primary : danger ? XfjDesignTokens.danger.withValues(alpha: 0.35) : XfjDesignTokens.line, ), borderRadius: BorderRadius.circular(XfjDesignTokens.controlRadius), ), child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ if (icon != null) ...[ FIcon(icon!, color: foreground, size: 20.sp), SizedBox(width: 8.w), ], Text( label, style: theme.typography.base.copyWith( color: foreground, fontWeight: FontWeight.w600, ), ), ], ), ), ), ); } }