ferrish wheel sound

This commit is contained in:
efrilm 2025-09-18 05:18:12 +07:00
parent e4af5f10d7
commit c072e4c168
14 changed files with 376 additions and 279 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,6 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart';
import 'dart:math' as math;
import '../../../../common/theme/theme.dart';
@ -16,23 +17,34 @@ class FerrisWheelPage extends StatefulWidget {
class _FerrisWheelPageState extends State<FerrisWheelPage>
with TickerProviderStateMixin {
// Animation Controllers
late AnimationController _rotationController;
late AnimationController _glowController;
late AnimationController _pulseController;
late AnimationController _idleRotationController;
// Animations
Animation<double>? _spinAnimation;
Animation<double>? _pulseAnimation;
Animation<double>? _idleRotationAnimation;
// Audio Players
final AudioPlayer _bgmPlayer = AudioPlayer();
final AudioPlayer _sfxPlayer = AudioPlayer();
// Audio Settings
bool _isSoundEnabled = true;
bool _isMusicEnabled = true;
// Game State
int tokens = 3;
bool isSpinning = false;
String resultText = 'Belum pernah spin';
double currentRotation = 0.0;
int currentTabIndex = 0;
// Game Data
List<PrizeHistory> prizeHistory = [];
List<WheelSection> wheelSections = [
WheelSection(
color: AppColor.primary,
@ -87,42 +99,8 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
@override
void initState() {
super.initState();
// Initialize animation controllers
_rotationController = AnimationController(
duration: const Duration(seconds: 4),
vsync: this,
);
_glowController = AnimationController(
duration: const Duration(milliseconds: 2000),
vsync: this,
);
_pulseController = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
);
_idleRotationController = AnimationController(
duration: const Duration(seconds: 10), // 10 detik per putaran
vsync: this,
);
// Initialize animations
_pulseAnimation = Tween<double>(begin: 1.0, end: 1.1).animate(
CurvedAnimation(parent: _pulseController, curve: Curves.easeInOut),
);
_idleRotationAnimation = Tween<double>(
begin: 0.0,
end: 2 * math.pi,
).animate(_idleRotationController);
// Start animations
_glowController.repeat(reverse: true);
_pulseController.repeat(reverse: true);
_idleRotationController.repeat();
_initializeAudio();
_initializeAnimations();
}
@override
@ -131,27 +109,113 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
_glowController.dispose();
_pulseController.dispose();
_idleRotationController.dispose();
_bgmPlayer.dispose();
_sfxPlayer.dispose();
super.dispose();
}
void _initializeAudio() async {
try {
await _bgmPlayer.setSource(
AssetSource('audio/carnival/bgm/carnival_main_theme.mp3'),
);
await _bgmPlayer.setReleaseMode(ReleaseMode.loop);
await _bgmPlayer.setVolume(0.5);
if (_isMusicEnabled) _bgmPlayer.resume();
} catch (e) {
print('Error initializing audio: $e');
}
}
void _playSound(String soundPath, {double volume = 0.7}) async {
if (!_isSoundEnabled) return;
try {
await _sfxPlayer.stop();
await _sfxPlayer.setSource(AssetSource(soundPath));
await _sfxPlayer.setVolume(volume);
await _sfxPlayer.resume();
} catch (e) {
print('Error playing sound: $e');
}
}
void _playButtonTap() =>
_playSound('audio/carnival/sfx/button_tap.mp3', volume: 0.3);
void _playTokenSound() =>
_playSound('audio/carnival/sfx/token_sound.mp3', volume: 0.5);
void _playWheelSpin() =>
_playSound('audio/carnival/sfx/wheel_spin.mp3', volume: 0.8);
void _playWinSound(int prizeValue) {
if (prizeValue >= 1000000) {
_playSound('audio/carnival/sfx/win_big.mp3', volume: 0.9);
} else if (prizeValue >= 5000) {
_playSound('audio/carnival/sfx/win_medium.mp3', volume: 0.8);
} else {
_playSound('audio/carnival/sfx/win_small.mp3', volume: 0.7);
}
}
void _toggleSound() {
setState(() => _isSoundEnabled = !_isSoundEnabled);
if (_isSoundEnabled) _playButtonTap();
}
void _toggleMusic() {
setState(() => _isMusicEnabled = !_isMusicEnabled);
_isMusicEnabled ? _bgmPlayer.resume() : _bgmPlayer.pause();
if (_isSoundEnabled) _playButtonTap();
}
void _initializeAnimations() {
_rotationController = AnimationController(
duration: const Duration(seconds: 4),
vsync: this,
);
_glowController = AnimationController(
duration: const Duration(milliseconds: 2000),
vsync: this,
);
_pulseController = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
);
_idleRotationController = AnimationController(
duration: const Duration(seconds: 10),
vsync: this,
);
_pulseAnimation = Tween<double>(begin: 1.0, end: 1.1).animate(
CurvedAnimation(parent: _pulseController, curve: Curves.easeInOut),
);
_idleRotationAnimation = Tween<double>(
begin: 0.0,
end: 2 * math.pi,
).animate(_idleRotationController);
_glowController.repeat(reverse: true);
_pulseController.repeat(reverse: true);
_idleRotationController.repeat();
}
void _spinWheel() {
if (isSpinning || tokens <= 0) return;
_playWheelSpin();
_playTokenSound();
setState(() {
isSpinning = true;
tokens--;
resultText = 'Sedang berputar...';
});
// Stop idle rotation
_idleRotationController.stop();
int targetSection = math.Random().nextInt(8);
double sectionAngle = (2 * math.pi) / 8;
double targetAngle = (targetSection * sectionAngle) + (sectionAngle / 2);
double baseRotations = 4 + math.Random().nextDouble() * 3;
// Calculate current total rotation
double currentIdleRotation = _idleRotationAnimation?.value ?? 0.0;
double finalRotation =
currentRotation +
@ -172,12 +236,13 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
_rotationController.reset();
_rotationController.animateTo(1.0).then((_) {
_playWinSound(wheelSections[targetSection].value);
setState(() {
currentRotation = finalRotation;
isSpinning = false;
resultText =
'Selamat! Anda mendapat ${wheelSections[targetSection].prize}!';
prizeHistory.insert(
0,
PrizeHistory(
@ -191,8 +256,6 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
});
_showWinDialog(wheelSections[targetSection]);
// Restart idle rotation
_idleRotationController.reset();
_idleRotationController.repeat();
});
@ -235,7 +298,7 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
),
const SizedBox(height: 16),
Text(
'🎉 Selamat! 🎉',
'Selamat!',
style: AppStyle.h5.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.textWhite,
@ -256,7 +319,10 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
onPressed: () {
_playButtonTap();
Navigator.of(context).pop();
},
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.white,
foregroundColor: AppColor.primary,
@ -283,9 +349,126 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: AppColor.primaryGradient,
),
),
child: SafeArea(
child: Column(
children: [
// Header
Container(
padding: const EdgeInsets.all(16),
child: Row(
children: [
IconButton(
onPressed: () {
_playButtonTap();
context.router.back();
},
icon: Icon(
Icons.close,
color: AppColor.textWhite,
size: 28,
),
),
Expanded(
child: Text(
'SPIN & WIN',
style: AppStyle.h6.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.textWhite,
letterSpacing: 2,
),
),
),
IconButton(
onPressed: _toggleMusic,
icon: Icon(
_isMusicEnabled ? Icons.volume_up : Icons.volume_off,
color: AppColor.textWhite,
),
),
IconButton(
onPressed: _toggleSound,
icon: Icon(
_isSoundEnabled ? Icons.graphic_eq : Icons.volume_mute,
color: AppColor.textWhite,
),
),
],
),
),
// Tab Selector
Container(
margin: const EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(
color: AppColor.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(25),
),
child: Row(
children: [
for (int i = 0; i < 3; i++)
Expanded(
child: GestureDetector(
onTap: () {
_playButtonTap();
setState(() => currentTabIndex = i);
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration(
color: currentTabIndex == i
? AppColor.white
: Colors.transparent,
borderRadius: BorderRadius.circular(25),
),
child: Text(
['Spin Wheel', 'Daftar Hadiah', 'Riwayat'][i],
textAlign: TextAlign.center,
style: AppStyle.md.copyWith(
fontWeight: FontWeight.bold,
color: currentTabIndex == i
? AppColor.primary
: AppColor.textWhite,
),
),
),
),
),
],
),
),
const SizedBox(height: 20),
// Content Area
Expanded(
child: currentTabIndex == 0
? _buildMainContent()
: currentTabIndex == 1
? _buildPrizeListContent()
: _buildHistoryContent(),
),
],
),
),
),
);
}
Widget _buildMainContent() {
return Column(
children: [
// User Info Card
Container(
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
padding: const EdgeInsets.all(16),
@ -376,18 +559,18 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
resultText,
style: AppStyle.md.copyWith(color: AppColor.textWhite),
),
const SizedBox(height: 20),
// Wheel Section
Expanded(
child: Center(
child: Stack(
alignment: Alignment.center,
children: [
// Glow Effect
AnimatedBuilder(
animation: _glowController,
builder: (context, child) {
return Container(
builder: (context, child) => Container(
width: 340,
height: 340,
decoration: BoxDecoration(
@ -402,23 +585,19 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
),
],
),
);
},
),
),
// Spinning Wheel
AnimatedBuilder(
animation: isSpinning
? _rotationController
: _idleRotationController,
builder: (context, child) {
double rotationAngle;
if (isSpinning) {
rotationAngle = _spinAnimation?.value ?? currentRotation;
} else {
rotationAngle =
currentRotation +
(_idleRotationAnimation?.value ?? 0.0);
}
double rotationAngle = isSpinning
? (_spinAnimation?.value ?? currentRotation)
: (currentRotation +
(_idleRotationAnimation?.value ?? 0.0));
return Transform.rotate(
angle: rotationAngle,
@ -429,19 +608,18 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
);
},
),
// Spin Button
AnimatedBuilder(
animation:
_pulseAnimation ?? const AlwaysStoppedAnimation(1.0),
builder: (context, child) {
return Transform.scale(
builder: (context, child) => Transform.scale(
scale: _pulseAnimation?.value ?? 1.0,
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColor.warning, AppColor.warning],
),
shape: BoxShape.circle,
@ -476,9 +654,10 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
),
),
),
);
},
),
),
// Pointer
Positioned(
top: 30,
child: Container(
@ -498,10 +677,9 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
),
),
// Bottom Banner
Container(
margin: const EdgeInsets.all(20),
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: LinearGradient(
@ -510,7 +688,7 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
borderRadius: BorderRadius.circular(15),
),
child: Text(
'❄️ Spin 30x lagi buat mainin spesial spin',
'Spin 30x lagi buat mainin spesial spin',
textAlign: TextAlign.center,
style: AppStyle.lg.copyWith(
color: AppColor.textWhite,
@ -518,7 +696,6 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
),
),
),
),
],
);
}
@ -527,7 +704,6 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
return Container(
margin: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: double.infinity,
@ -544,7 +720,7 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
],
),
child: Text(
'🎁 Daftar Hadiah',
'Daftar Hadiah',
style: AppStyle.h6.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.textPrimary,
@ -639,7 +815,6 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
return Container(
margin: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: double.infinity,
@ -656,7 +831,7 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
],
),
child: Text(
'📜 Riwayat Hadiah',
'Riwayat Hadiah',
style: AppStyle.h6.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.textPrimary,
@ -773,152 +948,4 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: AppColor.primaryGradient,
),
),
child: SafeArea(
child: Column(
children: [
Container(
padding: const EdgeInsets.all(16),
child: Row(
children: [
IconButton(
onPressed: () => context.router.back(),
icon: Icon(
Icons.close,
color: AppColor.textWhite,
size: 28,
),
),
Expanded(
child: Text(
'SPIN & WIN',
style: AppStyle.h6.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.textWhite,
letterSpacing: 2,
),
),
),
IconButton(
onPressed: () {},
icon: Icon(Icons.volume_up, color: AppColor.textWhite),
),
IconButton(
onPressed: () {},
icon: Icon(Icons.info_outline, color: AppColor.textWhite),
),
],
),
),
Container(
margin: const EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(
color: AppColor.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(25),
),
child: Row(
children: [
Expanded(
child: GestureDetector(
onTap: () => setState(() => currentTabIndex = 0),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration(
color: currentTabIndex == 0
? AppColor.white
: Colors.transparent,
borderRadius: BorderRadius.circular(25),
),
child: Text(
'Spin Wheel',
textAlign: TextAlign.center,
style: AppStyle.md.copyWith(
fontWeight: FontWeight.bold,
color: currentTabIndex == 0
? AppColor.primary
: AppColor.textWhite,
),
),
),
),
),
Expanded(
child: GestureDetector(
onTap: () => setState(() => currentTabIndex = 1),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration(
color: currentTabIndex == 1
? AppColor.white
: Colors.transparent,
borderRadius: BorderRadius.circular(25),
),
child: Text(
'Daftar Hadiah',
textAlign: TextAlign.center,
style: AppStyle.md.copyWith(
fontWeight: FontWeight.bold,
color: currentTabIndex == 1
? AppColor.primary
: AppColor.textWhite,
),
),
),
),
),
Expanded(
child: GestureDetector(
onTap: () => setState(() => currentTabIndex = 2),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration(
color: currentTabIndex == 2
? AppColor.white
: Colors.transparent,
borderRadius: BorderRadius.circular(25),
),
child: Text(
'Riwayat',
textAlign: TextAlign.center,
style: AppStyle.md.copyWith(
fontWeight: FontWeight.bold,
color: currentTabIndex == 2
? AppColor.primary
: AppColor.textWhite,
),
),
),
),
),
],
),
),
const SizedBox(height: 20),
Expanded(
child: currentTabIndex == 0
? _buildMainContent()
: currentTabIndex == 1
? _buildPrizeListContent()
: _buildHistoryContent(),
),
],
),
),
),
);
}
}

View File

@ -6,9 +6,13 @@
#include "generated_plugin_registrant.h"
#include <audioplayers_linux/audioplayers_linux_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);

View File

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_linux
url_launcher_linux
)

View File

@ -5,6 +5,7 @@
import FlutterMacOS
import Foundation
import audioplayers_darwin
import connectivity_plus
import path_provider_foundation
import shared_preferences_foundation
@ -12,6 +13,7 @@ import sqflite_darwin
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))

View File

@ -41,6 +41,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.13.0"
audioplayers:
dependency: "direct main"
description:
name: audioplayers
sha256: "5441fa0ceb8807a5ad701199806510e56afde2b4913d9d17c2f19f2902cf0ae4"
url: "https://pub.dev"
source: hosted
version: "6.5.1"
audioplayers_android:
dependency: transitive
description:
name: audioplayers_android
sha256: "60a6728277228413a85755bd3ffd6fab98f6555608923813ce383b190a360605"
url: "https://pub.dev"
source: hosted
version: "5.2.1"
audioplayers_darwin:
dependency: transitive
description:
name: audioplayers_darwin
sha256: "0811d6924904ca13f9ef90d19081e4a87f7297ddc19fc3d31f60af1aaafee333"
url: "https://pub.dev"
source: hosted
version: "6.3.0"
audioplayers_linux:
dependency: transitive
description:
name: audioplayers_linux
sha256: f75bce1ce864170ef5e6a2c6a61cd3339e1a17ce11e99a25bae4474ea491d001
url: "https://pub.dev"
source: hosted
version: "4.2.1"
audioplayers_platform_interface:
dependency: transitive
description:
name: audioplayers_platform_interface
sha256: "0e2f6a919ab56d0fec272e801abc07b26ae7f31980f912f24af4748763e5a656"
url: "https://pub.dev"
source: hosted
version: "7.1.1"
audioplayers_web:
dependency: transitive
description:
name: audioplayers_web
sha256: "1c0f17cec68455556775f1e50ca85c40c05c714a99c5eb1d2d57cc17ba5522d7"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
audioplayers_windows:
dependency: transitive
description:
name: audioplayers_windows
sha256: "4048797865105b26d47628e6abb49231ea5de84884160229251f37dfcbe52fd7"
url: "https://pub.dev"
source: hosted
version: "4.2.1"
auto_route:
dependency: "direct main"
description:

View File

@ -31,6 +31,7 @@ dependencies:
url_launcher: ^6.3.2
cached_network_image: ^3.4.1
shimmer: ^3.0.0
audioplayers: ^6.5.1
dev_dependencies:
flutter_test:
@ -54,6 +55,8 @@ flutter:
- assets/icons/
- assets/fonts/
- assets/json/
- assets/audio/
fonts:
- family: Quicksand
fonts:

View File

@ -6,10 +6,13 @@
#include "generated_plugin_registrant.h"
#include <audioplayers_windows/audioplayers_windows_plugin.h>
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
AudioplayersWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(

View File

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_windows
connectivity_plus
url_launcher_windows
)