feat(mobile): redesign guide pages with fullscreen background images

向导页重新设计:

1. 页面1-4 改为全屏背景图片布局
   - 移除 SafeArea,图片延伸到状态栏
   - 移除文字标题和副标题(文字设计在图片中)
   - 仅保留底部页面指示器(白色圆点)

2. 页面5(欢迎加入页)也改为全屏背景图片
   - 添加渐变遮罩(30%~60%黑色)提高可读性
   - 表单区域改为透明背景
   - 所有文字、输入框、单选按钮改为白色系
   - 退出按钮文字从"退出 Exit"改为"退出"

3. 添加5张向导页背景图片
   - guide_1.jpg ~ guide_4.jpg: 介绍页背景
   - guide_5.png: 欢迎加入页背景

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2025-12-09 17:44:38 -08:00
parent 53320df220
commit 07448b381b
6 changed files with 223 additions and 171 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

View File

@ -38,28 +38,34 @@ class _GuidePageState extends ConsumerState<GuidePage> {
final PageController _pageController = PageController(); final PageController _pageController = PageController();
int _currentPage = 0; int _currentPage = 0;
// 1-4 // 1-5 (5)
// pngjpgwebp
final List<GuidePageData> _guidePages = const [ final List<GuidePageData> _guidePages = const [
GuidePageData( GuidePageData(
imagePath: 'assets/images/guide_1.png', imagePath: 'assets/images/guide_1.jpg',
title: '认种一棵榴莲树\n拥有真实RWA资产', title: '认种一棵榴莲树\n拥有真实RWA资产',
subtitle: '绑定真实果园20年收益让区块链与农业完美结合', subtitle: '绑定真实果园20年收益让区块链与农业完美结合',
), ),
GuidePageData( GuidePageData(
imagePath: 'assets/images/guide_2.png', imagePath: 'assets/images/guide_2.jpg',
title: '认种即可开启算力\n自动挖矿持续收益', title: '认种即可开启算力\n自动挖矿持续收益',
subtitle: '每一棵树都对应真实资产注入,为算力提供真实价值支撑', subtitle: '每一棵树都对应真实资产注入,为算力提供真实价值支撑',
), ),
GuidePageData( GuidePageData(
imagePath: 'assets/images/guide_3.png', imagePath: 'assets/images/guide_3.jpg',
title: '分享链接\n获得团队算力与收益', title: '分享链接\n获得团队算力与收益',
subtitle: '真实认种数据透明可信 · 团队越大算力越强', subtitle: '真实认种数据透明可信 · 团队越大算力越强',
), ),
GuidePageData( GuidePageData(
imagePath: 'assets/images/guide_4.png', imagePath: 'assets/images/guide_4.jpg',
title: 'MPC多方安全\n所有地址与收益可审计', title: 'MPC多方安全\n所有地址与收益可审计',
subtitle: '你的资产 · 安全透明 · 不可被篡改', subtitle: '你的资产 · 安全透明 · 不可被篡改',
), ),
GuidePageData(
imagePath: 'assets/images/guide_5.png',
title: '欢迎加入',
subtitle: '创建账号前的最后一步 · 请选择是否有推荐人',
),
]; ];
@override @override
@ -99,85 +105,53 @@ class _GuidePageState extends ConsumerState<GuidePage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: Colors.white, backgroundColor: Colors.white,
body: SafeArea( body: PageView.builder(
child: PageView.builder( controller: _pageController,
controller: _pageController, onPageChanged: _onPageChanged,
onPageChanged: _onPageChanged, itemCount: 5, // 4 + 1
itemCount: 5, // 4 + 1 itemBuilder: (context, index) {
itemBuilder: (context, index) { if (index < 4) {
if (index < 4) { return _buildGuidePage(_guidePages[index], index);
return _buildGuidePage(_guidePages[index], index); } else {
} else { return _buildWelcomePage();
return _buildWelcomePage(); }
} },
},
),
), ),
); );
} }
/// (1-4) /// (1-4) -
Widget _buildGuidePage(GuidePageData data, int index) { Widget _buildGuidePage(GuidePageData data, int index) {
return Padding( return Stack(
padding: EdgeInsets.symmetric(horizontal: 24.w), fit: StackFit.expand,
child: Column( children: [
children: [ //
SizedBox(height: 64.h), if (data.imagePath != null)
// Image.asset(
Expanded( data.imagePath!,
flex: 5, fit: BoxFit.cover,
child: Container( width: double.infinity,
width: 312.w, height: double.infinity,
decoration: BoxDecoration( errorBuilder: (context, error, stackTrace) {
borderRadius: BorderRadius.circular(12.r), return Container(
color: const Color(0xFFFFF8E7), color: const Color(0xFFFFF8E7),
), child: _buildPlaceholderImage(index),
child: data.imagePath != null );
? ClipRRect( },
borderRadius: BorderRadius.circular(12.r), )
child: Image.asset( else
data.imagePath!, Container(
fit: BoxFit.cover, color: const Color(0xFFFFF8E7),
errorBuilder: (context, error, stackTrace) { child: _buildPlaceholderImage(index),
return _buildPlaceholderImage(index);
},
),
)
: _buildPlaceholderImage(index),
),
), ),
SizedBox(height: 48.h), //
// Positioned(
Text( left: 0,
data.title, right: 0,
style: TextStyle( bottom: 80.h,
fontSize: 24.sp, child: _buildPageIndicator(),
fontWeight: FontWeight.w700, ),
height: 1.33, ],
color: const Color(0xFF292524),
),
textAlign: TextAlign.center,
),
SizedBox(height: 16.h),
//
Padding(
padding: EdgeInsets.symmetric(horizontal: 24.w),
child: Text(
data.subtitle,
style: TextStyle(
fontSize: 14.sp,
height: 1.43,
color: const Color(0xFF57534E),
),
textAlign: TextAlign.center,
),
),
SizedBox(height: 48.h),
//
_buildPageIndicator(),
SizedBox(height: 80.h),
],
),
); );
} }
@ -223,10 +197,12 @@ class _GuidePageState extends ConsumerState<GuidePage> {
return _WelcomePageContent( return _WelcomePageContent(
onNext: _goToOnboarding, onNext: _goToOnboarding,
onExit: _exitApp, // 退 onExit: _exitApp, // 退
backgroundImage: _guidePages[4].imagePath,
pageIndicator: _buildPageIndicator(),
); );
} }
/// /// (使)
Widget _buildPageIndicator() { Widget _buildPageIndicator() {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@ -239,8 +215,8 @@ class _GuidePageState extends ConsumerState<GuidePage> {
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
color: isActive color: isActive
? const Color(0xFF8E794A) ? Colors.white
: const Color(0xFFEAE0CD), : Colors.white.withValues(alpha: 0.4),
), ),
); );
}), }),
@ -252,10 +228,14 @@ class _GuidePageState extends ConsumerState<GuidePage> {
class _WelcomePageContent extends ConsumerStatefulWidget { class _WelcomePageContent extends ConsumerStatefulWidget {
final VoidCallback onNext; final VoidCallback onNext;
final VoidCallback onExit; final VoidCallback onExit;
final String? backgroundImage;
final Widget pageIndicator;
const _WelcomePageContent({ const _WelcomePageContent({
required this.onNext, required this.onNext,
required this.onExit, required this.onExit,
this.backgroundImage,
required this.pageIndicator,
}); });
@override @override
@ -405,93 +385,152 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(), onTap: () => FocusScope.of(context).unfocus(),
child: LayoutBuilder( child: Stack(
builder: (context, constraints) { fit: StackFit.expand,
return SingleChildScrollView( children: [
child: ConstrainedBox( //
constraints: BoxConstraints( if (widget.backgroundImage != null)
minHeight: constraints.maxHeight, Image.asset(
), widget.backgroundImage!,
child: IntrinsicHeight( fit: BoxFit.cover,
child: Padding( width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 24.w), height: double.infinity,
child: Column( errorBuilder: (context, error, stackTrace) {
children: [ return Container(
// 退 color: const Color(0xFFFFF8E7),
Align( );
alignment: Alignment.topRight, },
child: Padding( )
padding: EdgeInsets.only(top: 32.h), else
child: GestureDetector( Container(
onTap: widget.onExit, color: const Color(0xFFFFF8E7),
child: Text( ),
'退出 Exit', //
style: TextStyle( Container(
fontSize: 14.sp, decoration: BoxDecoration(
color: const Color(0xFFA99F93), gradient: LinearGradient(
), begin: Alignment.topCenter,
), end: Alignment.bottomCenter,
), colors: [
), Colors.black.withValues(alpha: 0.3),
), Colors.black.withValues(alpha: 0.6),
SizedBox(height: 80.h), ],
//
Text(
'欢迎加入',
style: TextStyle(
fontSize: 24.sp,
fontWeight: FontWeight.w700,
height: 1.33,
color: const Color(0xFF6F6354),
),
),
SizedBox(height: 12.h),
//
Text(
'创建账号前的最后一步 · 请选择是否有推荐人',
style: TextStyle(
fontSize: 14.sp,
height: 1.43,
color: const Color(0xFFA99F93),
),
),
SizedBox(height: 48.h),
//
_buildReferrerOptions(),
const Spacer(),
//
GestureDetector(
onTap: _canProceed ? _saveReferralCodeAndProceed : null,
child: Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 16.h),
child: Text(
'下一步 (创建账号)',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
height: 1.5,
color: _canProceed
? const Color(0xFFD4A84B) // -
: const Color(0xFFCCC5B9), // -
),
textAlign: TextAlign.right,
),
),
),
SizedBox(height: 48.h),
],
),
),
), ),
), ),
); ),
}, //
SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.w),
child: Column(
children: [
// 退
Align(
alignment: Alignment.topRight,
child: Padding(
padding: EdgeInsets.only(top: 16.h),
child: GestureDetector(
onTap: widget.onExit,
child: Text(
'退出',
style: TextStyle(
fontSize: 14.sp,
color: Colors.white.withValues(alpha: 0.8),
),
),
),
),
),
SizedBox(height: 60.h),
//
Text(
'欢迎加入',
style: TextStyle(
fontSize: 28.sp,
fontWeight: FontWeight.w700,
height: 1.33,
color: Colors.white,
shadows: [
Shadow(
color: Colors.black.withValues(alpha: 0.5),
blurRadius: 4,
),
],
),
),
SizedBox(height: 12.h),
//
Text(
'创建账号前的最后一步 · 请选择是否有推荐人',
style: TextStyle(
fontSize: 14.sp,
height: 1.43,
color: Colors.white.withValues(alpha: 0.9),
shadows: [
Shadow(
color: Colors.black.withValues(alpha: 0.5),
blurRadius: 4,
),
],
),
),
SizedBox(height: 48.h),
// ()
_buildReferrerOptions(),
const Spacer(),
//
widget.pageIndicator,
SizedBox(height: 24.h),
//
GestureDetector(
onTap: _canProceed ? _saveReferralCodeAndProceed : null,
child: Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 16.h),
decoration: BoxDecoration(
color: _canProceed
? const Color(0xFFD4A84B)
: Colors.white.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(12.r),
),
child: Text(
'下一步 (创建账号)',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
height: 1.5,
color: _canProceed
? Colors.white
: Colors.white.withValues(alpha: 0.6),
),
textAlign: TextAlign.center,
),
),
),
SizedBox(height: 32.h),
],
),
),
),
),
);
},
),
),
],
), ),
); );
} }
/// /// ()
Widget _buildReferrerOptions() { Widget _buildReferrerOptions() {
return Column( return Column(
children: [ children: [
@ -511,7 +550,13 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
height: 1.5, height: 1.5,
color: const Color(0xFF6F6354), color: Colors.white,
shadows: [
Shadow(
color: Colors.black.withValues(alpha: 0.5),
blurRadius: 4,
),
],
), ),
), ),
], ],
@ -522,11 +567,11 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
if (_hasReferrer) if (_hasReferrer)
Container( Container(
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 4.w), padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 4.w),
decoration: const BoxDecoration( decoration: BoxDecoration(
border: Border( border: Border(
bottom: BorderSide( bottom: BorderSide(
width: 1, width: 1,
color: Color(0xFFEAE1D2), color: Colors.white.withValues(alpha: 0.5),
), ),
), ),
), ),
@ -539,7 +584,7 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
hintText: '请输入推荐码 / 序列号', hintText: '请输入推荐码 / 序列号',
hintStyle: TextStyle( hintStyle: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
color: const Color(0xFFA99F93), color: Colors.white.withValues(alpha: 0.6),
), ),
border: InputBorder.none, border: InputBorder.none,
isDense: true, isDense: true,
@ -547,8 +592,9 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
), ),
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
color: const Color(0xFF6F6354), color: Colors.white,
), ),
cursorColor: Colors.white,
), ),
), ),
// //
@ -559,7 +605,7 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
child: Icon( child: Icon(
Icons.camera_alt_outlined, Icons.camera_alt_outlined,
size: 20.sp, size: 20.sp,
color: const Color(0xFFA99F93), color: Colors.white.withValues(alpha: 0.8),
), ),
), ),
), ),
@ -583,7 +629,13 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
height: 1.5, height: 1.5,
color: const Color(0xFF6F6354), color: Colors.white,
shadows: [
Shadow(
color: Colors.black.withValues(alpha: 0.5),
blurRadius: 4,
),
],
), ),
), ),
], ],
@ -593,7 +645,7 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
); );
} }
/// /// ()
Widget _buildRadio(bool isSelected) { Widget _buildRadio(bool isSelected) {
return Container( return Container(
width: 20.w, width: 20.w,
@ -603,8 +655,8 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
border: Border.all( border: Border.all(
width: isSelected ? 6.w : 2.w, width: isSelected ? 6.w : 2.w,
color: isSelected color: isSelected
? const Color(0xFF2563EB) ? Colors.white
: const Color(0xFFA99F93), : Colors.white.withValues(alpha: 0.5),
), ),
), ),
); );