fix:按照shadcn_ui对着demo_shadcn对齐
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
import '../theme/yanting_text.dart';
|
||||
import '../theme/yanting_tokens.dart';
|
||||
|
||||
class AppButton extends StatelessWidget {
|
||||
@@ -21,48 +21,84 @@ class AppButton extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colors = switch (kind) {
|
||||
AppButtonKind.primary => (
|
||||
YantingColors.primary,
|
||||
YantingColors.primaryForeground,
|
||||
Colors.transparent,
|
||||
final leading = icon == null ? null : Icon(icon, size: 16);
|
||||
final width = expand ? double.infinity : null;
|
||||
final child = Text(label);
|
||||
|
||||
return switch (kind) {
|
||||
AppButtonKind.primary => ShadButton(
|
||||
width: width,
|
||||
onPressed: onPressed,
|
||||
leading: leading,
|
||||
child: child,
|
||||
),
|
||||
AppButtonKind.dark => (
|
||||
YantingColors.foreground,
|
||||
YantingColors.background,
|
||||
Colors.transparent,
|
||||
AppButtonKind.dark => ShadButton(
|
||||
width: width,
|
||||
onPressed: onPressed,
|
||||
leading: leading,
|
||||
backgroundColor: YantingColors.foreground,
|
||||
foregroundColor: YantingColors.background,
|
||||
hoverBackgroundColor: YantingColors.foreground.withValues(alpha: 0.9),
|
||||
child: child,
|
||||
),
|
||||
AppButtonKind.accent => (
|
||||
YantingColors.brandSoft,
|
||||
YantingColors.primaryForeground,
|
||||
Colors.transparent,
|
||||
AppButtonKind.accent => ShadButton.secondary(
|
||||
width: width,
|
||||
onPressed: onPressed,
|
||||
leading: leading,
|
||||
backgroundColor: YantingColors.brandSoft,
|
||||
foregroundColor: YantingColors.primaryForeground,
|
||||
hoverBackgroundColor: YantingColors.brandSoftBorder,
|
||||
child: child,
|
||||
),
|
||||
AppButtonKind.ghost => (
|
||||
YantingColors.background,
|
||||
YantingColors.foreground,
|
||||
YantingColors.border,
|
||||
AppButtonKind.ghost => ShadButton.outline(
|
||||
width: width,
|
||||
onPressed: onPressed,
|
||||
leading: leading,
|
||||
child: child,
|
||||
),
|
||||
};
|
||||
final child = FilledButton.icon(
|
||||
onPressed: onPressed,
|
||||
icon: icon == null ? const SizedBox.shrink() : Icon(icon, size: 18),
|
||||
label: Text(label),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: colors.$1,
|
||||
foregroundColor: colors.$2,
|
||||
disabledBackgroundColor: YantingColors.border,
|
||||
disabledForegroundColor: YantingColors.mutedForeground,
|
||||
minimumSize: Size(expand ? double.infinity : 0, 44),
|
||||
textStyle: YantingText.body.copyWith(fontWeight: FontWeight.w600),
|
||||
side: colors.$3 == Colors.transparent
|
||||
? BorderSide.none
|
||||
: BorderSide(color: colors.$3),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(YantingRadius.md),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
class AppIconButton extends StatelessWidget {
|
||||
const AppIconButton({
|
||||
required this.icon,
|
||||
required this.onPressed,
|
||||
this.kind = AppButtonKind.ghost,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final IconData icon;
|
||||
final VoidCallback? onPressed;
|
||||
final AppButtonKind kind;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final iconWidget = Icon(icon, size: 16);
|
||||
return switch (kind) {
|
||||
AppButtonKind.primary => ShadIconButton(
|
||||
onPressed: onPressed,
|
||||
icon: iconWidget,
|
||||
),
|
||||
);
|
||||
return expand ? SizedBox(width: double.infinity, child: child) : child;
|
||||
AppButtonKind.dark => ShadIconButton(
|
||||
onPressed: onPressed,
|
||||
backgroundColor: YantingColors.foreground,
|
||||
foregroundColor: YantingColors.background,
|
||||
hoverBackgroundColor: YantingColors.foreground.withValues(alpha: 0.9),
|
||||
icon: iconWidget,
|
||||
),
|
||||
AppButtonKind.accent => ShadIconButton.secondary(
|
||||
onPressed: onPressed,
|
||||
backgroundColor: YantingColors.brandSoft,
|
||||
foregroundColor: YantingColors.primaryForeground,
|
||||
hoverBackgroundColor: YantingColors.brandSoftBorder,
|
||||
icon: iconWidget,
|
||||
),
|
||||
AppButtonKind.ghost => ShadIconButton.outline(
|
||||
onPressed: onPressed,
|
||||
icon: iconWidget,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-18
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
import '../theme/yanting_tokens.dart';
|
||||
|
||||
@@ -20,29 +21,29 @@ class AppCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final decoration = BoxDecoration(
|
||||
color: color,
|
||||
border: Border.all(color: borderColor),
|
||||
borderRadius: BorderRadius.circular(YantingRadius.xl),
|
||||
);
|
||||
final content = DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
border: Border.all(color: borderColor),
|
||||
borderRadius: BorderRadius.circular(YantingRadius.xl),
|
||||
),
|
||||
child: Padding(padding: padding, child: child),
|
||||
final theme = ShadTheme.of(context);
|
||||
final radius = BorderRadius.circular(YantingRadius.xl);
|
||||
final content = ShadCard(
|
||||
padding: padding,
|
||||
backgroundColor: color,
|
||||
radius: radius,
|
||||
border: ShadBorder.all(color: borderColor),
|
||||
shadows: const [],
|
||||
child: child,
|
||||
);
|
||||
if (onTap == null) return content;
|
||||
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: Ink(
|
||||
decoration: decoration,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(YantingRadius.xl),
|
||||
onTap: onTap,
|
||||
child: Padding(padding: padding, child: child),
|
||||
borderRadius: radius,
|
||||
child: InkWell(
|
||||
borderRadius: radius,
|
||||
splashColor: theme.colorScheme.mutedForeground.withValues(alpha: 0.08),
|
||||
highlightColor: theme.colorScheme.mutedForeground.withValues(
|
||||
alpha: 0.04,
|
||||
),
|
||||
onTap: onTap,
|
||||
child: content,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
+49
-77
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
import '../theme/yanting_text.dart';
|
||||
import '../theme/yanting_tokens.dart';
|
||||
|
||||
class AppBadge extends StatelessWidget {
|
||||
@@ -17,60 +17,45 @@ class AppBadge extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colors = switch (kind) {
|
||||
BadgeKind.brand => (
|
||||
YantingColors.primary,
|
||||
YantingColors.primaryForeground,
|
||||
Colors.transparent,
|
||||
final child = Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (icon != null) ...[Icon(icon, size: 12), const SizedBox(width: 4)],
|
||||
Text(text),
|
||||
],
|
||||
);
|
||||
final shape = RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(YantingRadius.sm),
|
||||
side: kind == BadgeKind.tier || kind == BadgeKind.warning
|
||||
? const BorderSide(color: YantingColors.border)
|
||||
: BorderSide.none,
|
||||
);
|
||||
|
||||
return switch (kind) {
|
||||
BadgeKind.brand => ShadBadge(
|
||||
shape: shape,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 3),
|
||||
child: child,
|
||||
),
|
||||
BadgeKind.audio => (
|
||||
YantingColors.secondary,
|
||||
YantingColors.secondaryForeground,
|
||||
Colors.transparent,
|
||||
BadgeKind.audio || BadgeKind.neutral => ShadBadge.secondary(
|
||||
shape: shape,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 3),
|
||||
child: child,
|
||||
),
|
||||
BadgeKind.tier => (
|
||||
YantingColors.background,
|
||||
YantingColors.mutedForeground,
|
||||
YantingColors.border,
|
||||
BadgeKind.tier => ShadBadge.outline(
|
||||
shape: shape,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 3),
|
||||
foregroundColor: YantingColors.mutedForeground,
|
||||
child: child,
|
||||
),
|
||||
BadgeKind.warning => (
|
||||
YantingColors.background,
|
||||
YantingColors.destructive,
|
||||
YantingColors.border,
|
||||
),
|
||||
BadgeKind.neutral => (
|
||||
YantingColors.secondary,
|
||||
YantingColors.secondaryForeground,
|
||||
Colors.transparent,
|
||||
BadgeKind.warning => ShadBadge.destructive(
|
||||
shape: shape,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 3),
|
||||
backgroundColor: YantingColors.background,
|
||||
foregroundColor: YantingColors.destructive,
|
||||
child: child,
|
||||
),
|
||||
};
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: colors.$1,
|
||||
border: colors.$3 == Colors.transparent
|
||||
? null
|
||||
: Border.all(color: colors.$3),
|
||||
borderRadius: BorderRadius.circular(YantingRadius.sm),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 3),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (icon != null) ...[
|
||||
Icon(icon, size: 14, color: colors.$2),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
Text(
|
||||
text,
|
||||
style:
|
||||
(Theme.of(context).textTheme.labelSmall ?? YantingText.badge)
|
||||
.copyWith(color: colors.$2, letterSpacing: 0),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,33 +75,20 @@ class AppChip extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final background = selected
|
||||
? YantingColors.foreground
|
||||
: YantingColors.secondary;
|
||||
final foreground = selected
|
||||
? YantingColors.background
|
||||
: YantingColors.secondaryForeground;
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
color: background,
|
||||
borderRadius: BorderRadius.circular(YantingRadius.pill),
|
||||
),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(YantingRadius.pill),
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 9),
|
||||
child: Text(
|
||||
label,
|
||||
style:
|
||||
(Theme.of(context).textTheme.labelLarge ?? YantingText.chip)
|
||||
.copyWith(color: foreground, letterSpacing: 0),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
return ShadBadge.secondary(
|
||||
onPressed: onTap,
|
||||
shape: const StadiumBorder(),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 9),
|
||||
backgroundColor: selected
|
||||
? YantingColors.foreground
|
||||
: YantingColors.secondary,
|
||||
hoverBackgroundColor: selected
|
||||
? YantingColors.foreground.withValues(alpha: 0.9)
|
||||
: YantingColors.border,
|
||||
foregroundColor: selected
|
||||
? YantingColors.background
|
||||
: YantingColors.secondaryForeground,
|
||||
child: Text(label),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
import '../data/models/models.dart';
|
||||
import '../theme/app_icons.dart';
|
||||
import '../theme/yanting_text.dart';
|
||||
import '../theme/yanting_tokens.dart';
|
||||
import '../theme/wise_tokens.dart';
|
||||
import 'app_buttons.dart';
|
||||
import 'app_card.dart';
|
||||
|
||||
class PlayerStateModel {
|
||||
@@ -126,14 +128,12 @@ class MiniPlayer extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
ShadIconButton.ghost(
|
||||
onPressed: onToggle,
|
||||
icon: Icon(
|
||||
player.playing ? AppIcons.pause : AppIcons.playCircle,
|
||||
size: player.playing ? 24 : 28,
|
||||
),
|
||||
color: YantingColors.foreground,
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -184,13 +184,7 @@ class PlayerCard extends StatelessWidget {
|
||||
style: YantingText.meta.copyWith(fontSize: 12.5),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
LinearProgressIndicator(
|
||||
value: ratio.clamp(0, 1),
|
||||
minHeight: 4,
|
||||
borderRadius: BorderRadius.circular(YantingRadius.pill),
|
||||
backgroundColor: YantingColors.border,
|
||||
color: YantingColors.primary,
|
||||
),
|
||||
ShadProgress(value: ratio.clamp(0, 1)),
|
||||
const SizedBox(height: WiseSpacing.x2),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@@ -216,18 +210,15 @@ class PlayerCard extends StatelessWidget {
|
||||
children: [
|
||||
_SkipButton(label: '-15', onPressed: () => onSeek(-15)),
|
||||
const SizedBox(width: 26),
|
||||
IconButton.filled(
|
||||
onPressed: active ? onToggle : onStart,
|
||||
icon: Icon(
|
||||
active && player.playing
|
||||
SizedBox(
|
||||
width: 56,
|
||||
height: 56,
|
||||
child: AppIconButton(
|
||||
kind: AppButtonKind.primary,
|
||||
onPressed: active ? onToggle : onStart,
|
||||
icon: active && player.playing
|
||||
? AppIcons.pause
|
||||
: AppIcons.play,
|
||||
size: 28,
|
||||
),
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: YantingColors.primary,
|
||||
foregroundColor: YantingColors.primaryForeground,
|
||||
fixedSize: const Size(56, 56),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 26),
|
||||
@@ -236,22 +227,10 @@ class PlayerCard extends StatelessWidget {
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: TextButton(
|
||||
child: ShadButton.outline(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: onSpeed,
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: YantingColors.background,
|
||||
foregroundColor: YantingColors.foreground,
|
||||
side: const BorderSide(color: YantingColors.border),
|
||||
shape: const StadiumBorder(),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
),
|
||||
child: Text(
|
||||
'${player.speed.toStringAsFixed(1)}x',
|
||||
style: YantingText.meta.copyWith(
|
||||
color: YantingColors.foreground,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
child: Text('${player.speed.toStringAsFixed(1)}x'),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
+25
-46
@@ -1,27 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
import '../theme/wise_tokens.dart';
|
||||
import 'app_buttons.dart';
|
||||
import 'states.dart';
|
||||
|
||||
Future<void> showLoginSheet(BuildContext context, {String reason = '登录后保存当前动作'}) {
|
||||
return showModalBottomSheet<void>(
|
||||
Future<void> showLoginSheet(
|
||||
BuildContext context, {
|
||||
String reason = '登录后保存当前动作',
|
||||
}) {
|
||||
return showShadSheet<void>(
|
||||
context: context,
|
||||
showDragHandle: true,
|
||||
backgroundColor: WiseColors.surface,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(WiseRadius.lg)),
|
||||
),
|
||||
builder: (context) => Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 4, 20, 28),
|
||||
side: ShadSheetSide.bottom,
|
||||
builder: (context) => ShadSheet(
|
||||
title: const Text('登录研听'),
|
||||
description: Text(reason),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('登录研听', style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: WiseSpacing.x2),
|
||||
Text(reason, style: Theme.of(context).textTheme.bodyMedium),
|
||||
const SizedBox(height: WiseSpacing.x4),
|
||||
AppButton(
|
||||
label: '使用手机号继续',
|
||||
icon: Icons.phone_iphone,
|
||||
@@ -31,7 +26,7 @@ Future<void> showLoginSheet(BuildContext context, {String reason = '登录后保
|
||||
showAppToast(context, '登录接口待接入,已保留当前页面');
|
||||
},
|
||||
),
|
||||
const SizedBox(height: WiseSpacing.x2),
|
||||
const SizedBox(height: 8),
|
||||
AppButton(
|
||||
label: '微信 / Apple 登录占位',
|
||||
icon: Icons.account_circle_outlined,
|
||||
@@ -49,37 +44,21 @@ Future<void> showLoginSheet(BuildContext context, {String reason = '登录后保
|
||||
}
|
||||
|
||||
Future<void> showOutboundSheet(BuildContext context, {required String title}) {
|
||||
return showModalBottomSheet<void>(
|
||||
return showShadSheet<void>(
|
||||
context: context,
|
||||
showDragHandle: true,
|
||||
backgroundColor: WiseColors.surface,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(WiseRadius.lg)),
|
||||
),
|
||||
builder: (context) => Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 4, 20, 28),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('即将打开外部服务', style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: WiseSpacing.x2),
|
||||
Text(
|
||||
'$title\n外跳仅用于了解原文或相关服务,本内容不构成投资建议。',
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: WiseSpacing.x4),
|
||||
AppButton(
|
||||
label: '确认并记录占位事件',
|
||||
icon: Icons.open_in_new,
|
||||
kind: AppButtonKind.accent,
|
||||
expand: true,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
showAppToast(context, '外跳事件接口待接入');
|
||||
},
|
||||
),
|
||||
],
|
||||
side: ShadSheetSide.bottom,
|
||||
builder: (context) => ShadSheet(
|
||||
title: const Text('即将打开外部服务'),
|
||||
description: Text('$title\n外跳仅用于了解原文或相关服务,本内容不构成投资建议。'),
|
||||
child: AppButton(
|
||||
label: '确认并记录占位事件',
|
||||
icon: Icons.open_in_new,
|
||||
kind: AppButtonKind.accent,
|
||||
expand: true,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
showAppToast(context, '外跳事件接口待接入');
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
+70
-24
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
import '../theme/wise_tokens.dart';
|
||||
import '../theme/yanting_tokens.dart';
|
||||
import 'app_buttons.dart';
|
||||
import 'app_card.dart';
|
||||
|
||||
@@ -12,9 +13,10 @@ class LoadingState extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.separated(
|
||||
padding: const EdgeInsets.all(WiseSpacing.x4),
|
||||
padding: const EdgeInsets.all(YantingSpacing.screenX),
|
||||
itemCount: 4,
|
||||
separatorBuilder: (_, _) => const SizedBox(height: WiseSpacing.x3),
|
||||
separatorBuilder: (_, _) =>
|
||||
const SizedBox(height: YantingSpacing.cardGap),
|
||||
itemBuilder: (context, index) => const SkeletonCard(),
|
||||
);
|
||||
}
|
||||
@@ -30,11 +32,11 @@ class SkeletonCard extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: const [
|
||||
SkeletonLine(width: 96),
|
||||
SizedBox(height: WiseSpacing.x3),
|
||||
SizedBox(height: YantingSpacing.cardGap),
|
||||
SkeletonLine(width: double.infinity, height: 18),
|
||||
SizedBox(height: WiseSpacing.x2),
|
||||
SizedBox(height: YantingSpacing.x2),
|
||||
SkeletonLine(width: 240),
|
||||
SizedBox(height: WiseSpacing.x3),
|
||||
SizedBox(height: YantingSpacing.cardGap),
|
||||
SkeletonLine(width: 160),
|
||||
],
|
||||
),
|
||||
@@ -50,12 +52,58 @@ class SkeletonLine extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
final theme = ShadTheme.of(context);
|
||||
return _PulsingSkeleton(
|
||||
width: width,
|
||||
height: height,
|
||||
decoration: BoxDecoration(
|
||||
color: WiseColors.border,
|
||||
borderRadius: BorderRadius.circular(WiseRadius.pill),
|
||||
color: theme.colorScheme.muted,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PulsingSkeleton extends StatefulWidget {
|
||||
const _PulsingSkeleton({
|
||||
required this.color,
|
||||
required this.width,
|
||||
required this.height,
|
||||
});
|
||||
|
||||
final Color color;
|
||||
final double width;
|
||||
final double height;
|
||||
|
||||
@override
|
||||
State<_PulsingSkeleton> createState() => _PulsingSkeletonState();
|
||||
}
|
||||
|
||||
class _PulsingSkeletonState extends State<_PulsingSkeleton>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late final AnimationController _controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 1500),
|
||||
)..repeat(reverse: true);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = ShadTheme.of(context);
|
||||
return FadeTransition(
|
||||
opacity: Tween<double>(
|
||||
begin: 0.4,
|
||||
end: 1,
|
||||
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut)),
|
||||
child: Container(
|
||||
width: widget.width,
|
||||
height: widget.height,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.color,
|
||||
borderRadius: theme.radius,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -79,24 +127,29 @@ class EmptyState extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = ShadTheme.of(context);
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(WiseSpacing.x6),
|
||||
padding: const EdgeInsets.all(YantingSpacing.x6),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 42, color: WiseColors.primary),
|
||||
const SizedBox(height: WiseSpacing.x3),
|
||||
Icon(icon, size: 42, color: theme.colorScheme.foreground),
|
||||
const SizedBox(height: YantingSpacing.cardGap),
|
||||
Text(title, style: Theme.of(context).textTheme.titleMedium),
|
||||
const SizedBox(height: WiseSpacing.x2),
|
||||
const SizedBox(height: YantingSpacing.x2),
|
||||
Text(
|
||||
message,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
if (actionLabel != null) ...[
|
||||
const SizedBox(height: WiseSpacing.x4),
|
||||
AppButton(label: actionLabel!, onPressed: onAction, kind: AppButtonKind.ghost),
|
||||
const SizedBox(height: YantingSpacing.x4),
|
||||
AppButton(
|
||||
label: actionLabel!,
|
||||
onPressed: onAction,
|
||||
kind: AppButtonKind.ghost,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
@@ -124,12 +177,5 @@ class ErrorState extends StatelessWidget {
|
||||
}
|
||||
|
||||
void showAppToast(BuildContext context, String message) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
backgroundColor: WiseColors.primary,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(WiseRadius.md)),
|
||||
),
|
||||
);
|
||||
ShadToaster.of(context).show(ShadToast(title: Text(message)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user