318 lines
12 KiB
Dart
318 lines
12 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:tugas_akhir_supabase/services/auth_services.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:tugas_akhir_supabase/di/service_locator.dart';
|
|
import 'package:audioplayers/audioplayers.dart';
|
|
import 'package:tugas_akhir_supabase/services/session_manager.dart';
|
|
import 'dart:async';
|
|
// Import HomeScreen
|
|
|
|
class SplashScreen extends StatefulWidget {
|
|
const SplashScreen({super.key});
|
|
|
|
@override
|
|
_SplashScreenState createState() => _SplashScreenState();
|
|
}
|
|
|
|
class _SplashScreenState extends State<SplashScreen>
|
|
with SingleTickerProviderStateMixin {
|
|
late AnimationController _animationController;
|
|
late Animation<double> _fadeAnimation;
|
|
late Animation<double> _scaleAnimation;
|
|
late Animation<double> _slideAnimation;
|
|
late AuthServices _authServices;
|
|
final AudioPlayer _audioPlayer = AudioPlayer();
|
|
bool _isAudioPlayed = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
// Get auth service instance using the sl instance from service_locator.dart
|
|
try {
|
|
_authServices = sl<AuthServices>();
|
|
} catch (e) {
|
|
debugPrint('Error getting AuthServices from GetIt: $e');
|
|
// Create a local instance as fallback if GetIt fails
|
|
_authServices = AuthServices();
|
|
}
|
|
|
|
_animationController = AnimationController(
|
|
duration: const Duration(milliseconds: 2000), // Increased duration
|
|
vsync: this,
|
|
);
|
|
|
|
_fadeAnimation = TweenSequence<double>([
|
|
TweenSequenceItem(tween: Tween<double>(begin: 0.0, end: 1.0), weight: 20),
|
|
TweenSequenceItem(tween: Tween<double>(begin: 1.0, end: 1.0), weight: 60),
|
|
TweenSequenceItem(tween: Tween<double>(begin: 1.0, end: 0.0), weight: 20),
|
|
]).animate(
|
|
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
|
|
);
|
|
|
|
_scaleAnimation = TweenSequence<double>([
|
|
TweenSequenceItem(
|
|
tween: Tween<double>(
|
|
begin: 0.5,
|
|
end: 1.0,
|
|
).chain(CurveTween(curve: Curves.elasticOut)),
|
|
weight: 40,
|
|
),
|
|
TweenSequenceItem(tween: Tween<double>(begin: 1.0, end: 1.0), weight: 40),
|
|
TweenSequenceItem(tween: Tween<double>(begin: 1.0, end: 1.2), weight: 20),
|
|
]).animate(_animationController);
|
|
|
|
_slideAnimation = Tween<double>(begin: 50.0, end: 0.0).animate(
|
|
CurvedAnimation(
|
|
parent: _animationController,
|
|
curve: const Interval(0.0, 0.5, curve: Curves.easeOut),
|
|
),
|
|
);
|
|
|
|
// Delay animation start for native splash to finish properly
|
|
Future.delayed(const Duration(milliseconds: 900), () {
|
|
if (mounted) {
|
|
_animationController.forward();
|
|
}
|
|
});
|
|
|
|
// Play sound after a short delay
|
|
Timer(const Duration(milliseconds: 1000), () {
|
|
_playDeepMaleVoice();
|
|
});
|
|
|
|
_animationController.addStatusListener((status) {
|
|
if (status == AnimationStatus.completed) {
|
|
// Langsung navigasi tanpa delay tambahan
|
|
if (mounted) {
|
|
_checkAuthAndNavigate();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
Future<void> _playDeepMaleVoice() async {
|
|
if (_isAudioPlayed) return;
|
|
_isAudioPlayed = true;
|
|
|
|
debugPrint('Playing intro voice...');
|
|
|
|
try {
|
|
// Play the custom intro voice with delayed initialization
|
|
Future.delayed(const Duration(milliseconds: 200), () async {
|
|
try {
|
|
await _audioPlayer.setReleaseMode(ReleaseMode.release);
|
|
|
|
// Gunakan metode yang lebih sederhana untuk audio
|
|
try {
|
|
// Try to play custom intro voice
|
|
debugPrint('Playing introVoice.mp3...');
|
|
await _audioPlayer.play(
|
|
AssetSource('audio/introVoice.mp3'),
|
|
volume: 0.8,
|
|
);
|
|
debugPrint('Intro voice started playing');
|
|
} catch (assetError) {
|
|
debugPrint('Could not play intro voice: $assetError');
|
|
|
|
// Fallback to regular welcome audio only
|
|
try {
|
|
debugPrint('Falling back to regular welcome audio...');
|
|
await _audioPlayer.play(
|
|
AssetSource('audio/welcome.mp3'),
|
|
volume: 0.8,
|
|
);
|
|
debugPrint('Regular welcome audio started playing');
|
|
} catch (welcomeError) {
|
|
debugPrint('Could not play welcome audio: $welcomeError');
|
|
// Don't try URL audio as it can cause connectivity issues
|
|
}
|
|
}
|
|
} catch (e) {
|
|
debugPrint('Audio initialization error: $e');
|
|
}
|
|
});
|
|
} catch (e) {
|
|
debugPrint('Error in audio playback: $e');
|
|
}
|
|
}
|
|
|
|
// Improved auth checking with timeout handling
|
|
Future<void> _checkAuthAndNavigate() async {
|
|
try {
|
|
// Check if user is logged in AND session is valid (not timed out)
|
|
final isLoggedIn = _authServices.isUserLoggedIn();
|
|
final isSessionValid = SessionManager.isAuthenticated;
|
|
|
|
if (!mounted) return;
|
|
|
|
// Menghapus delay tambahan
|
|
// Langsung navigasi
|
|
|
|
if (isLoggedIn && isSessionValid) {
|
|
// Valid session, navigate to home
|
|
Navigator.pushReplacementNamed(context, '/home');
|
|
} else {
|
|
// Session expired or no session, go to intro
|
|
Navigator.pushReplacementNamed(context, '/intro');
|
|
}
|
|
} catch (e) {
|
|
// Handle any errors by directing to login
|
|
if (mounted) {
|
|
debugPrint('Auth error in splash screen: $e');
|
|
Navigator.pushReplacementNamed(context, '/intro');
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_animationController.dispose();
|
|
_audioPlayer.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: Colors.black,
|
|
resizeToAvoidBottomInset: true,
|
|
body: SafeArea(
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topCenter,
|
|
end: Alignment.bottomCenter,
|
|
colors: [
|
|
Colors.black,
|
|
Colors.black87,
|
|
const Color(0xFF056839).withOpacity(0.3),
|
|
],
|
|
stops: const [0.0, 0.5, 1.0],
|
|
),
|
|
),
|
|
child: AnimatedBuilder(
|
|
animation: _animationController,
|
|
builder: (context, child) {
|
|
return Opacity(
|
|
opacity: _fadeAnimation.value,
|
|
child: Transform.scale(
|
|
scale: _scaleAnimation.value,
|
|
child: Center(
|
|
child: SingleChildScrollView(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Transform.translate(
|
|
offset: Offset(0, _slideAnimation.value),
|
|
child: Container(
|
|
width: 180,
|
|
height: 180,
|
|
decoration: BoxDecoration(
|
|
color: Colors.transparent,
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Stack(
|
|
alignment: Alignment.center,
|
|
children: [
|
|
Container(
|
|
width: 160,
|
|
height: 160,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [
|
|
const Color(
|
|
0xFF056839,
|
|
).withOpacity(0.8),
|
|
const Color(0xFF056839),
|
|
],
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: const Color(
|
|
0xFF056839,
|
|
).withOpacity(0.3),
|
|
blurRadius: 20,
|
|
spreadRadius: 5,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Image.asset(
|
|
'assets/images/logo.png',
|
|
width: 120,
|
|
height: 120,
|
|
color: Colors.white,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 40),
|
|
Transform.translate(
|
|
offset: Offset(0, _slideAnimation.value),
|
|
child: Text(
|
|
'TaniSM4RT',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 48,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
letterSpacing: 2,
|
|
shadows: [
|
|
Shadow(
|
|
color: const Color(
|
|
0xFF056839,
|
|
).withOpacity(0.5),
|
|
offset: const Offset(0, 4),
|
|
blurRadius: 15,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
Transform.translate(
|
|
offset: Offset(0, _slideAnimation.value),
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 20,
|
|
vertical: 8,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF056839).withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(30),
|
|
border: Border.all(
|
|
color: const Color(
|
|
0xFF056839,
|
|
).withOpacity(0.3),
|
|
width: 1,
|
|
),
|
|
),
|
|
child: Text(
|
|
'Smart Farming Solution',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 16,
|
|
color: Colors.white.withOpacity(0.9),
|
|
letterSpacing: 1,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|