add language section for onboarding

This commit is contained in:
mohamadmahdi jebeli 2025-09-22 09:46:20 +03:30
parent 9a4c51dafb
commit 4bc390c3e3
6 changed files with 248 additions and 54 deletions

View File

@ -1,8 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../extension/screenSize.dart';
import '../../gen/assets.gen.dart';
import '../../res/colors.dart';
import '../../widgets/language_selection_dialog.dart';
import 'login_page.dart';
class OnboardingPage extends StatefulWidget {
@ -16,6 +18,10 @@ class _OnboardingPageState extends State<OnboardingPage> {
int currentIndex = 0;
final PageController _pageController = PageController();
String _currentLanguage = '🇺🇲 English';
String _currentFlag = 'assets/icons/usa circle.svg';
final GlobalKey _languageKey = GlobalKey();
final List<String> imageAssets = [
Assets.images.ounboarding1.path,
Assets.images.frame.path,
@ -35,6 +41,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
curve: Curves.easeInOut,
);
} else {
_markOnboardingComplete();
Navigator.push(
context,
MaterialPageRoute(builder: (context) => LoginPage()),
@ -42,6 +49,16 @@ class _OnboardingPageState extends State<OnboardingPage> {
}
}
Future<void> _markOnboardingComplete() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('hasSeenOnboarding', true);
}
Future<void> _resetOnboarding() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('hasSeenOnboarding');
}
void _back() {
if (currentIndex > 0) {
_pageController.previousPage(
@ -59,6 +76,109 @@ class _OnboardingPageState extends State<OnboardingPage> {
return Scaffold(
body: Column(
children: [
Container(
padding: EdgeInsets.fromLTRB(
width / 15,
height / 20,
width / 15,
0,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 50),
GestureDetector(
key: _languageKey,
onTap: () {
showLanguageSelectionOverlay(context, _currentLanguage, (
language,
flag,
) {
setState(() {
_currentLanguage = language;
_currentFlag = flag;
});
}, _languageKey);
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: ClipRRect(
key: ValueKey(_currentFlag),
borderRadius: BorderRadius.circular(4),
child:
_currentFlag == 'placeholder'
? Container(
width: 24,
height: 18,
decoration: BoxDecoration(
color: AppColors.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(4),
),
child: Icon(
Icons.flag,
color: AppColors.primary,
size: 12,
),
)
: _currentFlag.endsWith('.svg')
? SvgPicture.asset(
_currentFlag,
width: 24,
height: 18,
)
: Image.asset(
_currentFlag,
width: 24,
height: 18,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
width: 24,
height: 18,
decoration: BoxDecoration(
color: AppColors.greyBorder
.withOpacity(0.2),
borderRadius: BorderRadius.circular(
4,
),
),
child: Icon(
Icons.flag,
color: AppColors.textSecondary,
size: 12,
),
);
},
),
),
),
const SizedBox(width: 8),
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: Text(
_currentLanguage,
key: ValueKey(_currentLanguage),
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: AppColors.textPrimary,
),
),
),
const SizedBox(width: 8),
SvgPicture.asset(
Assets.icons.arrowRight.path,
color: AppColors.textPrimary,
),
],
),
),
],
),
),
Expanded(
child: PageView.builder(
controller: _pageController,
@ -72,7 +192,8 @@ class _OnboardingPageState extends State<OnboardingPage> {
return Padding(
padding: EdgeInsets.fromLTRB(
width / 15,
height / 40,
height /
60,
width / 15,
height / 30,
),

View File

@ -115,6 +115,19 @@ class _HuntContentState extends State<_HuntContent> with TickerProviderStateMixi
void _onCardSelected(HuntCard card) async {
final huntProvider = Provider.of<HuntState>(context, listen: false);
// If the same card is selected, deselect it
if (huntProvider.selectedCard?.id == card.id) {
// Play sound for deselection too
await GameSoundService.playCardFlipSound();
Vibration.hasVibrator().then((hasVibrator) {
if (hasVibrator == true) {
Vibration.vibrate(duration: 50); // Lighter vibration for deselect
}
});
huntProvider.deselectCard();
return;
}
await GameSoundService.playCardFlipSound();
Vibration.hasVibrator().then((hasVibrator) {
if (hasVibrator == true) {
@ -243,6 +256,7 @@ class _HuntContentState extends State<_HuntContent> with TickerProviderStateMixi
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: HuntCardWidget(
key: ValueKey(card.id),
card: card,
onTap: () => _onCardSelected(card),
isSelected: huntProvider.selectedCard?.id == card.id,

View File

@ -62,6 +62,11 @@ class HuntState extends ChangeNotifier {
notifyListeners();
}
void deselectCard() {
_selectedCard = null;
notifyListeners();
}
void startHunt() {
if (_selectedCard != null) {
_huntStartTime = DateTime.now();

View File

@ -84,7 +84,21 @@ class _HuntCardWidgetState extends State<HuntCardWidget>
@override
void didUpdateWidget(HuntCardWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.isFlipped != oldWidget.isFlipped) {
// Handle selection state changes first
if (widget.isSelected != oldWidget.isSelected) {
if (widget.isSelected) {
// Card is being selected
_flipController.forward();
} else {
// Card is being deselected - animate back to front
_flipController.reverse();
_scaleController.reverse();
}
}
// Handle flip animation based on isFlipped property if not already handled by selection
if (widget.isFlipped != oldWidget.isFlipped && widget.isSelected == oldWidget.isSelected) {
if (widget.isFlipped) {
_flipController.forward();
} else {

View File

@ -3,12 +3,42 @@ import 'package:flutter/material.dart';
import 'package:lba/screens/auth/login_page.dart';
import 'package:lba/screens/auth/onboarding_page.dart';
import 'package:lba/screens/mains/navigation/navigation.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SimpleAuthGate extends StatelessWidget {
class SimpleAuthGate extends StatefulWidget {
const SimpleAuthGate({super.key});
@override
State<SimpleAuthGate> createState() => _SimpleAuthGateState();
}
class _SimpleAuthGateState extends State<SimpleAuthGate> {
bool? _isFirstTime;
@override
void initState() {
super.initState();
_checkFirstTime();
}
Future<void> _checkFirstTime() async {
final prefs = await SharedPreferences.getInstance();
final hasSeenOnboarding = prefs.getBool('hasSeenOnboarding') ?? false;
setState(() {
_isFirstTime = !hasSeenOnboarding;
});
}
@override
Widget build(BuildContext context) {
if (_isFirstTime == null) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
@ -42,12 +72,8 @@ class SimpleAuthGate extends StatelessWidget {
return const MainScreen();
}
return _shouldShowOnboarding() ? const OnboardingPage() : const LoginPage();
return _isFirstTime! ? const OnboardingPage() : const LoginPage();
},
);
}
bool _shouldShowOnboarding() {
return false;
}
}

View File

@ -105,6 +105,17 @@ class _LanguageSelectionOverlayState extends State<LanguageSelectionOverlay>
final targetPosition = _getTargetPosition();
final screenWidth = MediaQuery.of(context).size.width;
final overlayWidth = screenWidth * 0.45;
double? leftPosition;
double? rightPosition;
if (targetPosition.dx > screenWidth * 0.7) {
rightPosition = 16;
} else {
rightPosition = 24;
}
return AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
@ -113,21 +124,23 @@ class _LanguageSelectionOverlayState extends State<LanguageSelectionOverlay>
child: GestureDetector(
onTap: _closeOverlay,
behavior: HitTestBehavior.translucent,
child: Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
width: double.infinity,
height: double.infinity,
child: Stack(
children: [
Positioned(
right: targetPosition.dx + 10,
left: leftPosition,
right: rightPosition,
top: targetPosition.dy + 40,
child: Transform.scale(
scale: _scaleAnimation.value,
child: Opacity(
opacity: _opacityAnimation.value,
child: Container(
width:
screenWidth * 0.45, // Less than half screen width
width: overlayWidth,
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.only(
@ -166,6 +179,7 @@ class _LanguageSelectionOverlayState extends State<LanguageSelectionOverlay>
),
),
),
),
);
},
);
@ -177,7 +191,7 @@ class _LanguageSelectionOverlayState extends State<LanguageSelectionOverlay>
String flag,
bool isSelected,
) {
print('Building language option: $name with flag: $flag'); // Debug log
print('Building language option: $name with flag: $flag');
return Material(
color: Colors.transparent,
child: InkWell(