Commit 086e13f15b83e1d0bf9d367fed5c45cf66c63f13

Authored by 刘宏哲
1 parent 7ee5ba32

feat(app): update ui

... ... @@ -73,7 +73,7 @@ class AppReviewPromptLogic {
final result = await showDialog<AppReviewPromptResult>(
context: context,
barrierColor: Colors.black.withValues(alpha: 0.7),
barrierDismissible: false,
barrierDismissible: true,
builder: (_) => const AppReviewFeedbackDialog(),
);
... ...
... ... @@ -81,98 +81,95 @@ class _ReviewPromptCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isFeedback = height > 400;
return PopScope(
canPop: false,
child: Align(
alignment: _dialogAlignment,
child: Material(
color: Colors.transparent,
child: Container(
width: _cardWidth,
height: height,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFE6DBFF),
Color(0xFFEDE4FF),
Color(0xFFEEE6FF),
Color(0xFFF9F6FF),
],
stops: [0, 0.25, 0.75, 1],
),
return Align(
alignment: _dialogAlignment,
child: Material(
color: Colors.transparent,
child: Container(
width: _cardWidth,
height: height,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFE6DBFF),
Color(0xFFEDE4FF),
Color(0xFFEEE6FF),
Color(0xFFF9F6FF),
],
stops: [0, 0.25, 0.75, 1],
),
child: Stack(
children: [
Positioned(
top: 0,
left: 0,
right: 0,
height: _illustrationHeight,
child: Image.asset(
illustrationAsset,
fit: BoxFit.cover,
),
),
Positioned(
top: isFeedback ? 162 : 163,
left: 38,
right: 38,
child: Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(
color: _h1,
fontSize: 18,
height: 1.35,
fontWeight: FontWeight.w600,
),
),
),
Positioned(
top: isFeedback ? 222 : 195,
left: 20,
right: 20,
child: Text(
message,
textAlign: TextAlign.center,
style: const TextStyle(
color: _h2,
fontSize: 14,
height: 1.35,
fontWeight: FontWeight.w400,
),
),
),
child: Stack(
children: [
Positioned(
top: 0,
left: 0,
right: 0,
height: _illustrationHeight,
child: Image.asset(
illustrationAsset,
fit: BoxFit.cover,
),
Positioned(
top: isFeedback ? 305 : 260,
left: 40,
right: 40,
child: _PromptButton(
emoji: primaryEmoji,
text: primaryText,
textColor: Colors.white,
),
Positioned(
top: isFeedback ? 162 : 163,
left: 38,
right: 38,
child: Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(
color: _h1,
fontSize: 18,
height: 1.35,
fontWeight: FontWeight.w600,
backgroundColor: _brandColor,
onTap: onPrimaryTap,
),
),
Positioned(
top: isFeedback ? 357 : 312,
left: 40,
right: 40,
child: _PromptButton(
text: secondaryText,
textColor: _h2,
),
Positioned(
top: isFeedback ? 222 : 195,
left: 20,
right: 20,
child: Text(
message,
textAlign: TextAlign.center,
style: const TextStyle(
color: _h2,
fontSize: 14,
height: 1.35,
fontWeight: FontWeight.w400,
onTap: onSecondaryTap,
),
),
],
),
),
Positioned(
top: isFeedback ? 305 : 260,
left: 40,
right: 40,
child: _PromptButton(
emoji: primaryEmoji,
text: primaryText,
textColor: Colors.white,
fontWeight: FontWeight.w600,
backgroundColor: _brandColor,
onTap: onPrimaryTap,
),
),
Positioned(
top: isFeedback ? 357 : 312,
left: 40,
right: 40,
child: _PromptButton(
text: secondaryText,
textColor: _h2,
fontWeight: FontWeight.w400,
onTap: onSecondaryTap,
),
),
],
),
),
),
... ...
... ... @@ -347,7 +347,7 @@ class _FriendsHeader extends StatelessWidget {
const Spacer(),
IconButton(
onPressed: () {
_navigatorToPrivacySettings();
_navigatorToPrivacySettings(context);
},
icon: Image.asset(
R.assetsImagesSettings,
... ... @@ -364,7 +364,7 @@ class _FriendsHeader extends StatelessWidget {
}
}
_navigatorToPrivacySettings() {
_navigatorToPrivacySettings(BuildContext context) {
Get.toNamed(Routes.PRIVACY_SETTINGS);
}
... ...
... ... @@ -419,6 +419,57 @@ class _LegalText extends StatelessWidget {
final String? linkSuffix;
final VoidCallback? onLinkTap;
static const _baseStyle = TextStyle(
color: PurchaseColors.subtitle,
fontSize: 12,
height: 1.4,
);
static const _linkStyle = TextStyle(
color: Color(0xFF845EEE),
fontWeight: FontWeight.w500,
decoration: TextDecoration.underline,
decorationColor: Color(0xFF845EEE),
);
TextSpan _buildTextSpan() => TextSpan(
style: _baseStyle,
children: [
TextSpan(text: text),
if (linkText case final linkText?)
TextSpan(text: linkText, style: _linkStyle),
if (linkSuffix case final linkSuffix?) TextSpan(text: linkSuffix),
],
);
void _handleTapUp(
BuildContext context,
BoxConstraints constraints,
TapUpDetails details,
) {
final linkText = this.linkText;
if (linkText == null || onLinkTap == null) return;
final painter = TextPainter(
text: _buildTextSpan(),
textDirection: Directionality.of(context),
textScaler: MediaQuery.textScalerOf(context),
)..layout(maxWidth: constraints.maxWidth);
final linkStart = text.length;
final linkBoxes = painter.getBoxesForSelection(
TextSelection(
baseOffset: linkStart,
extentOffset: linkStart + linkText.length,
),
);
if (linkBoxes.any(
(box) => box.toRect().inflate(10).contains(details.localPosition),
)) {
onLinkTap!.call();
}
}
@override
Widget build(BuildContext context) {
return Row(
... ... @@ -433,38 +484,16 @@ class _LegalText extends StatelessWidget {
),
),
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(text: text),
if (linkText case final linkText?)
WidgetSpan(
alignment: PlaceholderAlignment.baseline,
baseline: TextBaseline.alphabetic,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onLinkTap,
child: Text(
linkText,
style: const TextStyle(
color: Color(0xFF845EEE),
fontSize: 12,
height: 1.4,
fontWeight: FontWeight.w500,
decoration: TextDecoration.underline,
decorationColor: Color(0xFF845EEE),
),
),
),
),
if (linkSuffix case final linkSuffix?)
TextSpan(text: linkSuffix),
],
),
style: const TextStyle(
color: PurchaseColors.subtitle,
fontSize: 12,
height: 1.4,
child: LayoutBuilder(
builder: (context, constraints) => GestureDetector(
behavior: HitTestBehavior.opaque,
onTapUp: linkText == null || onLinkTap == null
? null
: (details) => _handleTapUp(context, constraints, details),
child: Text.rich(
_buildTextSpan(),
textScaler: MediaQuery.textScalerOf(context),
),
),
),
),
... ...
... ... @@ -38,13 +38,15 @@ class PurchaseView extends GetView<PurchaseController> {
child: SingleChildScrollView(
controller: controller.scrollController,
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.only(bottom: 170),
padding: EdgeInsets.only(
bottom: 170 + MediaQuery.paddingOf(context).bottom,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const _HeroHeader(),
Padding(
padding: EdgeInsets.only(left: 18, top: 9.h),
padding: EdgeInsets.only(left: 18, top: 9.h),
child: Text(
context.l10n.purchaseHeroTitle,
style: const TextStyle(
... ... @@ -299,6 +301,57 @@ class _LegalText extends StatelessWidget {
final String? linkSuffix;
final VoidCallback? onLinkTap;
static const _baseStyle = TextStyle(
color: PurchaseColors.subtitle,
fontSize: 12,
height: 1.4,
);
static const _linkStyle = TextStyle(
color: Color(0xFF845EEE),
fontWeight: FontWeight.w500,
decoration: TextDecoration.underline,
decorationColor: Color(0xFF845EEE),
);
TextSpan _buildTextSpan() => TextSpan(
style: _baseStyle,
children: [
TextSpan(text: text),
if (linkText case final linkText?)
TextSpan(text: linkText, style: _linkStyle),
if (linkSuffix case final linkSuffix?) TextSpan(text: linkSuffix),
],
);
void _handleTapUp(
BuildContext context,
BoxConstraints constraints,
TapUpDetails details,
) {
final linkText = this.linkText;
if (linkText == null || onLinkTap == null) return;
final painter = TextPainter(
text: _buildTextSpan(),
textDirection: Directionality.of(context),
textScaler: MediaQuery.textScalerOf(context),
)..layout(maxWidth: constraints.maxWidth);
final linkStart = text.length;
final linkBoxes = painter.getBoxesForSelection(
TextSelection(
baseOffset: linkStart,
extentOffset: linkStart + linkText.length,
),
);
if (linkBoxes.any(
(box) => box.toRect().inflate(10).contains(details.localPosition),
)) {
onLinkTap!.call();
}
}
@override
Widget build(BuildContext context) {
return Row(
... ... @@ -313,38 +366,16 @@ class _LegalText extends StatelessWidget {
),
),
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(text: text),
if (linkText case final linkText?)
WidgetSpan(
alignment: PlaceholderAlignment.baseline,
baseline: TextBaseline.alphabetic,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onLinkTap,
child: Text(
linkText,
style: const TextStyle(
color: Color(0xFF845EEE),
fontSize: 12,
height: 1.4,
fontWeight: FontWeight.w500,
decoration: TextDecoration.underline,
decorationColor: Color(0xFF845EEE),
),
),
),
),
if (linkSuffix case final linkSuffix?)
TextSpan(text: linkSuffix),
],
),
style: const TextStyle(
color: PurchaseColors.subtitle,
fontSize: 12,
height: 1.4,
child: LayoutBuilder(
builder: (context, constraints) => GestureDetector(
behavior: HitTestBehavior.opaque,
onTapUp: linkText == null || onLinkTap == null
? null
: (details) => _handleTapUp(context, constraints, details),
child: Text.rich(
_buildTextSpan(),
textScaler: MediaQuery.textScalerOf(context),
),
),
),
),
... ...
... ... @@ -26,79 +26,91 @@ class PurchaseBottomBar extends StatelessWidget {
final bottomInset = MediaQuery.paddingOf(context).bottom;
final buttonWidth = math.min(280.0, MediaQuery.sizeOf(context).width - 74);
return IgnorePointer(
ignoring: loading,
child: Container(
height: 147 + bottomInset,
padding: EdgeInsets.only(bottom: bottomInset),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.center,
colors: [Color(0x00F5F2FF), PurchaseColors.background],
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
width: buttonWidth,
height: 48,
child: ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
elevation: 0,
shadowColor: Colors.transparent,
backgroundColor: PurchaseColors.proYellow,
foregroundColor: PurchaseColors.title,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
textStyle: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
letterSpacing: 0,
return SizedBox(
height: 147 + bottomInset,
child: Stack(
children: [
const Positioned.fill(
child: IgnorePointer(
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.center,
colors: [Color(0x00F5F2FF), PurchaseColors.background],
),
),
child: loading
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
color: PurchaseColors.title,
),
)
: Text(label),
),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_BottomLink(
label: context.l10n.purchaseTermsOfService,
onTap: onTermsTap,
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 8),
child: Text(
'|',
style: TextStyle(
color: PurchaseColors.subtitle,
fontSize: 12,
),
IgnorePointer(
ignoring: loading,
child: Padding(
padding: EdgeInsets.only(bottom: bottomInset),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
width: buttonWidth,
height: 48,
child: ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
elevation: 0,
shadowColor: Colors.transparent,
backgroundColor: PurchaseColors.proYellow,
foregroundColor: PurchaseColors.title,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
textStyle: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
letterSpacing: 0,
),
),
child: loading
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
color: PurchaseColors.title,
),
)
: Text(label),
),
),
),
_BottomLink(
label: context.l10n.purchasePrivacyPolicy,
onTap: onPrivacyTap,
),
],
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_BottomLink(
label: context.l10n.purchaseTermsOfService,
onTap: onTermsTap,
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 8),
child: Text(
'|',
style: TextStyle(
color: PurchaseColors.subtitle,
fontSize: 12,
),
),
),
_BottomLink(
label: context.l10n.purchasePrivacyPolicy,
onTap: onPrivacyTap,
),
],
),
const SizedBox(height: 18),
],
),
),
const SizedBox(height: 18),
],
),
),
],
),
);
}
... ...