Files
monisuo/flutter_monisuo/lib/ui/pages/onboarding/onboarding_page.dart
sion123 f5ac578892 docs(theme): update documentation and clean up deprecated color scheme definitions
Removed outdated compatibility aliases and deprecated methods from AppColorScheme,
and updated CLAUDE.md to reflect new theme system requirements with centralized
color management and no hard-coded values in UI components.
2026-04-05 23:37:27 +08:00

281 lines
9.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../../../core/theme/app_color_scheme.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../core/theme/app_theme.dart';
import '../../../core/storage/local_storage.dart';
/// 引导页数据模型
class _OnboardingItem {
final String title;
final String description;
final IconData? icon; // 图标(二选一)
final String? imagePath; // 图片路径(二选一)
final List<Color> gradientColors;
const _OnboardingItem({
required this.title,
required this.description,
this.icon,
this.imagePath,
required this.gradientColors,
});
}
/// 首次启动引导页
class OnboardingPage extends StatefulWidget {
final VoidCallback onComplete;
const OnboardingPage({super.key, required this.onComplete});
@override
State<OnboardingPage> createState() => _OnboardingPageState();
}
class _OnboardingPageState extends State<OnboardingPage> {
final PageController _pageController = PageController();
int _currentPage = 0;
final _items = const [
_OnboardingItem(
title: '实时行情',
description: '全球市场行情实时更新,把握每一个投资机会',
imagePath: 'assets/images/onboarding_1.png', // 替换为你的图片
gradientColors: [AppColorScheme.darkPrimary, AppColorScheme.darkPrimaryContainer],
),
_OnboardingItem(
title: '模拟交易',
description: '零风险体验真实交易,学习投资策略',
imagePath: 'assets/images/onboarding_2.png', // 替换为你的图片
gradientColors: [AppColorScheme.darkTertiary, AppColorScheme.darkTertiaryContainer],
),
_OnboardingItem(
title: '资产管理',
description: '清晰的资产概览,轻松管理你的投资组合',
imagePath: 'assets/images/onboarding_3.png', // 替换为你的图片
gradientColors: [AppColorScheme.darkSecondary, AppColorScheme.darkSecondaryFixed],
),
_OnboardingItem(
title: '安全可靠',
description: '数据加密存储,保护你的隐私安全',
imagePath: 'assets/images/onboarding_4.png', // 替换为你的图片
gradientColors: [AppColorScheme.darkPrimaryFixed, AppColorScheme.darkPrimaryFixedDim],
),
];
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
void _nextPage() {
if (_currentPage < _items.length - 1) {
_pageController.nextPage(
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
);
} else {
_completeOnboarding();
}
}
void _skip() {
_completeOnboarding();
}
Future<void> _completeOnboarding() async {
await LocalStorage.setBool('onboarding_completed', true);
widget.onComplete();
}
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final isDark = Theme.of(context).brightness == Brightness.dark;
return Scaffold(
backgroundColor: colorScheme.surface,
body: SafeArea(
child: Column(
children: [
// 顶部跳过按钮
Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.lg,
vertical: AppSpacing.md,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: _skip,
child: Text(
'跳过',
style: AppTextStyles.headlineMedium(context).copyWith(
color: colorScheme.onSurfaceVariant,
),
),
),
],
),
),
// 页面内容
Expanded(
child: PageView.builder(
controller: _pageController,
onPageChanged: (index) {
setState(() {
_currentPage = index;
});
},
itemCount: _items.length,
itemBuilder: (context, index) {
return _buildPage(_items[index], isDark);
},
),
),
// 底部指示器和按钮
Padding(
padding: const EdgeInsets.fromLTRB(
AppSpacing.lg,
AppSpacing.md,
AppSpacing.lg,
AppSpacing.xl,
),
child: Column(
children: [
// 页面指示器
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
_items.length,
(index) => _buildIndicator(index, isDark),
),
),
const SizedBox(height: AppSpacing.xl),
// 下一步/开始按钮
SizedBox(
width: double.infinity,
height: 56,
child: ElevatedButton(
onPressed: _nextPage,
style: ElevatedButton.styleFrom(
backgroundColor: colorScheme.primary,
foregroundColor: colorScheme.onPrimary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppRadius.lg),
),
elevation: 0,
),
child: Text(
_currentPage == _items.length - 1 ? '开始使用' : '下一步',
style: AppTextStyles.headlineLarge(context).copyWith(
fontWeight: FontWeight.w600,
),
),
),
),
],
),
),
],
),
),
);
}
Widget _buildPage(_OnboardingItem item, bool isDark) {
final colorScheme = Theme.of(context).colorScheme;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xl),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 图标/图片容器
Container(
width: 200,
height: 200,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: item.gradientColors,
),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: item.gradientColors.first.withValues(alpha: 0.4),
blurRadius: 40,
offset: const Offset(0, 20),
),
],
),
child: Center(
child: item.imagePath != null
? ClipOval(
child: Image.asset(
item.imagePath!,
width: 180,
height: 180,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
// 图片加载失败时显示图标
return Icon(
item.icon ?? LucideIcons.image,
size: 72,
color: AppColorScheme.darkOnPrimary,
);
},
),
)
: Icon(
item.icon ?? LucideIcons.star,
size: 72,
color: AppColorScheme.darkOnPrimary,
),
),
),
const SizedBox(height: AppSpacing.xxl + AppSpacing.lg),
// 标题
Text(
item.title,
style: AppTextStyles.displaySmall(context).copyWith(
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
letterSpacing: -0.5,
),
),
const SizedBox(height: AppSpacing.md),
// 描述
Text(
item.description,
textAlign: TextAlign.center,
style: AppTextStyles.headlineMedium(context).copyWith(
color: colorScheme.onSurfaceVariant,
height: 1.6,
),
),
],
),
);
}
Widget _buildIndicator(int index, bool isDark) {
final colorScheme = Theme.of(context).colorScheme;
final isActive = index == _currentPage;
return AnimatedContainer(
duration: const Duration(milliseconds: 300),
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.xs),
width: isActive ? 24 : 8,
height: 8,
decoration: BoxDecoration(
color: isActive ? colorScheme.primary : colorScheme.outlineVariant,
borderRadius: BorderRadius.circular(AppRadius.sm),
),
);
}
}