feat: 添加5页向导页和首次打开检测功能
- 新增向导页组件(guide_page.dart),支持左右滑动浏览 - 实现首次打开检测逻辑,控制向导页显示 - 更新app图标为自定义logo - 更新app名称为"榴莲皇后" - 添加响应式尺寸扩展(.w/.h/.sp/.r) - 优化底部导航栏响应式适配 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
|
|
@ -1,6 +1,6 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<application
|
<application
|
||||||
android:label="rwa_android_app"
|
android:label="榴莲皇后"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<activity
|
<activity
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
|
<foreground>
|
||||||
|
<inset
|
||||||
|
android:drawable="@drawable/ic_launcher_foreground"
|
||||||
|
android:inset="16%" />
|
||||||
|
</foreground>
|
||||||
|
</adaptive-icon>
|
||||||
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 27 KiB |
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_background">#FFFFFF</color>
|
||||||
|
</resources>
|
||||||
|
After Width: | Height: | Size: 424 KiB |
|
|
@ -427,7 +427,7 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
|
@ -484,7 +484,7 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
|
|
||||||
|
|
@ -1,122 +1 @@
|
||||||
{
|
{"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-20x20@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-20x20@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-29x29@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-29x29@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-29x29@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-40x40@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-40x40@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "60x60",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-60x60@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "60x60",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-60x60@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-20x20@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-20x20@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-29x29@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-29x29@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-40x40@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-40x40@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "76x76",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-76x76@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "76x76",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-76x76@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "83.5x83.5",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "1024x1024",
|
|
||||||
"idiom" : "ios-marketing",
|
|
||||||
"filename" : "Icon-App-1024x1024@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 440 KiB |
|
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 748 B |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 22 KiB |
|
|
@ -12,15 +12,25 @@ class App extends ConsumerWidget {
|
||||||
final router = ref.watch(appRouterProvider);
|
final router = ref.watch(appRouterProvider);
|
||||||
|
|
||||||
return ScreenUtilInit(
|
return ScreenUtilInit(
|
||||||
designSize: const Size(375, 812),
|
designSize: const Size(360, 800), // 与 UIPro Figma 设计稿一致
|
||||||
minTextAdapt: true,
|
minTextAdapt: true,
|
||||||
splitScreenMode: true,
|
splitScreenMode: true,
|
||||||
|
useInheritedMediaQuery: true,
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
return MaterialApp.router(
|
return MaterialApp.router(
|
||||||
title: 'RWA榴莲女皇',
|
title: '榴莲皇后',
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
theme: AppTheme.light,
|
theme: AppTheme.light,
|
||||||
routerConfig: router,
|
routerConfig: router,
|
||||||
|
builder: (context, widget) {
|
||||||
|
// 限制系统字体缩放,保持 UI 一致性
|
||||||
|
return MediaQuery(
|
||||||
|
data: MediaQuery.of(context).copyWith(
|
||||||
|
textScaler: TextScaler.noScaling,
|
||||||
|
),
|
||||||
|
child: widget!,
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,22 @@
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
/// 响应式尺寸扩展
|
||||||
|
/// 使用方式: 16.w (宽度适配), 16.h (高度适配), 16.sp (字体适配), 16.r (圆角适配)
|
||||||
|
extension ResponsiveNum on num {
|
||||||
|
/// 宽度适配
|
||||||
|
double get w => ScreenUtil().setWidth(this);
|
||||||
|
|
||||||
|
/// 高度适配
|
||||||
|
double get h => ScreenUtil().setHeight(this);
|
||||||
|
|
||||||
|
/// 字体大小适配
|
||||||
|
double get sp => ScreenUtil().setSp(this);
|
||||||
|
|
||||||
|
/// 圆角适配 (取宽高最小值)
|
||||||
|
double get r => ScreenUtil().radius(this);
|
||||||
|
}
|
||||||
|
|
||||||
extension NumExtensions on num {
|
extension NumExtensions on num {
|
||||||
String get formatted {
|
String get formatted {
|
||||||
return NumberFormat('#,##0.##').format(this);
|
return NumberFormat('#,##0.##').format(this);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
|
||||||
|
/// 应用尺寸常量
|
||||||
|
/// 提供静态值和响应式值两种方式
|
||||||
|
/// 静态值用于不需要适配的场景,响应式值用于需要适配的场景
|
||||||
class AppDimensions {
|
class AppDimensions {
|
||||||
AppDimensions._();
|
AppDimensions._();
|
||||||
|
|
||||||
// 间距
|
// ============ 静态间距 ============
|
||||||
static const double spacingXs = 4.0;
|
static const double spacingXs = 4.0;
|
||||||
static const double spacingSm = 8.0;
|
static const double spacingSm = 8.0;
|
||||||
static const double spacingMd = 16.0;
|
static const double spacingMd = 16.0;
|
||||||
|
|
@ -9,7 +14,15 @@ class AppDimensions {
|
||||||
static const double spacingXl = 32.0;
|
static const double spacingXl = 32.0;
|
||||||
static const double spacingXxl = 48.0;
|
static const double spacingXxl = 48.0;
|
||||||
|
|
||||||
// 圆角
|
// ============ 响应式间距 ============
|
||||||
|
static double get spacingXsR => 4.w;
|
||||||
|
static double get spacingSmR => 8.w;
|
||||||
|
static double get spacingMdR => 16.w;
|
||||||
|
static double get spacingLgR => 24.w;
|
||||||
|
static double get spacingXlR => 32.w;
|
||||||
|
static double get spacingXxlR => 48.w;
|
||||||
|
|
||||||
|
// ============ 静态圆角 ============
|
||||||
static const double radiusXs = 4.0;
|
static const double radiusXs = 4.0;
|
||||||
static const double radiusSm = 8.0;
|
static const double radiusSm = 8.0;
|
||||||
static const double radiusMd = 12.0;
|
static const double radiusMd = 12.0;
|
||||||
|
|
@ -17,34 +30,70 @@ class AppDimensions {
|
||||||
static const double radiusXl = 24.0;
|
static const double radiusXl = 24.0;
|
||||||
static const double radiusFull = 999.0;
|
static const double radiusFull = 999.0;
|
||||||
|
|
||||||
// 图标尺寸
|
// ============ 响应式圆角 ============
|
||||||
|
static double get radiusXsR => 4.r;
|
||||||
|
static double get radiusSmR => 8.r;
|
||||||
|
static double get radiusMdR => 12.r;
|
||||||
|
static double get radiusLgR => 16.r;
|
||||||
|
static double get radiusXlR => 24.r;
|
||||||
|
|
||||||
|
// ============ 静态图标尺寸 ============
|
||||||
static const double iconXs = 16.0;
|
static const double iconXs = 16.0;
|
||||||
static const double iconSm = 20.0;
|
static const double iconSm = 20.0;
|
||||||
static const double iconMd = 24.0;
|
static const double iconMd = 24.0;
|
||||||
static const double iconLg = 32.0;
|
static const double iconLg = 32.0;
|
||||||
static const double iconXl = 48.0;
|
static const double iconXl = 48.0;
|
||||||
|
|
||||||
// 按钮高度
|
// ============ 响应式图标尺寸 ============
|
||||||
|
static double get iconXsR => 16.sp;
|
||||||
|
static double get iconSmR => 20.sp;
|
||||||
|
static double get iconMdR => 24.sp;
|
||||||
|
static double get iconLgR => 32.sp;
|
||||||
|
static double get iconXlR => 48.sp;
|
||||||
|
|
||||||
|
// ============ 静态按钮高度 ============
|
||||||
static const double buttonHeightSm = 36.0;
|
static const double buttonHeightSm = 36.0;
|
||||||
static const double buttonHeightMd = 44.0;
|
static const double buttonHeightMd = 44.0;
|
||||||
static const double buttonHeightLg = 52.0;
|
static const double buttonHeightLg = 52.0;
|
||||||
|
|
||||||
// 输入框高度
|
// ============ 响应式按钮高度 ============
|
||||||
|
static double get buttonHeightSmR => 36.h;
|
||||||
|
static double get buttonHeightMdR => 44.h;
|
||||||
|
static double get buttonHeightLgR => 52.h;
|
||||||
|
|
||||||
|
// ============ 静态输入框高度 ============
|
||||||
static const double inputHeight = 48.0;
|
static const double inputHeight = 48.0;
|
||||||
|
|
||||||
// 头像尺寸
|
// ============ 响应式输入框高度 ============
|
||||||
|
static double get inputHeightR => 48.h;
|
||||||
|
|
||||||
|
// ============ 静态头像尺寸 ============
|
||||||
static const double avatarSm = 32.0;
|
static const double avatarSm = 32.0;
|
||||||
static const double avatarMd = 48.0;
|
static const double avatarMd = 48.0;
|
||||||
static const double avatarLg = 64.0;
|
static const double avatarLg = 64.0;
|
||||||
static const double avatarXl = 96.0;
|
static const double avatarXl = 96.0;
|
||||||
|
|
||||||
// 卡片
|
// ============ 响应式头像尺寸 ============
|
||||||
|
static double get avatarSmR => 32.w;
|
||||||
|
static double get avatarMdR => 48.w;
|
||||||
|
static double get avatarLgR => 64.w;
|
||||||
|
static double get avatarXlR => 96.w;
|
||||||
|
|
||||||
|
// ============ 卡片 ============
|
||||||
static const double cardPadding = 16.0;
|
static const double cardPadding = 16.0;
|
||||||
static const double cardRadius = 12.0;
|
static const double cardRadius = 12.0;
|
||||||
|
|
||||||
// 底部导航栏
|
static double get cardPaddingR => 16.w;
|
||||||
static const double bottomNavHeight = 56.0;
|
static double get cardRadiusR => 12.r;
|
||||||
|
|
||||||
// AppBar
|
// ============ 导航栏 ============
|
||||||
|
static const double bottomNavHeight = 65.0;
|
||||||
static const double appBarHeight = 56.0;
|
static const double appBarHeight = 56.0;
|
||||||
|
|
||||||
|
static double get bottomNavHeightR => 65.h;
|
||||||
|
static double get appBarHeightR => 56.h;
|
||||||
|
|
||||||
|
// ============ 最小点击区域 (无障碍规范要求至少 48x48) ============
|
||||||
|
static const double minTouchTarget = 48.0;
|
||||||
|
static double get minTouchTargetR => 48.w;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,442 @@
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -36,8 +36,11 @@ class _SplashPageState extends ConsumerState<SplashPage> {
|
||||||
if (authState.isWalletCreated) {
|
if (authState.isWalletCreated) {
|
||||||
// 已创建钱包,进入主页面(龙虎榜)
|
// 已创建钱包,进入主页面(龙虎榜)
|
||||||
context.go(RoutePaths.ranking);
|
context.go(RoutePaths.ranking);
|
||||||
|
} else if (authState.isFirstLaunch || !authState.hasSeenGuide) {
|
||||||
|
// 首次打开或未看过向导,进入向导页
|
||||||
|
context.go(RoutePaths.guide);
|
||||||
} else {
|
} else {
|
||||||
// 未创建钱包,进入引导页面
|
// 已看过向导但未创建钱包,直接进入创建账户页面
|
||||||
context.go(RoutePaths.onboarding);
|
context.go(RoutePaths.onboarding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,16 @@ class AuthState {
|
||||||
final AuthStatus status;
|
final AuthStatus status;
|
||||||
final String? walletAddress;
|
final String? walletAddress;
|
||||||
final bool isWalletCreated;
|
final bool isWalletCreated;
|
||||||
|
final bool isFirstLaunch;
|
||||||
|
final bool hasSeenGuide;
|
||||||
final String? errorMessage;
|
final String? errorMessage;
|
||||||
|
|
||||||
const AuthState({
|
const AuthState({
|
||||||
this.status = AuthStatus.initial,
|
this.status = AuthStatus.initial,
|
||||||
this.walletAddress,
|
this.walletAddress,
|
||||||
this.isWalletCreated = false,
|
this.isWalletCreated = false,
|
||||||
|
this.isFirstLaunch = true,
|
||||||
|
this.hasSeenGuide = false,
|
||||||
this.errorMessage,
|
this.errorMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -27,12 +31,16 @@ class AuthState {
|
||||||
AuthStatus? status,
|
AuthStatus? status,
|
||||||
String? walletAddress,
|
String? walletAddress,
|
||||||
bool? isWalletCreated,
|
bool? isWalletCreated,
|
||||||
|
bool? isFirstLaunch,
|
||||||
|
bool? hasSeenGuide,
|
||||||
String? errorMessage,
|
String? errorMessage,
|
||||||
}) {
|
}) {
|
||||||
return AuthState(
|
return AuthState(
|
||||||
status: status ?? this.status,
|
status: status ?? this.status,
|
||||||
walletAddress: walletAddress ?? this.walletAddress,
|
walletAddress: walletAddress ?? this.walletAddress,
|
||||||
isWalletCreated: isWalletCreated ?? this.isWalletCreated,
|
isWalletCreated: isWalletCreated ?? this.isWalletCreated,
|
||||||
|
isFirstLaunch: isFirstLaunch ?? this.isFirstLaunch,
|
||||||
|
hasSeenGuide: hasSeenGuide ?? this.hasSeenGuide,
|
||||||
errorMessage: errorMessage,
|
errorMessage: errorMessage,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -47,6 +55,11 @@ class AuthNotifier extends StateNotifier<AuthState> {
|
||||||
state = state.copyWith(status: AuthStatus.checking);
|
state = state.copyWith(status: AuthStatus.checking);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 检查是否首次启动
|
||||||
|
final isFirstLaunchStr = await _secureStorage.read(key: StorageKeys.isFirstLaunch);
|
||||||
|
final isFirstLaunch = isFirstLaunchStr == null || isFirstLaunchStr != 'false';
|
||||||
|
|
||||||
|
// 检查钱包状态
|
||||||
final walletAddress = await _secureStorage.read(key: StorageKeys.walletAddress);
|
final walletAddress = await _secureStorage.read(key: StorageKeys.walletAddress);
|
||||||
final isWalletCreated = walletAddress != null && walletAddress.isNotEmpty;
|
final isWalletCreated = walletAddress != null && walletAddress.isNotEmpty;
|
||||||
|
|
||||||
|
|
@ -55,11 +68,15 @@ class AuthNotifier extends StateNotifier<AuthState> {
|
||||||
status: AuthStatus.authenticated,
|
status: AuthStatus.authenticated,
|
||||||
walletAddress: walletAddress,
|
walletAddress: walletAddress,
|
||||||
isWalletCreated: true,
|
isWalletCreated: true,
|
||||||
|
isFirstLaunch: false,
|
||||||
|
hasSeenGuide: true,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
status: AuthStatus.unauthenticated,
|
status: AuthStatus.unauthenticated,
|
||||||
isWalletCreated: false,
|
isWalletCreated: false,
|
||||||
|
isFirstLaunch: isFirstLaunch,
|
||||||
|
hasSeenGuide: !isFirstLaunch,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -70,6 +87,15 @@ class AuthNotifier extends StateNotifier<AuthState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 标记已查看向导页
|
||||||
|
Future<void> markGuideAsSeen() async {
|
||||||
|
await _secureStorage.write(key: StorageKeys.isFirstLaunch, value: 'false');
|
||||||
|
state = state.copyWith(
|
||||||
|
isFirstLaunch: false,
|
||||||
|
hasSeenGuide: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> saveWallet(String walletAddress, String privateKey) async {
|
Future<void> saveWallet(String walletAddress, String privateKey) async {
|
||||||
await _secureStorage.write(key: StorageKeys.walletAddress, value: walletAddress);
|
await _secureStorage.write(key: StorageKeys.walletAddress, value: walletAddress);
|
||||||
await _secureStorage.write(key: StorageKeys.privateKey, value: privateKey);
|
await _secureStorage.write(key: StorageKeys.privateKey, value: privateKey);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
|
||||||
/// 底部导航栏组件
|
/// 底部导航栏组件
|
||||||
/// 包含四个Tab:龙虎榜、矿机、交易、我
|
/// 包含四个Tab:龙虎榜、矿机、交易、我
|
||||||
|
/// 使用响应式设计适配各种屏幕尺寸
|
||||||
class BottomNavBar extends StatelessWidget {
|
class BottomNavBar extends StatelessWidget {
|
||||||
final int currentIndex;
|
final int currentIndex;
|
||||||
final Function(int) onTap;
|
final Function(int) onTap;
|
||||||
|
|
@ -15,7 +17,7 @@ class BottomNavBar extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
height: 65,
|
height: 65.h,
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: Color(0xFFFFF5E6),
|
color: Color(0xFFFFF5E6),
|
||||||
border: Border(
|
border: Border(
|
||||||
|
|
@ -77,14 +79,14 @@ class BottomNavBar extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
isSelected ? activeIcon : icon,
|
isSelected ? activeIcon : icon,
|
||||||
size: 24,
|
size: 24.sp,
|
||||||
color: isSelected ? const Color(0xFFD4AF37) : const Color(0xFF8B5A2B),
|
color: isSelected ? const Color(0xFFD4AF37) : const Color(0xFF8B5A2B),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
SizedBox(height: 2.h),
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12.sp,
|
||||||
fontFamily: 'Inter',
|
fontFamily: 'Inter',
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
height: 1.33,
|
height: 1.33,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import '../features/auth/presentation/pages/splash_page.dart';
|
import '../features/auth/presentation/pages/splash_page.dart';
|
||||||
|
import '../features/auth/presentation/pages/guide_page.dart';
|
||||||
import '../features/auth/presentation/pages/onboarding_page.dart';
|
import '../features/auth/presentation/pages/onboarding_page.dart';
|
||||||
import '../features/auth/presentation/pages/backup_mnemonic_page.dart';
|
import '../features/auth/presentation/pages/backup_mnemonic_page.dart';
|
||||||
import '../features/auth/presentation/pages/verify_mnemonic_page.dart';
|
import '../features/auth/presentation/pages/verify_mnemonic_page.dart';
|
||||||
|
|
@ -67,6 +68,13 @@ final appRouterProvider = Provider<GoRouter>((ref) {
|
||||||
builder: (context, state) => const SplashPage(),
|
builder: (context, state) => const SplashPage(),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Guide Pages (向导页)
|
||||||
|
GoRoute(
|
||||||
|
path: RoutePaths.guide,
|
||||||
|
name: RouteNames.guide,
|
||||||
|
builder: (context, state) => const GuidePage(),
|
||||||
|
),
|
||||||
|
|
||||||
// Onboarding / Create Account
|
// Onboarding / Create Account
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: RoutePaths.onboarding,
|
path: RoutePaths.onboarding,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ class RouteNames {
|
||||||
|
|
||||||
// Auth
|
// Auth
|
||||||
static const splash = 'splash';
|
static const splash = 'splash';
|
||||||
|
static const guide = 'guide';
|
||||||
static const onboarding = 'onboarding';
|
static const onboarding = 'onboarding';
|
||||||
static const createWallet = 'create-wallet';
|
static const createWallet = 'create-wallet';
|
||||||
static const backupMnemonic = 'backup-mnemonic';
|
static const backupMnemonic = 'backup-mnemonic';
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ class RoutePaths {
|
||||||
|
|
||||||
// Auth
|
// Auth
|
||||||
static const splash = '/';
|
static const splash = '/';
|
||||||
|
static const guide = '/guide';
|
||||||
static const onboarding = '/onboarding';
|
static const onboarding = '/onboarding';
|
||||||
static const createWallet = '/auth/create';
|
static const createWallet = '/auth/create';
|
||||||
static const backupMnemonic = '/auth/backup-mnemonic';
|
static const backupMnemonic = '/auth/backup-mnemonic';
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.4"
|
version: "2.0.4"
|
||||||
|
cli_util:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cli_util
|
||||||
|
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.2"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -398,6 +406,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.1"
|
version: "3.4.1"
|
||||||
|
flutter_launcher_icons:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: flutter_launcher_icons
|
||||||
|
sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.14.4"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
|
@ -605,6 +621,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.2"
|
version: "4.1.2"
|
||||||
|
image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image
|
||||||
|
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.5.4"
|
||||||
image_picker:
|
image_picker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,16 @@ dev_dependencies:
|
||||||
# 测试
|
# 测试
|
||||||
mocktail: ^1.0.3
|
mocktail: ^1.0.3
|
||||||
|
|
||||||
|
# 应用图标生成
|
||||||
|
flutter_launcher_icons: ^0.14.3
|
||||||
|
|
||||||
|
flutter_launcher_icons:
|
||||||
|
android: true
|
||||||
|
ios: true
|
||||||
|
image_path: "assets/images/logo/app_icon.png"
|
||||||
|
adaptive_icon_background: "#FFFFFF"
|
||||||
|
adaptive_icon_foreground: "assets/images/logo/app_icon.png"
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
||||||
|
|
|
||||||