add language section for onboarding
This commit is contained in:
parent
9a4c51dafb
commit
4bc390c3e3
|
|
@ -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,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -62,6 +62,11 @@ class HuntState extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
}
|
||||
|
||||
void deselectCard() {
|
||||
_selectedCard = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void startHunt() {
|
||||
if (_selectedCard != null) {
|
||||
_huntStartTime = DateTime.now();
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Reference in New Issue