feat(ui): 登录页重设计并统一弹框主题渐变外壳
This commit is contained in:
1 parent
c027acc91f
commit
de7da64886
25 files changed
+905
-317
No files matched your search
@@ -11,6 +11,7 @@ import '../../core/formatting/localized_date_time.dart';
|
||||
import '../../core/theme/app_colors.dart';
|
||||
import '../../core/theme/app_dimens.dart';
|
||||
import '../../core/theme/app_theme.dart';
|
||||
import '../../core/widgets/app_glow_panel.dart';
|
||||
import '../../core/widgets/auth_gate.dart';
|
||||
import '../../core/widgets/plain_page_scaffold.dart';
|
||||
import '../../l10n/app_localizations.dart';
|
||||
@@ -172,15 +173,15 @@ class _AccountDeactivatePageState extends State<AccountDeactivatePage> {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: Text(l10n.accountConfirmDeactivateTitle),
|
||||
content: Text(l10n.accountConfirmDeactivateMessage),
|
||||
builder: (dialogContext) => AppGlowDialog(
|
||||
title: l10n.accountConfirmDeactivateTitle,
|
||||
content: l10n.accountConfirmDeactivateMessage,
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
FilledButton(
|
||||
TextButton(
|
||||
key: const Key('account-deactivate-confirm'),
|
||||
onPressed: () => Navigator.of(dialogContext).pop(true),
|
||||
child: Text(l10n.accountConfirmDeactivate),
|
||||
|
||||
@@ -61,7 +61,7 @@ class _AuthFields extends StatelessWidget {
|
||||
onChanged: callbacks.onEmailChanged,
|
||||
onSubmitted: (_) => state.fields.passwordFocus.requestFocus(),
|
||||
style: const TextStyle(color: AppColors.onSurface, fontSize: 15),
|
||||
cursorColor: AppColors.blue,
|
||||
cursorColor: AppColors.brandRed,
|
||||
decoration: _decoration(
|
||||
context: context,
|
||||
hint: 'name@example.com',
|
||||
@@ -93,7 +93,7 @@ class _AuthFields extends StatelessWidget {
|
||||
onChanged: callbacks.onPasswordChanged,
|
||||
onSubmitted: (_) => callbacks.onSubmit(),
|
||||
style: const TextStyle(color: AppColors.onSurface, fontSize: 15),
|
||||
cursorColor: AppColors.blue,
|
||||
cursorColor: AppColors.brandRed,
|
||||
decoration: _decoration(
|
||||
context: context,
|
||||
hint: l10n.authPasswordHint,
|
||||
@@ -128,7 +128,7 @@ class _AuthFields extends StatelessWidget {
|
||||
style: const TextStyle(
|
||||
color: AppColors.muted,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -142,10 +142,7 @@ class _AuthFields extends StatelessWidget {
|
||||
}) {
|
||||
final border = OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(_fieldRadius),
|
||||
borderSide: const BorderSide(
|
||||
color: AppColors.border,
|
||||
width: _fieldBorderWidth,
|
||||
),
|
||||
borderSide: BorderSide.none,
|
||||
);
|
||||
final errorColor = Theme.of(context).colorScheme.error;
|
||||
final errorBorder = OutlineInputBorder(
|
||||
@@ -158,7 +155,7 @@ class _AuthFields extends StatelessWidget {
|
||||
prefixIcon: Icon(
|
||||
icon,
|
||||
size: 21,
|
||||
color: focused ? AppColors.blue : AppColors.muted,
|
||||
color: focused ? AppColors.brandRed : AppColors.muted,
|
||||
),
|
||||
prefixIconConstraints: const BoxConstraints(
|
||||
minWidth: _minTapTarget,
|
||||
@@ -166,7 +163,7 @@ class _AuthFields extends StatelessWidget {
|
||||
),
|
||||
suffixIcon: suffix,
|
||||
filled: true,
|
||||
fillColor: AppColors.card,
|
||||
fillColor: Colors.white.withValues(alpha: 0.05),
|
||||
isDense: true,
|
||||
contentPadding: const EdgeInsets.only(right: 16),
|
||||
border: border,
|
||||
@@ -175,7 +172,7 @@ class _AuthFields extends StatelessWidget {
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(_fieldRadius),
|
||||
borderSide: const BorderSide(
|
||||
color: AppColors.blue,
|
||||
color: AppColors.brandRed,
|
||||
width: _fieldBorderWidth,
|
||||
),
|
||||
),
|
||||
@@ -204,17 +201,17 @@ class _AuthSubmitButton extends StatelessWidget {
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return AppColors.blue.withValues(alpha: 0.5);
|
||||
return AppColors.brandRed.withValues(alpha: 0.5);
|
||||
}
|
||||
if (states.contains(WidgetState.pressed)) {
|
||||
return AppColors.bluePressed;
|
||||
return AppColors.brandRedPressed;
|
||||
}
|
||||
return AppColors.blue;
|
||||
return AppColors.brandRed;
|
||||
}),
|
||||
foregroundColor: const WidgetStatePropertyAll(AppColors.onPrimary),
|
||||
elevation: const WidgetStatePropertyAll(2),
|
||||
shadowColor: WidgetStatePropertyAll(
|
||||
AppColors.blue.withValues(alpha: 0.22),
|
||||
AppColors.brandRed.withValues(alpha: 0.22),
|
||||
),
|
||||
padding: const WidgetStatePropertyAll(EdgeInsets.zero),
|
||||
shape: WidgetStatePropertyAll(
|
||||
@@ -235,7 +232,7 @@ class _AuthSubmitButton extends StatelessWidget {
|
||||
status.signUpMode ? l10n.signUp : l10n.signIn,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -243,18 +240,16 @@ class _AuthSubmitButton extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _ModeSegment extends StatelessWidget {
|
||||
const _ModeSegment({
|
||||
class _ModeTab extends StatelessWidget {
|
||||
const _ModeTab({
|
||||
required this.segmentKey,
|
||||
required this.label,
|
||||
required this.hiddenLabel,
|
||||
required this.selected,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final Key segmentKey;
|
||||
final String label;
|
||||
final String hiddenLabel;
|
||||
final bool selected;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@@ -264,43 +259,30 @@ class _ModeSegment extends StatelessWidget {
|
||||
key: segmentKey,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: onTap,
|
||||
child: AnimatedContainer(
|
||||
duration: _segmentDuration,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: selected ? AppColors.blue : AppColors.transparent,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: selected ? AppColors.onPrimary : AppColors.onSurface,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: selected ? AppColors.textPrimary : AppColors.muted,
|
||||
fontSize: AppDimens.followingSegmentFont,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppDimens.followingSegmentIndicatorTopGap),
|
||||
Container(
|
||||
width: AppDimens.followingSegmentIndicatorWidth,
|
||||
height: AppDimens.followingSegmentIndicatorHeight,
|
||||
decoration: BoxDecoration(
|
||||
color: selected ? AppColors.brandRed : AppColors.transparent,
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppDimens.followingSegmentIndicatorRadius,
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: ExcludeSemantics(
|
||||
child: Opacity(
|
||||
opacity: 0,
|
||||
child: Center(
|
||||
child: Text(
|
||||
hiddenLabel,
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -349,7 +331,7 @@ class _AuthBrand extends StatelessWidget {
|
||||
style: const TextStyle(
|
||||
color: AppColors.onSurface,
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
height: 1.1,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -50,7 +50,7 @@ class _AuthLegalLinksState extends State<AuthLegalLinks> {
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final muted = TextStyle(color: AppColors.muted, fontSize: _legalFontSize, height: 1.6);
|
||||
final link = muted.copyWith(color: AppColors.blue);
|
||||
final link = muted.copyWith(color: AppColors.brandRed);
|
||||
return Text.rich(
|
||||
TextSpan(
|
||||
style: muted,
|
||||
|
||||
@@ -24,18 +24,14 @@ part 'auth_widgets.dart';
|
||||
part 'auth_form_widgets.dart';
|
||||
|
||||
const double _minTapTarget = 44;
|
||||
const double _brandMarkSize = 72;
|
||||
const double _brandMarkSize = 88;
|
||||
const double _compactBrandMarkSize = 48;
|
||||
const double _brandMarkRadius = 20;
|
||||
const double _brandMarkRadius = 26;
|
||||
const double _compactBrandMarkRadius = 14;
|
||||
const double _landingLogoTopGap = 28;
|
||||
const String _appLogoAsset = 'assets/icon/fruitshorts.png';
|
||||
const double _contentMaxWidth = 440;
|
||||
// Auth keeps its approved 20px card silhouette.
|
||||
const double _cardRadius = AppDimens.authCardRadius;
|
||||
const double _cardPadding = 20;
|
||||
const double _fieldHeight = 52;
|
||||
const double _fieldRadius = 14;
|
||||
const double _fieldRadius = 12;
|
||||
const double _fieldBorderWidth = 1;
|
||||
const double _buttonHeight = 50;
|
||||
const double _buttonRadius = 25;
|
||||
@@ -47,14 +43,11 @@ const double _bottomContentPadding = 28;
|
||||
const double _compactTopContentPadding = 40;
|
||||
const double _compactHeightBreakpoint = 720;
|
||||
const double _compactBrandGap = 16;
|
||||
const double _glowSize = 280;
|
||||
const double _warmGlowOpacity = 0.18;
|
||||
const double _entryButtonHeight = 50;
|
||||
const double _entryButtonRadius = 25;
|
||||
const double _entryButtonHeight = 52;
|
||||
const double _entryButtonRadius = 26;
|
||||
const double _entryButtonFontSize = 15;
|
||||
const double _entryIconSize = 20;
|
||||
const Duration _enterDuration = Duration(milliseconds: 220);
|
||||
const Duration _segmentDuration = Duration(milliseconds: 160);
|
||||
|
||||
Widget _brandMarkWidget({
|
||||
Key? key,
|
||||
@@ -267,45 +260,53 @@ class _AuthPageState extends State<AuthPage> {
|
||||
child: IgnorePointer(
|
||||
child: DecoratedBox(
|
||||
key: Key('auth-page-background'),
|
||||
decoration: BoxDecoration(gradient: AppTheme.pageGradient),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: -90,
|
||||
right: -70,
|
||||
width: _glowSize,
|
||||
height: _glowSize,
|
||||
child: IgnorePointer(
|
||||
child: Container(
|
||||
key: const Key('auth-background-glow'),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: RadialGradient(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
AppColors.surfaceTop,
|
||||
AppColors.surfaceTop.withValues(alpha: 0),
|
||||
AppColors.followingGlow,
|
||||
AppColors.followingGlowMid,
|
||||
AppColors.appBackground,
|
||||
],
|
||||
stops: [0, 0.14, 0.3],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
left: -110,
|
||||
bottom: -90,
|
||||
width: _glowSize,
|
||||
height: _glowSize,
|
||||
// 全屏海报背景:压暗保证文字可读,顶部再叠品牌红光。
|
||||
Positioned.fill(
|
||||
child: IgnorePointer(child: _AuthPosterBackdrop()),
|
||||
),
|
||||
const Positioned.fill(
|
||||
child: IgnorePointer(
|
||||
child: Container(
|
||||
key: const Key('auth-background-warm-glow'),
|
||||
child: DecoratedBox(
|
||||
key: Key('auth-poster-scrim'),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: RadialGradient(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
AppColors.gold.withValues(alpha: _warmGlowOpacity),
|
||||
AppColors.gold.withValues(alpha: 0),
|
||||
Color(0x66000000),
|
||||
Color(0x14000000),
|
||||
Color(0xE0000000),
|
||||
],
|
||||
stops: [0, 0.42, 1],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Positioned.fill(
|
||||
child: IgnorePointer(
|
||||
child: DecoratedBox(
|
||||
key: Key('auth-red-glow'),
|
||||
decoration: BoxDecoration(
|
||||
gradient: RadialGradient(
|
||||
center: Alignment(0, -1.15),
|
||||
radius: 1.3,
|
||||
colors: [AppColors.primaryBorder, AppColors.transparent],
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -347,7 +348,11 @@ class _AuthPageState extends State<AuthPage> {
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: _contentMaxWidth,
|
||||
),
|
||||
child: _content(l10n, compact: compact),
|
||||
child: _content(
|
||||
l10n,
|
||||
compact: compact,
|
||||
minHeight: minHeight,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -387,7 +392,11 @@ class _AuthPageState extends State<AuthPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _content(AppLocalizations l10n, {required bool compact}) =>
|
||||
Widget _content(
|
||||
AppLocalizations l10n, {
|
||||
required bool compact,
|
||||
required double minHeight,
|
||||
}) =>
|
||||
TweenAnimationBuilder<double>(
|
||||
key: const Key('auth-enter-animation'),
|
||||
tween: Tween(begin: 0, end: 1),
|
||||
@@ -409,15 +418,18 @@ class _AuthPageState extends State<AuthPage> {
|
||||
_authCard(l10n),
|
||||
],
|
||||
)
|
||||
: _landing(l10n),
|
||||
: _landing(l10n, minHeight: minHeight),
|
||||
);
|
||||
|
||||
Widget _landing(AppLocalizations l10n) => Column(
|
||||
/// 落地页内容需要有限高度才能用 Spacer 三段分割,这里显式钉住可用高度。
|
||||
Widget _landing(AppLocalizations l10n, {required double minHeight}) =>
|
||||
SizedBox(
|
||||
height: minHeight,
|
||||
child: Column(
|
||||
key: const Key('auth-landing'),
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const SizedBox(height: _landingLogoTopGap),
|
||||
const Spacer(flex: 5),
|
||||
Center(
|
||||
child: _brandMarkWidget(
|
||||
key: const Key('auth-brand-logo'),
|
||||
@@ -425,24 +437,19 @@ class _AuthPageState extends State<AuthPage> {
|
||||
radius: _brandMarkRadius,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
l10n.fruitShortsBrand,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Outfit',
|
||||
color: AppColors.onSurface,
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: 2,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
l10n.authLandingSubtitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(color: AppColors.muted, fontSize: 13),
|
||||
),
|
||||
const SizedBox(height: 28),
|
||||
const Spacer(flex: 3),
|
||||
_AuthEntryButton(
|
||||
key: const Key('auth-entry-phone'),
|
||||
label: l10n.authPhoneSignIn,
|
||||
@@ -458,10 +465,12 @@ class _AuthPageState extends State<AuthPage> {
|
||||
primary: false,
|
||||
onTap: () => setState(() => _emailEntryOpen = true),
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
const AuthLegalLinks(),
|
||||
],
|
||||
);
|
||||
const SizedBox(height: 14),
|
||||
const Opacity(opacity: 0.75, child: AuthLegalLinks()),
|
||||
const Spacer(flex: 6),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Future<void> _openPhoneSheet() async {
|
||||
final signedIn = await showPhoneAuthSheet(
|
||||
@@ -526,15 +535,15 @@ class _AuthEntryButton extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final background = primary ? AppColors.blue : AppColors.card;
|
||||
// 主按钮纯品牌红;次按钮为无边框深色玻璃(白 6%),Apple 风格。
|
||||
final background = primary
|
||||
? AppColors.brandRed
|
||||
: Colors.white.withValues(alpha: 0.06);
|
||||
final foreground = primary ? AppColors.onPrimary : AppColors.onSurface;
|
||||
return Material(
|
||||
color: background,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(_entryButtonRadius),
|
||||
side: primary
|
||||
? BorderSide.none
|
||||
: const BorderSide(color: AppColors.border),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: InkWell(
|
||||
@@ -556,7 +565,7 @@ class _AuthEntryButton extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
color: foreground,
|
||||
fontSize: _entryButtonFontSize,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -567,3 +576,18 @@ class _AuthEntryButton extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 登录页全屏海报背景(单张,无拼缝)。
|
||||
class _AuthPosterBackdrop extends StatelessWidget {
|
||||
const _AuthPosterBackdrop();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Image.asset(
|
||||
key: const Key('auth-poster-backdrop'),
|
||||
'assets/auth/bg_3.jpg',
|
||||
fit: BoxFit.cover,
|
||||
alignment: Alignment.center,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -70,52 +70,45 @@ class _AuthCard extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = state.l10n;
|
||||
final status = state.status;
|
||||
return Container(
|
||||
// 不再做卡片盒子,表单直接排在红光晕背景上。
|
||||
return Column(
|
||||
key: const Key('auth-card'),
|
||||
padding: const EdgeInsets.all(_cardPadding),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.card,
|
||||
borderRadius: BorderRadius.circular(_cardRadius),
|
||||
border: Border.all(color: AppColors.border),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_AuthModeSwitch(
|
||||
l10n: l10n,
|
||||
signUpMode: status.signUpMode,
|
||||
busy: status.busy,
|
||||
onSelected: callbacks.onModeSelected,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_AuthModeSwitch(
|
||||
l10n: l10n,
|
||||
signUpMode: status.signUpMode,
|
||||
busy: status.busy,
|
||||
onSelected: callbacks.onModeSelected,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
status.signUpMode ? l10n.authSignUpTitle : l10n.authSignInTitle,
|
||||
style: const TextStyle(
|
||||
color: AppColors.onSurface,
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.2,
|
||||
),
|
||||
const SizedBox(height: 22),
|
||||
Text(
|
||||
status.signUpMode ? l10n.authSignUpTitle : l10n.authSignInTitle,
|
||||
style: const TextStyle(
|
||||
color: AppColors.onSurface,
|
||||
fontSize: 25,
|
||||
fontWeight: FontWeight.w800,
|
||||
height: 1.2,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
status.signUpMode ? l10n.authSignUpSubtitle : l10n.authSubtitle,
|
||||
style: const TextStyle(
|
||||
color: AppColors.muted,
|
||||
fontSize: AppDimens.body,
|
||||
height: 1.45,
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
status.signUpMode ? l10n.authSignUpSubtitle : l10n.authSubtitle,
|
||||
style: const TextStyle(
|
||||
color: AppColors.muted,
|
||||
fontSize: AppDimens.body,
|
||||
height: 1.45,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 22),
|
||||
_AuthFields(state: state, callbacks: callbacks),
|
||||
if (status.errorMessage != null) ...[
|
||||
const SizedBox(height: 14),
|
||||
_AuthError(message: status.errorMessage!),
|
||||
],
|
||||
const SizedBox(height: 22),
|
||||
_AuthSubmitButton(state: state, onSubmit: callbacks.onSubmit),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_AuthFields(state: state, callbacks: callbacks),
|
||||
if (status.errorMessage != null) ...[
|
||||
const SizedBox(height: 14),
|
||||
_AuthError(message: status.errorMessage!),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 22),
|
||||
_AuthSubmitButton(state: state, onSubmit: callbacks.onSubmit),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -135,47 +128,23 @@ class _AuthModeSwitch extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextButton(
|
||||
return Row(
|
||||
key: const Key('auth-switch-mode'),
|
||||
onPressed: busy ? null : () => onSelected(!signUpMode),
|
||||
style: TextButton.styleFrom(
|
||||
minimumSize: const Size(0, _minTapTarget),
|
||||
padding: EdgeInsets.zero,
|
||||
overlayColor: AppColors.transparent,
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(22)),
|
||||
),
|
||||
child: Container(
|
||||
height: 36,
|
||||
padding: const EdgeInsets.all(2),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.raised,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
children: [
|
||||
_ModeTab(
|
||||
segmentKey: const Key('auth-mode-sign-in'),
|
||||
label: l10n.signIn,
|
||||
selected: !signUpMode,
|
||||
onTap: busy ? null : () => onSelected(false),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _ModeSegment(
|
||||
segmentKey: const Key('auth-mode-sign-in'),
|
||||
label: l10n.signIn,
|
||||
hiddenLabel: l10n.authSwitchToSignIn,
|
||||
selected: !signUpMode,
|
||||
onTap: busy ? null : () => onSelected(false),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Expanded(
|
||||
child: _ModeSegment(
|
||||
segmentKey: const Key('auth-mode-sign-up'),
|
||||
label: l10n.signUp,
|
||||
hiddenLabel: l10n.authSwitchToSignUp,
|
||||
selected: signUpMode,
|
||||
onTap: busy ? null : () => onSelected(true),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(width: AppDimens.followingSegmentGap),
|
||||
_ModeTab(
|
||||
segmentKey: const Key('auth-mode-sign-up'),
|
||||
label: l10n.signUp,
|
||||
selected: signUpMode,
|
||||
onTap: busy ? null : () => onSelected(true),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import '../../core/data/services/auth_controller.dart';
|
||||
import '../../core/platform/haptics.dart';
|
||||
import '../../core/platform/keyboard_inset.dart';
|
||||
import '../../core/theme/app_colors.dart';
|
||||
import '../../core/widgets/app_glow_panel.dart';
|
||||
import '../../core/widgets/centered_toast.dart';
|
||||
import '../../l10n/app_localizations.dart';
|
||||
|
||||
@@ -252,15 +253,15 @@ class _PhoneAuthSheetState extends State<PhoneAuthSheet> {
|
||||
final bottom = effectiveKeyboardInset(context, _keyboardInset);
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: bottom),
|
||||
child: Container(
|
||||
key: const Key('phone-auth-sheet'),
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.card,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(_sheetRadius),
|
||||
),
|
||||
child: AppGlowPanel(
|
||||
borderRadius: const BorderRadius.vertical(
|
||||
top: Radius.circular(_sheetRadius),
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
border: false,
|
||||
child: Container(
|
||||
key: const Key('phone-auth-sheet'),
|
||||
decoration: const BoxDecoration(color: AppColors.transparent),
|
||||
child: SingleChildScrollView(
|
||||
controller: _scrollController,
|
||||
padding: const EdgeInsets.fromLTRB(22, 10, 22, 26),
|
||||
child: Column(
|
||||
@@ -313,6 +314,7 @@ class _PhoneAuthSheetState extends State<PhoneAuthSheet> {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -403,7 +405,7 @@ class _PhoneAuthSheetState extends State<PhoneAuthSheet> {
|
||||
key: const Key('auth-phone-submit'),
|
||||
onPressed: _submitting ? null : _submit,
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: AppColors.blue,
|
||||
backgroundColor: AppColors.brandRed,
|
||||
foregroundColor: AppColors.onPrimary,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(_buttonHeight / 2),
|
||||
@@ -439,7 +441,7 @@ class _PhoneAuthSheetState extends State<PhoneAuthSheet> {
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(_fieldRadius),
|
||||
borderSide: const BorderSide(color: AppColors.blue, width: 1.5),
|
||||
borderSide: const BorderSide(color: AppColors.brandRed, width: 1.5),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import '../../core/platform/haptics.dart';
|
||||
import '../../core/theme/app_colors.dart';
|
||||
import '../../core/theme/app_dimens.dart';
|
||||
import '../../core/theme/app_theme.dart';
|
||||
import '../../core/widgets/app_glow_panel.dart';
|
||||
import '../../core/widgets/app_pull_to_refresh.dart';
|
||||
import '../../core/widgets/auth_gate.dart';
|
||||
import '../../core/widgets/empty_state.dart';
|
||||
@@ -181,15 +182,15 @@ class _DownloadsBody extends StatelessWidget {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: Text(l10n.clearDownloadsTitle),
|
||||
content: Text(l10n.clearDownloadsMessage),
|
||||
builder: (dialogContext) => AppGlowDialog(
|
||||
title: l10n.clearDownloadsTitle,
|
||||
content: l10n.clearDownloadsMessage,
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
FilledButton(
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(true),
|
||||
child: Text(l10n.confirmClear),
|
||||
),
|
||||
@@ -435,7 +436,7 @@ class _LicenseTile extends StatelessWidget {
|
||||
minHeight: AppDimens.progressHeight,
|
||||
value: progress.clamp(0.0, 1.0),
|
||||
backgroundColor: AppColors.border,
|
||||
valueColor: const AlwaysStoppedAnimation(AppColors.blueSoft),
|
||||
valueColor: const AlwaysStoppedAnimation(AppColors.brandRed),
|
||||
),
|
||||
),
|
||||
if (license.expireAt != null) ...[
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../core/data/models/interaction.dart';
|
||||
import '../../../core/widgets/app_glow_panel.dart';
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
|
||||
const int _maxDescriptionLength = 300;
|
||||
@@ -67,9 +68,9 @@ class _ReportDialogState extends State<_ReportDialog> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
return AlertDialog(
|
||||
title: Text(widget.title),
|
||||
content: SingleChildScrollView(
|
||||
return AppGlowDialog(
|
||||
title: widget.title,
|
||||
contentChild: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
@@ -13,6 +13,7 @@ import '../../core/formatting/localized_date_time.dart';
|
||||
import '../../core/theme/app_colors.dart';
|
||||
import '../../core/theme/app_dimens.dart';
|
||||
import '../../core/theme/app_theme.dart';
|
||||
import '../../core/widgets/app_glow_panel.dart';
|
||||
import '../../core/widgets/app_pull_to_refresh.dart';
|
||||
import '../../core/widgets/empty_state.dart';
|
||||
import '../../core/widgets/plain_page_scaffold.dart';
|
||||
@@ -191,9 +192,9 @@ class _NoticesPageState extends State<NoticesPage> {
|
||||
: formatLocalizedDateTime(context, notice.publishAt!);
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: Text(notice.title),
|
||||
content: SizedBox(
|
||||
builder: (dialogContext) => AppGlowDialog(
|
||||
title: notice.title,
|
||||
contentChild: SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
|
||||
@@ -14,6 +14,7 @@ import '../../../core/data/repositories/interaction_repository.dart';
|
||||
import '../../../core/data/services/comment_controller.dart';
|
||||
import '../../../core/formatting/error_text.dart';
|
||||
import '../../../core/theme/app_colors.dart';
|
||||
import '../../../core/widgets/app_glow_panel.dart';
|
||||
import '../../../core/widgets/empty_state.dart';
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
import 'comment_composer_sheet.dart';
|
||||
@@ -176,14 +177,14 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
|
||||
child: SizedBox(
|
||||
key: const Key('comment-sheet-constraints'),
|
||||
height: sheetHeight,
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.card,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(_sheetRadius),
|
||||
),
|
||||
child: AppGlowPanel(
|
||||
borderRadius: const BorderRadius.vertical(
|
||||
top: Radius.circular(_sheetRadius),
|
||||
),
|
||||
child: SafeArea(
|
||||
border: false,
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(color: AppColors.transparent),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: ListenableBuilder(
|
||||
listenable: _controller,
|
||||
@@ -193,6 +194,7 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
|
||||
child: _content(context),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -238,7 +240,7 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
|
||||
style: const TextStyle(
|
||||
color: AppColors.onSurface,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
|
||||
@@ -7,6 +7,7 @@ import '../../../core/data/models/drama.dart';
|
||||
import '../../../core/formatting/localized_content.dart';
|
||||
import '../../../core/theme/app_colors.dart';
|
||||
import '../../../core/theme/app_dimens.dart';
|
||||
import '../../../core/widgets/app_glow_panel.dart';
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
import 'episode_picker_cell.dart';
|
||||
|
||||
@@ -47,16 +48,16 @@ class EpisodePickerSheet extends StatelessWidget {
|
||||
systemNavigationBarIconBrightness: Brightness.light,
|
||||
systemNavigationBarContrastEnforced: false,
|
||||
),
|
||||
child: Container(
|
||||
key: const Key('player-episodes-sheet'),
|
||||
height: MediaQuery.sizeOf(context).height * _sheetHeightFactor,
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.card,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(_sheetRadius),
|
||||
),
|
||||
child: AppGlowPanel(
|
||||
borderRadius: const BorderRadius.vertical(
|
||||
top: Radius.circular(_sheetRadius),
|
||||
),
|
||||
child: SafeArea(
|
||||
border: false,
|
||||
child: Container(
|
||||
key: const Key('player-episodes-sheet'),
|
||||
height: MediaQuery.sizeOf(context).height * _sheetHeightFactor,
|
||||
decoration: const BoxDecoration(color: AppColors.transparent),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
bottom: true,
|
||||
child: Column(
|
||||
@@ -139,6 +140,7 @@ class EpisodePickerSheet extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../core/theme/app_colors.dart';
|
||||
import '../../../core/widgets/app_glow_panel.dart';
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
|
||||
const double _sheetRadius = 20;
|
||||
@@ -52,6 +53,8 @@ class PlayerMoreActions {
|
||||
required this.onDownload,
|
||||
required this.onQualityChanged,
|
||||
this.onPip,
|
||||
this.autoPipEnabled = false,
|
||||
this.onAutoPipChanged,
|
||||
});
|
||||
|
||||
/// 倍速变化。
|
||||
@@ -68,6 +71,12 @@ class PlayerMoreActions {
|
||||
|
||||
/// 进入小窗播放;平台不支持时为 null。
|
||||
final VoidCallback? onPip;
|
||||
|
||||
/// 退出 App 自动小窗开关的当前状态(仅支持自动小窗的平台展示开关行)。
|
||||
final bool autoPipEnabled;
|
||||
|
||||
/// 切换退出 App 自动小窗;为 null 时隐藏开关行。
|
||||
final ValueChanged<bool>? onAutoPipChanged;
|
||||
}
|
||||
|
||||
/// K29.6 精简版播放页更多面板。
|
||||
@@ -96,16 +105,19 @@ class PlayerMoreSheet extends StatefulWidget {
|
||||
class _PlayerMoreSheetState extends State<PlayerMoreSheet> {
|
||||
late double _speed = widget.state.speed;
|
||||
late String _quality = widget.state.selectedQuality.toUpperCase();
|
||||
late bool _autoPip = widget.actions.autoPipEnabled;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = widget.l10n;
|
||||
return Material(
|
||||
key: const Key('player-more-sheet'),
|
||||
color: AppColors.ink,
|
||||
return AppGlowPanel(
|
||||
borderRadius: const BorderRadius.vertical(
|
||||
top: Radius.circular(_sheetRadius),
|
||||
),
|
||||
border: false,
|
||||
child: Material(
|
||||
key: const Key('player-more-sheet'),
|
||||
type: MaterialType.transparency,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
@@ -142,6 +154,17 @@ class _PlayerMoreSheetState extends State<PlayerMoreSheet> {
|
||||
title: l10n.pictureInPicture,
|
||||
onTap: () => _closeThen(widget.actions.onPip!),
|
||||
),
|
||||
if (widget.actions.onAutoPipChanged != null)
|
||||
_switchRow(
|
||||
key: const Key('player-more-auto-pip'),
|
||||
icon: Icons.auto_awesome_motion_outlined,
|
||||
title: l10n.autoEnterPip,
|
||||
value: _autoPip,
|
||||
onChanged: (value) {
|
||||
setState(() => _autoPip = value);
|
||||
widget.actions.onAutoPipChanged!(value);
|
||||
},
|
||||
),
|
||||
_actionRow(
|
||||
key: const Key('player-more-offline'),
|
||||
icon: Icons.download_outlined,
|
||||
@@ -153,6 +176,7 @@ class _PlayerMoreSheetState extends State<PlayerMoreSheet> {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -160,7 +184,7 @@ class _PlayerMoreSheetState extends State<PlayerMoreSheet> {
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(vertical: _groupVerticalPadding),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.card,
|
||||
color: Colors.white.withValues(alpha: 0.06),
|
||||
borderRadius: BorderRadius.circular(_groupRadius),
|
||||
),
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: rows),
|
||||
@@ -241,7 +265,7 @@ class _PlayerMoreSheetState extends State<PlayerMoreSheet> {
|
||||
horizontal: _segmentTrackPadding,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.raised,
|
||||
color: Colors.white.withValues(alpha: 0.08),
|
||||
borderRadius: BorderRadius.circular(_segmentTrackRadius),
|
||||
),
|
||||
child: Row(
|
||||
@@ -286,6 +310,54 @@ class _PlayerMoreSheetState extends State<PlayerMoreSheet> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 开关行:左侧图标+标题,右侧主题色 Switch。
|
||||
Widget _switchRow({
|
||||
required Key key,
|
||||
required IconData icon,
|
||||
required String title,
|
||||
required bool value,
|
||||
required ValueChanged<bool> onChanged,
|
||||
}) {
|
||||
return SizedBox(
|
||||
key: key,
|
||||
height: _rowHeight,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: _rowHorizontalPadding),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, color: AppColors.onSurface, size: _rowIconSize),
|
||||
const SizedBox(width: _rowIconGap),
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
color: AppColors.onSurface,
|
||||
fontSize: _rowLabelSize,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
Transform.scale(
|
||||
scale: 0.7,
|
||||
child: Switch(
|
||||
key: const Key('player-more-auto-pip-switch'),
|
||||
value: value,
|
||||
activeThumbColor: AppColors.onSurface,
|
||||
activeTrackColor: AppColors.brandRed,
|
||||
inactiveThumbColor: AppColors.textSecondary,
|
||||
inactiveTrackColor: AppColors.contentBorder,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
onChanged: onChanged,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _actionRow({
|
||||
required Key key,
|
||||
required IconData icon,
|
||||
@@ -380,7 +452,9 @@ class _SegmentButton extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: _segmentPadding),
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: selected ? AppColors.card : AppColors.transparent,
|
||||
color: selected
|
||||
? Colors.white.withValues(alpha: 0.16)
|
||||
: AppColors.transparent,
|
||||
borderRadius: BorderRadius.circular(_segmentRadius),
|
||||
boxShadow: selected
|
||||
? const [
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../core/theme/app_colors.dart';
|
||||
import '../../../core/widgets/app_glow_panel.dart';
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
|
||||
const List<double> _speedOptions = [0.75, 1.0, 1.25, 1.5, 2.0, 3.0];
|
||||
@@ -19,14 +20,16 @@ class PlayerSpeedSheet extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
return Material(
|
||||
key: const Key('player-speed-sheet'),
|
||||
color: AppColors.card,
|
||||
return AppGlowPanel(
|
||||
borderRadius: const BorderRadius.vertical(
|
||||
top: Radius.circular(_sheetRadius),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: SafeArea(
|
||||
border: false,
|
||||
child: Material(
|
||||
key: const Key('player-speed-sheet'),
|
||||
type: MaterialType.transparency,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -50,6 +53,7 @@ class PlayerSpeedSheet extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import '../../../core/data/models/drama.dart';
|
||||
import '../../../core/data/models/play_ticket.dart';
|
||||
import '../../../core/formatting/error_text.dart';
|
||||
import '../../../core/theme/app_colors.dart';
|
||||
import '../../../core/widgets/app_glow_panel.dart';
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
|
||||
const double _cardRadius = 20;
|
||||
@@ -209,12 +210,10 @@ class _PlayerUnlockSheetState extends State<PlayerUnlockSheet> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = widget.l10n;
|
||||
return Container(
|
||||
return AppGlowPanel(
|
||||
key: const Key('player-unlock-panel'),
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.card,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(_cardRadius)),
|
||||
),
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(_cardRadius)),
|
||||
border: false,
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: Padding(
|
||||
|
||||
@@ -14,6 +14,7 @@ import '../../core/formatting/error_text.dart';
|
||||
import '../../core/platform/haptics.dart';
|
||||
import '../../core/theme/app_colors.dart';
|
||||
import '../../core/theme/app_theme.dart';
|
||||
import '../../core/widgets/app_glow_panel.dart';
|
||||
import '../../core/widgets/app_pull_to_refresh.dart';
|
||||
import '../../core/widgets/empty_state.dart';
|
||||
import '../../core/widgets/paged_list_footer.dart';
|
||||
@@ -255,15 +256,15 @@ class _WatchRecordsBodyState extends State<_WatchRecordsBody> {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: Text(l10n.clearWatchHistoryTitle),
|
||||
content: Text(l10n.clearWatchHistoryMessage),
|
||||
builder: (dialogContext) => AppGlowDialog(
|
||||
title: l10n.clearWatchHistoryTitle,
|
||||
content: l10n.clearWatchHistoryMessage,
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
FilledButton(
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(true),
|
||||
child: Text(l10n.confirmClear),
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user