import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../core/localization/app_localizations.dart';
import '../core/theme/app_colors.dart';
class FirstRunOnboardingTrigger extends StatefulWidget {
const FirstRunOnboardingTrigger({super.key});
@override
State<FirstRunOnboardingTrigger> createState() =>
_FirstRunOnboardingTriggerState();
}
class _FirstRunOnboardingTriggerState extends State<FirstRunOnboardingTrigger> {
static const _key = 'mw_onboarding_seen_v1';
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => _maybeShow());
}
Future<void> _maybeShow() async {
try {
final prefs = await SharedPreferences.getInstance();
if (prefs.getBool(_key) == true || !mounted) return;
await showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
useSafeArea: true,
builder: (_) => const _OnboardingSheet(),
);
await prefs.setBool(_key, true);
} catch (_) {
// Onboarding is optional; storage failures must never block the app.
}
}
@override
Widget build(BuildContext context) => const SizedBox.shrink();
}
class _OnboardingSheet extends StatefulWidget {
const _OnboardingSheet();
@override
State<_OnboardingSheet> createState() => _OnboardingSheetState();
}
class _OnboardingSheetState extends State<_OnboardingSheet> {
final PageController _controller = PageController();
int _page = 0;
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context);
final items = [
(
Icons.explore_rounded,
l10n.t('onboardingDiscoverTitle'),
l10n.t('onboardingDiscoverBody')
),
(
Icons.location_on_rounded,
l10n.t('onboardingNowTitle'),
l10n.t('onboardingNowBody')
),
(
Icons.route_rounded,
l10n.t('onboardingGuideTitle'),
l10n.t('onboardingGuideBody')
),
];
final availableHeight = MediaQuery.sizeOf(context).height;
return ConstrainedBox(
constraints: BoxConstraints(maxHeight: availableHeight * .78),
child: Padding(
padding: const EdgeInsets.fromLTRB(24, 22, 24, 20),
child: Column(
children: [
Expanded(
child: PageView.builder(
controller: _controller,
itemCount: items.length,
onPageChanged: (value) => setState(() => _page = value),
itemBuilder: (context, index) {
final item = items[index];
return SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 110,
height: 110,
decoration: BoxDecoration(
color: AppColors.softPink,
borderRadius: BorderRadius.circular(34)),
child: Icon(item.$1,
size: 58, color: AppColors.heartRed),
),
const SizedBox(height: 24),
Text(item.$2,
textAlign: TextAlign.center,
style: Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(fontWeight: FontWeight.w900)),
const SizedBox(height: 10),
Text(item.$3,
textAlign: TextAlign.center,
style: const TextStyle(
color: AppColors.mutedInk, height: 1.45)),
],
),
);
},
),
),
Row(
children: [
for (var i = 0; i < items.length; i++)
AnimatedContainer(
duration: const Duration(milliseconds: 180),
margin: const EdgeInsets.only(right: 6),
width: i == _page ? 22 : 7,
height: 7,
decoration: BoxDecoration(
color: i == _page ? AppColors.heartRed : AppColors.line,
borderRadius: BorderRadius.circular(99)),
),
const Spacer(),
FilledButton(
onPressed: () {
if (_page == items.length - 1) {
Navigator.of(context).pop();
} else {
_controller.nextPage(
duration: const Duration(milliseconds: 250),
curve: Curves.easeOutCubic);
}
},
child: Text(_page == items.length - 1
? l10n.t('onboardingStart')
: l10n.t('onboardingNext')),
),
],
),
],
),
),
);
}
}

