443 lines
12 KiB
Dart
443 lines
12 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||
import 'package:go_router/go_router.dart';
|
||
import '../../../../routes/route_paths.dart';
|
||
import '../providers/auth_provider.dart';
|
||
|
||
/// 向导页数据模型
|
||
class GuidePageData {
|
||
final String? imagePath;
|
||
final String title;
|
||
final String subtitle;
|
||
final Widget? customContent;
|
||
|
||
const GuidePageData({
|
||
this.imagePath,
|
||
required this.title,
|
||
required this.subtitle,
|
||
this.customContent,
|
||
});
|
||
}
|
||
|
||
/// 向导页面 - 用户首次打开应用时展示
|
||
/// 支持左右滑动切换页面
|
||
class GuidePage extends ConsumerStatefulWidget {
|
||
const GuidePage({super.key});
|
||
|
||
@override
|
||
ConsumerState<GuidePage> createState() => _GuidePageState();
|
||
}
|
||
|
||
class _GuidePageState extends ConsumerState<GuidePage> {
|
||
final PageController _pageController = PageController();
|
||
int _currentPage = 0;
|
||
|
||
// 向导页1-4的数据
|
||
final List<GuidePageData> _guidePages = const [
|
||
GuidePageData(
|
||
imagePath: 'assets/images/guide_1.png',
|
||
title: '认种一棵榴莲树\n拥有真实RWA资产',
|
||
subtitle: '绑定真实果园20年收益,让区块链与农业完美结合',
|
||
),
|
||
GuidePageData(
|
||
imagePath: 'assets/images/guide_2.png',
|
||
title: '认种即可开启算力\n自动挖矿持续收益',
|
||
subtitle: '每一棵树都对应真实资产注入,为算力提供真实价值支撑',
|
||
),
|
||
GuidePageData(
|
||
imagePath: 'assets/images/guide_3.png',
|
||
title: '分享链接\n获得团队算力与收益',
|
||
subtitle: '真实认种数据透明可信 · 团队越大算力越强',
|
||
),
|
||
GuidePageData(
|
||
imagePath: 'assets/images/guide_4.png',
|
||
title: 'MPC多方安全\n所有地址与收益可审计',
|
||
subtitle: '你的资产 · 安全透明 · 不可被篡改',
|
||
),
|
||
];
|
||
|
||
@override
|
||
void dispose() {
|
||
_pageController.dispose();
|
||
super.dispose();
|
||
}
|
||
|
||
void _onPageChanged(int page) {
|
||
setState(() {
|
||
_currentPage = page;
|
||
});
|
||
}
|
||
|
||
void _goToNextPage() {
|
||
if (_currentPage < 4) {
|
||
_pageController.nextPage(
|
||
duration: const Duration(milliseconds: 300),
|
||
curve: Curves.easeInOut,
|
||
);
|
||
}
|
||
}
|
||
|
||
void _goToOnboarding() async {
|
||
// 标记已查看向导页
|
||
await ref.read(authProvider.notifier).markGuideAsSeen();
|
||
if (!mounted) return;
|
||
context.go(RoutePaths.onboarding);
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
backgroundColor: Colors.white,
|
||
body: SafeArea(
|
||
child: PageView.builder(
|
||
controller: _pageController,
|
||
onPageChanged: _onPageChanged,
|
||
itemCount: 5, // 4个介绍页 + 1个欢迎加入页
|
||
itemBuilder: (context, index) {
|
||
if (index < 4) {
|
||
return _buildGuidePage(_guidePages[index], index);
|
||
} else {
|
||
return _buildWelcomePage();
|
||
}
|
||
},
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建向导页 (页面1-4)
|
||
Widget _buildGuidePage(GuidePageData data, int index) {
|
||
return Padding(
|
||
padding: EdgeInsets.symmetric(horizontal: 24.w),
|
||
child: Column(
|
||
children: [
|
||
SizedBox(height: 64.h),
|
||
// 图片区域
|
||
Expanded(
|
||
flex: 5,
|
||
child: Container(
|
||
width: 312.w,
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.circular(12.r),
|
||
color: const Color(0xFFFFF8E7),
|
||
),
|
||
child: data.imagePath != null
|
||
? ClipRRect(
|
||
borderRadius: BorderRadius.circular(12.r),
|
||
child: Image.asset(
|
||
data.imagePath!,
|
||
fit: BoxFit.cover,
|
||
errorBuilder: (context, error, stackTrace) {
|
||
return _buildPlaceholderImage(index);
|
||
},
|
||
),
|
||
)
|
||
: _buildPlaceholderImage(index),
|
||
),
|
||
),
|
||
SizedBox(height: 48.h),
|
||
// 标题
|
||
Text(
|
||
data.title,
|
||
style: TextStyle(
|
||
fontSize: 24.sp,
|
||
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),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建占位图片
|
||
Widget _buildPlaceholderImage(int index) {
|
||
final icons = [
|
||
Icons.nature,
|
||
Icons.memory,
|
||
Icons.people,
|
||
Icons.security,
|
||
];
|
||
final colors = [
|
||
const Color(0xFF8BC34A),
|
||
const Color(0xFFD4AF37),
|
||
const Color(0xFFFF9800),
|
||
const Color(0xFF2196F3),
|
||
];
|
||
|
||
return Center(
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Icon(
|
||
icons[index],
|
||
size: 80.sp,
|
||
color: colors[index],
|
||
),
|
||
SizedBox(height: 16.h),
|
||
Text(
|
||
'向导页 ${index + 1}',
|
||
style: TextStyle(
|
||
fontSize: 16.sp,
|
||
color: const Color(0xFF57534E),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建欢迎加入页面 (页面5)
|
||
Widget _buildWelcomePage() {
|
||
return _WelcomePageContent(
|
||
onNext: _goToOnboarding,
|
||
);
|
||
}
|
||
|
||
/// 构建页面指示器
|
||
Widget _buildPageIndicator() {
|
||
return Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: List.generate(5, (index) {
|
||
final isActive = index == _currentPage;
|
||
return Container(
|
||
width: 8.w,
|
||
height: 8.w,
|
||
margin: EdgeInsets.symmetric(horizontal: 4.w),
|
||
decoration: BoxDecoration(
|
||
shape: BoxShape.circle,
|
||
color: isActive
|
||
? const Color(0xFF8E794A)
|
||
: const Color(0xFFEAE0CD),
|
||
),
|
||
);
|
||
}),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 欢迎加入页面内容 (第5页)
|
||
class _WelcomePageContent extends StatefulWidget {
|
||
final VoidCallback onNext;
|
||
|
||
const _WelcomePageContent({required this.onNext});
|
||
|
||
@override
|
||
State<_WelcomePageContent> createState() => _WelcomePageContentState();
|
||
}
|
||
|
||
class _WelcomePageContentState extends State<_WelcomePageContent> {
|
||
bool _hasReferrer = true;
|
||
final TextEditingController _referralCodeController = TextEditingController();
|
||
|
||
@override
|
||
void dispose() {
|
||
_referralCodeController.dispose();
|
||
super.dispose();
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Padding(
|
||
padding: EdgeInsets.symmetric(horizontal: 24.w),
|
||
child: Column(
|
||
children: [
|
||
// 退出按钮
|
||
Align(
|
||
alignment: Alignment.topRight,
|
||
child: Padding(
|
||
padding: EdgeInsets.only(top: 32.h),
|
||
child: GestureDetector(
|
||
onTap: () {
|
||
// 退出向导
|
||
Navigator.of(context).maybePop();
|
||
},
|
||
child: Text(
|
||
'退出 Exit',
|
||
style: TextStyle(
|
||
fontSize: 14.sp,
|
||
color: const Color(0xFFA99F93),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
SizedBox(height: 100.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: 62.h),
|
||
// 选项区域
|
||
_buildReferrerOptions(),
|
||
const Spacer(),
|
||
// 下一步按钮
|
||
GestureDetector(
|
||
onTap: widget.onNext,
|
||
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: const Color(0xFFD9C8A9),
|
||
),
|
||
textAlign: TextAlign.right,
|
||
),
|
||
),
|
||
),
|
||
SizedBox(height: 80.h),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建推荐人选项
|
||
Widget _buildReferrerOptions() {
|
||
return Column(
|
||
children: [
|
||
// 有推荐人选项
|
||
GestureDetector(
|
||
onTap: () {
|
||
setState(() {
|
||
_hasReferrer = true;
|
||
});
|
||
},
|
||
child: Row(
|
||
children: [
|
||
_buildRadio(_hasReferrer),
|
||
SizedBox(width: 12.w),
|
||
Text(
|
||
'我有推荐人',
|
||
style: TextStyle(
|
||
fontSize: 16.sp,
|
||
height: 1.5,
|
||
color: const Color(0xFF6F6354),
|
||
),
|
||
),
|
||
const Spacer(),
|
||
// 扫码图标
|
||
Icon(
|
||
Icons.qr_code_scanner,
|
||
size: 24.sp,
|
||
color: const Color(0xFFA99F93),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
SizedBox(height: 14.h),
|
||
// 推荐码输入框
|
||
if (_hasReferrer)
|
||
Container(
|
||
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 4.w),
|
||
decoration: const BoxDecoration(
|
||
border: Border(
|
||
bottom: BorderSide(
|
||
width: 1,
|
||
color: Color(0xFFEAE1D2),
|
||
),
|
||
),
|
||
),
|
||
child: TextField(
|
||
controller: _referralCodeController,
|
||
decoration: InputDecoration(
|
||
hintText: '请输入推荐码 / 序列号',
|
||
hintStyle: TextStyle(
|
||
fontSize: 16.sp,
|
||
color: const Color(0xFFA99F93),
|
||
),
|
||
border: InputBorder.none,
|
||
isDense: true,
|
||
contentPadding: EdgeInsets.zero,
|
||
),
|
||
style: TextStyle(
|
||
fontSize: 16.sp,
|
||
color: const Color(0xFF6F6354),
|
||
),
|
||
),
|
||
),
|
||
SizedBox(height: 24.h),
|
||
// 没有推荐人选项
|
||
GestureDetector(
|
||
onTap: () {
|
||
setState(() {
|
||
_hasReferrer = false;
|
||
});
|
||
},
|
||
child: Row(
|
||
children: [
|
||
_buildRadio(!_hasReferrer),
|
||
SizedBox(width: 12.w),
|
||
Text(
|
||
'我没有推荐人',
|
||
style: TextStyle(
|
||
fontSize: 16.sp,
|
||
height: 1.5,
|
||
color: const Color(0xFF6F6354),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
/// 构建单选按钮
|
||
Widget _buildRadio(bool isSelected) {
|
||
return Container(
|
||
width: 20.w,
|
||
height: 20.w,
|
||
decoration: BoxDecoration(
|
||
shape: BoxShape.circle,
|
||
border: Border.all(
|
||
width: isSelected ? 6.w : 2.w,
|
||
color: isSelected
|
||
? const Color(0xFF2563EB)
|
||
: const Color(0xFFA99F93),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|