ponshu-room-lite/lib/widgets/onboarding_dialog.dart

205 lines
8.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:lucide_icons/lucide_icons.dart';
import '../theme/app_colors.dart';
class OnboardingDialog extends StatefulWidget {
final VoidCallback onFinish;
final List<Map<String, dynamic>>? pages; // Support dynamic for IconData
const OnboardingDialog({super.key, required this.onFinish, this.pages});
@override
State<OnboardingDialog> createState() => _OnboardingDialogState();
}
class _OnboardingDialogState extends State<OnboardingDialog> {
final PageController _controller = PageController();
int _currentPage = 0;
late final List<Map<String, dynamic>> _pages;
@override
void initState() {
super.initState();
_pages = widget.pages ?? [
{
'title': '日本酒の『魂』を保存する',
'description': 'Ponshu Roomへようこそ。\n飲んだ日本酒のラベルを撮影するだけで、\nAIが情報を解析し、あなたのリストに加えます。',
'icon': '🍶',
},
{
'title': 'ラベルを撮るだけ',
'description': '面倒な入力は不要です。\nラベルの写真を撮れば、銘柄、蔵元、味わいまで\nAIが自動でデータベース化します。',
'icon': LucideIcons.camera,
},
{
'title': 'あなただけのお品書き',
'description': '集めたコレクションから、\nボタンひとつで美しいお品書きPDFを作成。\n飲食店の方も、個人の方も楽しめます。',
'icon': LucideIcons.scrollText,
},
{
'title': 'さあ、始めましょう',
'description': 'AIの力で、日本酒ライフをもっと豊かに。\n右下のカメラボタンからスタートしてください。',
'icon': LucideIcons.sparkles,
},
];
}
@override
Widget build(BuildContext context) {
final appColors = Theme.of(context).extension<AppColors>()!;
return Dialog(
backgroundColor: Colors.transparent, // Custom shape
insetPadding: const EdgeInsets.all(20),
child: Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
Container(
height: 500,
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.3),
blurRadius: 20,
offset: const Offset(0, 10),
)
],
),
child: Column(
children: [
Expanded(
child: PageView.builder(
controller: _controller,
onPageChanged: (index) => setState(() => _currentPage = index),
itemCount: _pages.length,
itemBuilder: (context, index) {
final page = _pages[index];
final iconData = page['icon'];
return Center(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (iconData is IconData)
Icon(iconData, size: 80, color: Theme.of(context).brightness == Brightness.dark ? Colors.white : Theme.of(context).primaryColor)
else
Text(
iconData.toString(),
style: const TextStyle(fontSize: 80),
),
const SizedBox(height: 32),
Text(
page['title']!,
textAlign: TextAlign.center,
style: GoogleFonts.zenOldMincho(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Theme.of(context).textTheme.bodyLarge?.color,
),
),
const SizedBox(height: 16),
Text(
page['description']!,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
height: 1.6,
color: appColors.textSecondary,
),
),
],
),
),
),
);
},
),
),
// Indicators
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(_pages.length, (index) {
return AnimatedContainer(
duration: const Duration(milliseconds: 300),
margin: const EdgeInsets.symmetric(horizontal: 4),
height: 8,
width: _currentPage == index ? 24 : 8,
decoration: BoxDecoration(
color: _currentPage == index
? Theme.of(context).primaryColor
: appColors.textSecondary,
borderRadius: BorderRadius.circular(4),
),
);
}),
),
const SizedBox(height: 24),
// Button
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32.0, vertical: 24.0),
child: SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 0,
backgroundColor: appColors.brandPrimary,
foregroundColor: appColors.surfaceSubtle,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
onPressed: () {
if (_currentPage < _pages.length - 1) {
_controller.nextPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeIn,
);
} else {
widget.onFinish();
}
},
child: Text(
_currentPage < _pages.length - 1 ? '次へ' : 'はじめる',
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
),
),
),
],
),
),
// Close Button
Positioned(
top: -12,
right: -12,
child: GestureDetector(
onTap: widget.onFinish,
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.2),
blurRadius: 8,
offset: const Offset(0, 4),
)
],
),
child: const Icon(LucideIcons.x, size: 24),
),
),
),
],
),
);
}
}