Ferish Wheel and Music

This commit is contained in:
efrilm 2025-09-18 14:53:39 +07:00
parent 73918430b2
commit 909c312af0
28 changed files with 970 additions and 1422 deletions

View File

@ -0,0 +1,42 @@
import 'package:bloc/bloc.dart';
import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:injectable/injectable.dart';
import '../../../domain/game/game.dart';
part 'ferris_wheel_loader_event.dart';
part 'ferris_wheel_loader_state.dart';
part 'ferris_wheel_loader_bloc.freezed.dart';
@injectable
class FerrisWheelLoaderBloc
extends Bloc<FerrisWheelLoaderEvent, FerrisWheelLoaderState> {
final IGameRepository _repository;
FerrisWheelLoaderBloc(this._repository)
: super(FerrisWheelLoaderState.initial()) {
on<FerrisWheelLoaderEvent>(_onFerrisWheelLoaderEvent);
}
Future<void> _onFerrisWheelLoaderEvent(
FerrisWheelLoaderEvent event,
Emitter<FerrisWheelLoaderState> emit,
) {
return event.map(
fetched: (e) async {
emit(
state.copyWith(isFetching: true, failureOptionFerrisWheel: none()),
);
final result = await _repository.ferrisWheel();
var data = result.fold(
(f) => state.copyWith(failureOptionFerrisWheel: optionOf(f)),
(ferrisWheel) => state.copyWith(ferrisWheel: ferrisWheel),
);
emit(data.copyWith(isFetching: false));
},
);
}
}

View File

@ -0,0 +1,388 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'ferris_wheel_loader_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models',
);
/// @nodoc
mixin _$FerrisWheelLoaderEvent {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? fetched,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Fetched value) fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Fetched value)? fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Fetched value)? fetched,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $FerrisWheelLoaderEventCopyWith<$Res> {
factory $FerrisWheelLoaderEventCopyWith(
FerrisWheelLoaderEvent value,
$Res Function(FerrisWheelLoaderEvent) then,
) = _$FerrisWheelLoaderEventCopyWithImpl<$Res, FerrisWheelLoaderEvent>;
}
/// @nodoc
class _$FerrisWheelLoaderEventCopyWithImpl<
$Res,
$Val extends FerrisWheelLoaderEvent
>
implements $FerrisWheelLoaderEventCopyWith<$Res> {
_$FerrisWheelLoaderEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of FerrisWheelLoaderEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$FetchedImplCopyWith<$Res> {
factory _$$FetchedImplCopyWith(
_$FetchedImpl value,
$Res Function(_$FetchedImpl) then,
) = __$$FetchedImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$FetchedImplCopyWithImpl<$Res>
extends _$FerrisWheelLoaderEventCopyWithImpl<$Res, _$FetchedImpl>
implements _$$FetchedImplCopyWith<$Res> {
__$$FetchedImplCopyWithImpl(
_$FetchedImpl _value,
$Res Function(_$FetchedImpl) _then,
) : super(_value, _then);
/// Create a copy of FerrisWheelLoaderEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$FetchedImpl implements _Fetched {
const _$FetchedImpl();
@override
String toString() {
return 'FerrisWheelLoaderEvent.fetched()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$FetchedImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({required TResult Function() fetched}) {
return fetched();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({TResult? Function()? fetched}) {
return fetched?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? fetched,
required TResult orElse(),
}) {
if (fetched != null) {
return fetched();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Fetched value) fetched,
}) {
return fetched(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Fetched value)? fetched,
}) {
return fetched?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Fetched value)? fetched,
required TResult orElse(),
}) {
if (fetched != null) {
return fetched(this);
}
return orElse();
}
}
abstract class _Fetched implements FerrisWheelLoaderEvent {
const factory _Fetched() = _$FetchedImpl;
}
/// @nodoc
mixin _$FerrisWheelLoaderState {
Game get ferrisWheel => throw _privateConstructorUsedError;
Option<GameFailure> get failureOptionFerrisWheel =>
throw _privateConstructorUsedError;
bool get isFetching => throw _privateConstructorUsedError;
/// Create a copy of FerrisWheelLoaderState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$FerrisWheelLoaderStateCopyWith<FerrisWheelLoaderState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $FerrisWheelLoaderStateCopyWith<$Res> {
factory $FerrisWheelLoaderStateCopyWith(
FerrisWheelLoaderState value,
$Res Function(FerrisWheelLoaderState) then,
) = _$FerrisWheelLoaderStateCopyWithImpl<$Res, FerrisWheelLoaderState>;
@useResult
$Res call({
Game ferrisWheel,
Option<GameFailure> failureOptionFerrisWheel,
bool isFetching,
});
$GameCopyWith<$Res> get ferrisWheel;
}
/// @nodoc
class _$FerrisWheelLoaderStateCopyWithImpl<
$Res,
$Val extends FerrisWheelLoaderState
>
implements $FerrisWheelLoaderStateCopyWith<$Res> {
_$FerrisWheelLoaderStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of FerrisWheelLoaderState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? ferrisWheel = null,
Object? failureOptionFerrisWheel = null,
Object? isFetching = null,
}) {
return _then(
_value.copyWith(
ferrisWheel: null == ferrisWheel
? _value.ferrisWheel
: ferrisWheel // ignore: cast_nullable_to_non_nullable
as Game,
failureOptionFerrisWheel: null == failureOptionFerrisWheel
? _value.failureOptionFerrisWheel
: failureOptionFerrisWheel // ignore: cast_nullable_to_non_nullable
as Option<GameFailure>,
isFetching: null == isFetching
? _value.isFetching
: isFetching // ignore: cast_nullable_to_non_nullable
as bool,
)
as $Val,
);
}
/// Create a copy of FerrisWheelLoaderState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$GameCopyWith<$Res> get ferrisWheel {
return $GameCopyWith<$Res>(_value.ferrisWheel, (value) {
return _then(_value.copyWith(ferrisWheel: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$FerrisWheelLoaderStateImplCopyWith<$Res>
implements $FerrisWheelLoaderStateCopyWith<$Res> {
factory _$$FerrisWheelLoaderStateImplCopyWith(
_$FerrisWheelLoaderStateImpl value,
$Res Function(_$FerrisWheelLoaderStateImpl) then,
) = __$$FerrisWheelLoaderStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({
Game ferrisWheel,
Option<GameFailure> failureOptionFerrisWheel,
bool isFetching,
});
@override
$GameCopyWith<$Res> get ferrisWheel;
}
/// @nodoc
class __$$FerrisWheelLoaderStateImplCopyWithImpl<$Res>
extends
_$FerrisWheelLoaderStateCopyWithImpl<$Res, _$FerrisWheelLoaderStateImpl>
implements _$$FerrisWheelLoaderStateImplCopyWith<$Res> {
__$$FerrisWheelLoaderStateImplCopyWithImpl(
_$FerrisWheelLoaderStateImpl _value,
$Res Function(_$FerrisWheelLoaderStateImpl) _then,
) : super(_value, _then);
/// Create a copy of FerrisWheelLoaderState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? ferrisWheel = null,
Object? failureOptionFerrisWheel = null,
Object? isFetching = null,
}) {
return _then(
_$FerrisWheelLoaderStateImpl(
ferrisWheel: null == ferrisWheel
? _value.ferrisWheel
: ferrisWheel // ignore: cast_nullable_to_non_nullable
as Game,
failureOptionFerrisWheel: null == failureOptionFerrisWheel
? _value.failureOptionFerrisWheel
: failureOptionFerrisWheel // ignore: cast_nullable_to_non_nullable
as Option<GameFailure>,
isFetching: null == isFetching
? _value.isFetching
: isFetching // ignore: cast_nullable_to_non_nullable
as bool,
),
);
}
}
/// @nodoc
class _$FerrisWheelLoaderStateImpl implements _FerrisWheelLoaderState {
const _$FerrisWheelLoaderStateImpl({
required this.ferrisWheel,
required this.failureOptionFerrisWheel,
this.isFetching = false,
});
@override
final Game ferrisWheel;
@override
final Option<GameFailure> failureOptionFerrisWheel;
@override
@JsonKey()
final bool isFetching;
@override
String toString() {
return 'FerrisWheelLoaderState(ferrisWheel: $ferrisWheel, failureOptionFerrisWheel: $failureOptionFerrisWheel, isFetching: $isFetching)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$FerrisWheelLoaderStateImpl &&
(identical(other.ferrisWheel, ferrisWheel) ||
other.ferrisWheel == ferrisWheel) &&
(identical(
other.failureOptionFerrisWheel,
failureOptionFerrisWheel,
) ||
other.failureOptionFerrisWheel == failureOptionFerrisWheel) &&
(identical(other.isFetching, isFetching) ||
other.isFetching == isFetching));
}
@override
int get hashCode => Object.hash(
runtimeType,
ferrisWheel,
failureOptionFerrisWheel,
isFetching,
);
/// Create a copy of FerrisWheelLoaderState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$FerrisWheelLoaderStateImplCopyWith<_$FerrisWheelLoaderStateImpl>
get copyWith =>
__$$FerrisWheelLoaderStateImplCopyWithImpl<_$FerrisWheelLoaderStateImpl>(
this,
_$identity,
);
}
abstract class _FerrisWheelLoaderState implements FerrisWheelLoaderState {
const factory _FerrisWheelLoaderState({
required final Game ferrisWheel,
required final Option<GameFailure> failureOptionFerrisWheel,
final bool isFetching,
}) = _$FerrisWheelLoaderStateImpl;
@override
Game get ferrisWheel;
@override
Option<GameFailure> get failureOptionFerrisWheel;
@override
bool get isFetching;
/// Create a copy of FerrisWheelLoaderState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$FerrisWheelLoaderStateImplCopyWith<_$FerrisWheelLoaderStateImpl>
get copyWith => throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,6 @@
part of 'ferris_wheel_loader_bloc.dart';
@freezed
class FerrisWheelLoaderEvent with _$FerrisWheelLoaderEvent {
const factory FerrisWheelLoaderEvent.fetched() = _Fetched;
}

View File

@ -0,0 +1,15 @@
part of 'ferris_wheel_loader_bloc.dart';
@freezed
class FerrisWheelLoaderState with _$FerrisWheelLoaderState {
const factory FerrisWheelLoaderState({
required Game ferrisWheel,
required Option<GameFailure> failureOptionFerrisWheel,
@Default(false) bool isFetching,
}) = _FerrisWheelLoaderState;
factory FerrisWheelLoaderState.initial() => FerrisWheelLoaderState(
ferrisWheel: Game.empty(),
failureOptionFerrisWheel: none(),
);
}

View File

@ -1,40 +0,0 @@
import 'package:bloc/bloc.dart';
import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:injectable/injectable.dart';
import '../../../domain/game/game.dart';
part 'game_prize_loader_event.dart';
part 'game_prize_loader_state.dart';
part 'game_prize_loader_bloc.freezed.dart';
@injectable
class GamePrizeLoaderBloc
extends Bloc<GamePrizeLoaderEvent, GamePrizeLoaderState> {
final IGameRepository _repository;
GamePrizeLoaderBloc(this._repository)
: super(GamePrizeLoaderState.initial()) {
on<GamePrizeLoaderEvent>(_onGamePrizeLoaderEvent);
}
Future<void> _onGamePrizeLoaderEvent(
GamePrizeLoaderEvent event,
Emitter<GamePrizeLoaderState> emit,
) {
return event.map(
fetched: (e) async {
emit(state.copyWith(isFetching: true, failureOptionGamePrize: none()));
final result = await _repository.gamePrizeByGameId(id: e.id);
var data = result.fold(
(f) => state.copyWith(failureOptionGamePrize: optionOf(f)),
(gamePrize) => state.copyWith(gamePrize: gamePrize),
);
emit(data.copyWith(isFetching: false));
},
);
}
}

View File

@ -1,442 +0,0 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'game_prize_loader_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models',
);
/// @nodoc
mixin _$GamePrizeLoaderEvent {
String get id => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String id) fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String id)? fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String id)? fetched,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Fetched value) fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Fetched value)? fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Fetched value)? fetched,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
/// Create a copy of GamePrizeLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$GamePrizeLoaderEventCopyWith<GamePrizeLoaderEvent> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $GamePrizeLoaderEventCopyWith<$Res> {
factory $GamePrizeLoaderEventCopyWith(
GamePrizeLoaderEvent value,
$Res Function(GamePrizeLoaderEvent) then,
) = _$GamePrizeLoaderEventCopyWithImpl<$Res, GamePrizeLoaderEvent>;
@useResult
$Res call({String id});
}
/// @nodoc
class _$GamePrizeLoaderEventCopyWithImpl<
$Res,
$Val extends GamePrizeLoaderEvent
>
implements $GamePrizeLoaderEventCopyWith<$Res> {
_$GamePrizeLoaderEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of GamePrizeLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? id = null}) {
return _then(
_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$FetchedImplCopyWith<$Res>
implements $GamePrizeLoaderEventCopyWith<$Res> {
factory _$$FetchedImplCopyWith(
_$FetchedImpl value,
$Res Function(_$FetchedImpl) then,
) = __$$FetchedImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String id});
}
/// @nodoc
class __$$FetchedImplCopyWithImpl<$Res>
extends _$GamePrizeLoaderEventCopyWithImpl<$Res, _$FetchedImpl>
implements _$$FetchedImplCopyWith<$Res> {
__$$FetchedImplCopyWithImpl(
_$FetchedImpl _value,
$Res Function(_$FetchedImpl) _then,
) : super(_value, _then);
/// Create a copy of GamePrizeLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? id = null}) {
return _then(
_$FetchedImpl(
null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
),
);
}
}
/// @nodoc
class _$FetchedImpl implements _Fetched {
const _$FetchedImpl(this.id);
@override
final String id;
@override
String toString() {
return 'GamePrizeLoaderEvent.fetched(id: $id)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$FetchedImpl &&
(identical(other.id, id) || other.id == id));
}
@override
int get hashCode => Object.hash(runtimeType, id);
/// Create a copy of GamePrizeLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$FetchedImplCopyWith<_$FetchedImpl> get copyWith =>
__$$FetchedImplCopyWithImpl<_$FetchedImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String id) fetched,
}) {
return fetched(id);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String id)? fetched,
}) {
return fetched?.call(id);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String id)? fetched,
required TResult orElse(),
}) {
if (fetched != null) {
return fetched(id);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Fetched value) fetched,
}) {
return fetched(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Fetched value)? fetched,
}) {
return fetched?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Fetched value)? fetched,
required TResult orElse(),
}) {
if (fetched != null) {
return fetched(this);
}
return orElse();
}
}
abstract class _Fetched implements GamePrizeLoaderEvent {
const factory _Fetched(final String id) = _$FetchedImpl;
@override
String get id;
/// Create a copy of GamePrizeLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$FetchedImplCopyWith<_$FetchedImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$GamePrizeLoaderState {
List<GamePrize> get gamePrize => throw _privateConstructorUsedError;
Option<GameFailure> get failureOptionGamePrize =>
throw _privateConstructorUsedError;
bool get isFetching => throw _privateConstructorUsedError;
/// Create a copy of GamePrizeLoaderState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$GamePrizeLoaderStateCopyWith<GamePrizeLoaderState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $GamePrizeLoaderStateCopyWith<$Res> {
factory $GamePrizeLoaderStateCopyWith(
GamePrizeLoaderState value,
$Res Function(GamePrizeLoaderState) then,
) = _$GamePrizeLoaderStateCopyWithImpl<$Res, GamePrizeLoaderState>;
@useResult
$Res call({
List<GamePrize> gamePrize,
Option<GameFailure> failureOptionGamePrize,
bool isFetching,
});
}
/// @nodoc
class _$GamePrizeLoaderStateCopyWithImpl<
$Res,
$Val extends GamePrizeLoaderState
>
implements $GamePrizeLoaderStateCopyWith<$Res> {
_$GamePrizeLoaderStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of GamePrizeLoaderState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? gamePrize = null,
Object? failureOptionGamePrize = null,
Object? isFetching = null,
}) {
return _then(
_value.copyWith(
gamePrize: null == gamePrize
? _value.gamePrize
: gamePrize // ignore: cast_nullable_to_non_nullable
as List<GamePrize>,
failureOptionGamePrize: null == failureOptionGamePrize
? _value.failureOptionGamePrize
: failureOptionGamePrize // ignore: cast_nullable_to_non_nullable
as Option<GameFailure>,
isFetching: null == isFetching
? _value.isFetching
: isFetching // ignore: cast_nullable_to_non_nullable
as bool,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$GamePrizeLoaderStateImplCopyWith<$Res>
implements $GamePrizeLoaderStateCopyWith<$Res> {
factory _$$GamePrizeLoaderStateImplCopyWith(
_$GamePrizeLoaderStateImpl value,
$Res Function(_$GamePrizeLoaderStateImpl) then,
) = __$$GamePrizeLoaderStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({
List<GamePrize> gamePrize,
Option<GameFailure> failureOptionGamePrize,
bool isFetching,
});
}
/// @nodoc
class __$$GamePrizeLoaderStateImplCopyWithImpl<$Res>
extends _$GamePrizeLoaderStateCopyWithImpl<$Res, _$GamePrizeLoaderStateImpl>
implements _$$GamePrizeLoaderStateImplCopyWith<$Res> {
__$$GamePrizeLoaderStateImplCopyWithImpl(
_$GamePrizeLoaderStateImpl _value,
$Res Function(_$GamePrizeLoaderStateImpl) _then,
) : super(_value, _then);
/// Create a copy of GamePrizeLoaderState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? gamePrize = null,
Object? failureOptionGamePrize = null,
Object? isFetching = null,
}) {
return _then(
_$GamePrizeLoaderStateImpl(
gamePrize: null == gamePrize
? _value._gamePrize
: gamePrize // ignore: cast_nullable_to_non_nullable
as List<GamePrize>,
failureOptionGamePrize: null == failureOptionGamePrize
? _value.failureOptionGamePrize
: failureOptionGamePrize // ignore: cast_nullable_to_non_nullable
as Option<GameFailure>,
isFetching: null == isFetching
? _value.isFetching
: isFetching // ignore: cast_nullable_to_non_nullable
as bool,
),
);
}
}
/// @nodoc
class _$GamePrizeLoaderStateImpl implements _GamePrizeLoaderState {
const _$GamePrizeLoaderStateImpl({
required final List<GamePrize> gamePrize,
required this.failureOptionGamePrize,
this.isFetching = false,
}) : _gamePrize = gamePrize;
final List<GamePrize> _gamePrize;
@override
List<GamePrize> get gamePrize {
if (_gamePrize is EqualUnmodifiableListView) return _gamePrize;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_gamePrize);
}
@override
final Option<GameFailure> failureOptionGamePrize;
@override
@JsonKey()
final bool isFetching;
@override
String toString() {
return 'GamePrizeLoaderState(gamePrize: $gamePrize, failureOptionGamePrize: $failureOptionGamePrize, isFetching: $isFetching)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$GamePrizeLoaderStateImpl &&
const DeepCollectionEquality().equals(
other._gamePrize,
_gamePrize,
) &&
(identical(other.failureOptionGamePrize, failureOptionGamePrize) ||
other.failureOptionGamePrize == failureOptionGamePrize) &&
(identical(other.isFetching, isFetching) ||
other.isFetching == isFetching));
}
@override
int get hashCode => Object.hash(
runtimeType,
const DeepCollectionEquality().hash(_gamePrize),
failureOptionGamePrize,
isFetching,
);
/// Create a copy of GamePrizeLoaderState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$GamePrizeLoaderStateImplCopyWith<_$GamePrizeLoaderStateImpl>
get copyWith =>
__$$GamePrizeLoaderStateImplCopyWithImpl<_$GamePrizeLoaderStateImpl>(
this,
_$identity,
);
}
abstract class _GamePrizeLoaderState implements GamePrizeLoaderState {
const factory _GamePrizeLoaderState({
required final List<GamePrize> gamePrize,
required final Option<GameFailure> failureOptionGamePrize,
final bool isFetching,
}) = _$GamePrizeLoaderStateImpl;
@override
List<GamePrize> get gamePrize;
@override
Option<GameFailure> get failureOptionGamePrize;
@override
bool get isFetching;
/// Create a copy of GamePrizeLoaderState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$GamePrizeLoaderStateImplCopyWith<_$GamePrizeLoaderStateImpl>
get copyWith => throw _privateConstructorUsedError;
}

View File

@ -1,6 +0,0 @@
part of 'game_prize_loader_bloc.dart';
@freezed
class GamePrizeLoaderEvent with _$GamePrizeLoaderEvent {
const factory GamePrizeLoaderEvent.fetched(String id) = _Fetched;
}

View File

@ -1,13 +0,0 @@
part of 'game_prize_loader_bloc.dart';
@freezed
class GamePrizeLoaderState with _$GamePrizeLoaderState {
const factory GamePrizeLoaderState({
required List<GamePrize> gamePrize,
required Option<GameFailure> failureOptionGamePrize,
@Default(false) bool isFetching,
}) = _GamePrizeLoaderState;
factory GamePrizeLoaderState.initial() =>
GamePrizeLoaderState(gamePrize: [], failureOptionGamePrize: none());
}

View File

@ -1,8 +1,7 @@
// wheel_painter.dart - Fixed implementation with consistent positioning
import 'dart:math' as math;
import 'package:flutter/material.dart';
import '../../domain/game/game.dart';
import '../theme/theme.dart';
class WheelPainter extends CustomPainter {
final List<GamePrize> gamePrizes;
@ -36,10 +35,11 @@ class WheelPainter extends CustomPainter {
..style = PaintingStyle.fill;
canvas.drawCircle(center, radius - 20, innerWhitePaint);
// Draw sections
// Draw sections - KONSISTEN dengan logic spin
for (int i = 0; i < gamePrizes.length; i++) {
final prize = gamePrizes[i];
final startAngle = i * sectionAngle - math.pi / 2;
// Section 0 di top (-π/2), section 1 di kanan atas, dst (clockwise)
final startAngle = (-math.pi / 2) + (i * sectionAngle);
// Section background
final sectionPaint = Paint()
@ -54,54 +54,13 @@ class WheelPainter extends CustomPainter {
sectionPaint,
);
// Draw icon in each section
final iconAngle = startAngle + sectionAngle / 2;
final iconPosition = Offset(
center.dx + (radius - 60) * math.cos(iconAngle),
center.dy + (radius - 60) * math.sin(iconAngle),
);
// Draw icon background circle
final iconBgPaint = Paint()
..color = Colors.white
..style = PaintingStyle.fill;
canvas.drawCircle(iconPosition, 16, iconBgPaint);
// Save canvas state for icon drawing
canvas.save();
canvas.translate(iconPosition.dx, iconPosition.dy);
// Get icon from metadata or use default
final iconData = _getIconFromMetadata(prize.metadata);
final iconText = _getIconText(iconData);
final iconTextPainter = TextPainter(
text: TextSpan(
text: iconText,
style: TextStyle(
fontFamily: 'MaterialIcons',
fontSize: 20,
color: getPrizeColor(prize, i),
fontWeight: FontWeight.normal,
),
),
textDirection: TextDirection.ltr,
);
iconTextPainter.layout();
iconTextPainter.paint(
canvas,
Offset(-iconTextPainter.width / 2, -iconTextPainter.height / 2),
);
canvas.restore();
// Draw prize text
final textAngle = startAngle + sectionAngle / 2;
final textPosition = Offset(
center.dx + (radius - 100) * math.cos(textAngle),
center.dy + (radius - 100) * math.sin(textAngle),
center.dx + (radius - 80) * math.cos(textAngle),
center.dy + (radius - 80) * math.sin(textAngle),
);
// Save canvas state for text rotation
canvas.save();
canvas.translate(textPosition.dx, textPosition.dy);
canvas.rotate(textAngle + math.pi / 2);
@ -111,62 +70,51 @@ class WheelPainter extends CustomPainter {
text: prize.name,
style: const TextStyle(
color: Colors.white,
fontSize: 10,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center,
);
textPainter.layout(maxWidth: 100);
textPainter.paint(
canvas,
Offset(-textPainter.width / 2, -textPainter.height / 2),
);
canvas.restore();
// DEBUG: Draw section number
final numberPosition = Offset(
center.dx + (radius - 50) * math.cos(textAngle),
center.dy + (radius - 50) * math.sin(textAngle),
);
canvas.drawCircle(numberPosition, 15, Paint()..color = Colors.white);
final numberPainter = TextPainter(
text: TextSpan(
text: i.toString(),
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
numberPainter.layout();
numberPainter.paint(
canvas,
Offset(-textPainter.width / 2, -textPainter.height / 2),
Offset(
numberPosition.dx - numberPainter.width / 2,
numberPosition.dy - numberPainter.height / 2,
),
);
canvas.restore();
// Draw stock indicator if stock is low
if (prize.stock <= 5 && prize.stock > 0) {
final stockAngle = startAngle + sectionAngle / 2;
final stockPosition = Offset(
center.dx + (radius - 35) * math.cos(stockAngle),
center.dy + (radius - 35) * math.sin(stockAngle),
);
final stockPaint = Paint()
..color = AppColor.error
..style = PaintingStyle.fill;
canvas.drawCircle(stockPosition, 8, stockPaint);
canvas.save();
canvas.translate(stockPosition.dx, stockPosition.dy);
final stockTextPainter = TextPainter(
text: TextSpan(
text: '!',
style: const TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
);
stockTextPainter.layout();
stockTextPainter.paint(
canvas,
Offset(-stockTextPainter.width / 2, -stockTextPainter.height / 2),
);
canvas.restore();
}
}
// Draw white dots around the outer edge
// Draw white dots
final dotPaint = Paint()
..color = Colors.white
..style = PaintingStyle.fill;
for (int i = 0; i < 24; i++) {
final dotAngle = (2 * math.pi / 24) * i;
final dotPosition = Offset(
@ -178,15 +126,14 @@ class WheelPainter extends CustomPainter {
// Draw section dividers
final dividerPaint = Paint()
..color = AppColor.white
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = 2;
for (int i = 0; i < gamePrizes.length; i++) {
final angle = i * sectionAngle - math.pi / 2;
final angle = (-math.pi / 2) + (i * sectionAngle);
final lineStart = Offset(
center.dx + (radius - 130) * math.cos(angle),
center.dy + (radius - 130) * math.sin(angle),
center.dx + (radius - 110) * math.cos(angle),
center.dy + (radius - 110) * math.sin(angle),
);
final lineEnd = Offset(
center.dx + (radius - 22) * math.cos(angle),
@ -196,64 +143,6 @@ class WheelPainter extends CustomPainter {
}
}
IconData _getIconFromMetadata(Map<String, dynamic> metadata) {
final iconName = metadata['icon'] as String?;
switch (iconName?.toLowerCase()) {
case 'card_giftcard':
return Icons.card_giftcard;
case 'monetization_on':
return Icons.monetization_on;
case 'redeem':
return Icons.redeem;
case 'attach_money':
return Icons.attach_money;
case 'account_balance_wallet':
return Icons.account_balance_wallet;
case 'diamond':
return Icons.diamond;
case 'star':
return Icons.star;
case 'emoji_events':
return Icons.emoji_events;
case 'refresh':
return Icons.refresh;
case 'visibility':
return Icons.visibility;
default:
return Icons.card_giftcard; // Default icon
}
}
String _getIconText(IconData icon) {
// Convert IconData to Unicode string for drawing
switch (icon.codePoint) {
case 0xe8f4: // Icons.visibility
return String.fromCharCode(0xe8f4);
case 0xe8f5: // Icons.visibility_off
return String.fromCharCode(0xe8f5);
case 0xe850: // Icons.account_balance_wallet
return String.fromCharCode(0xe850);
case 0xe151: // Icons.card_giftcard
return String.fromCharCode(0xe151);
case 0xe5d5: // Icons.refresh
return String.fromCharCode(0xe5d5);
case 0xe263: // Icons.attach_money
return String.fromCharCode(0xe263);
case 0xe8a1: // Icons.redeem
return String.fromCharCode(0xe8a1);
case 0xe57d: // Icons.monetization_on
return String.fromCharCode(0xe57d);
case 0xe5ca: // Icons.diamond
return String.fromCharCode(0xe5ca);
case 0xe838: // Icons.star
return String.fromCharCode(0xe838);
case 0xe2a3: // Icons.emoji_events
return String.fromCharCode(0xe2a3);
default:
return String.fromCharCode(0xe151); // Default card_giftcard icon
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

View File

@ -7,7 +7,7 @@ class ApiPath {
static String resend = '/api/v1/customer-auth/resend-otp';
// Marketing
static String gamePrizeByGameId = '/api/v1/marketing/game-prizes/game';
static String ferrisWheel = '/api/v1/customer/ferris-wheel';
// Customer
static String customerPoint = '/api/v1/customer/points';
}

View File

@ -8,6 +8,7 @@ class Game with _$Game {
required String type,
required bool isActive,
required Map<String, dynamic> metadata,
required List<GamePrize> prizes,
required String createdAt,
required String updatedAt,
}) = _Game;
@ -18,6 +19,7 @@ class Game with _$Game {
type: '',
isActive: false,
metadata: {},
prizes: [],
createdAt: '',
updatedAt: '',
);

View File

@ -6,26 +6,16 @@ class GamePrize with _$GamePrize {
required String id,
required String gameId,
required String name,
required int weight,
required int stock,
required int maxStock,
required int threshold,
required Map<String, dynamic> metadata,
required Game game,
required String createdAt,
required String updatedAt,
}) = _GamePrize;
factory GamePrize.empty() => GamePrize(
factory GamePrize.empty() => const GamePrize(
id: '',
gameId: '',
name: '',
weight: 0,
stock: 0,
maxStock: 0,
threshold: 0,
metadata: const {},
game: Game.empty(),
metadata: {},
createdAt: '',
updatedAt: '',
);

View File

@ -22,6 +22,7 @@ mixin _$Game {
String get type => throw _privateConstructorUsedError;
bool get isActive => throw _privateConstructorUsedError;
Map<String, dynamic> get metadata => throw _privateConstructorUsedError;
List<GamePrize> get prizes => throw _privateConstructorUsedError;
String get createdAt => throw _privateConstructorUsedError;
String get updatedAt => throw _privateConstructorUsedError;
@ -42,6 +43,7 @@ abstract class $GameCopyWith<$Res> {
String type,
bool isActive,
Map<String, dynamic> metadata,
List<GamePrize> prizes,
String createdAt,
String updatedAt,
});
@ -67,6 +69,7 @@ class _$GameCopyWithImpl<$Res, $Val extends Game>
Object? type = null,
Object? isActive = null,
Object? metadata = null,
Object? prizes = null,
Object? createdAt = null,
Object? updatedAt = null,
}) {
@ -92,6 +95,10 @@ class _$GameCopyWithImpl<$Res, $Val extends Game>
? _value.metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
prizes: null == prizes
? _value.prizes
: prizes // ignore: cast_nullable_to_non_nullable
as List<GamePrize>,
createdAt: null == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
@ -120,6 +127,7 @@ abstract class _$$GameImplCopyWith<$Res> implements $GameCopyWith<$Res> {
String type,
bool isActive,
Map<String, dynamic> metadata,
List<GamePrize> prizes,
String createdAt,
String updatedAt,
});
@ -142,6 +150,7 @@ class __$$GameImplCopyWithImpl<$Res>
Object? type = null,
Object? isActive = null,
Object? metadata = null,
Object? prizes = null,
Object? createdAt = null,
Object? updatedAt = null,
}) {
@ -167,6 +176,10 @@ class __$$GameImplCopyWithImpl<$Res>
? _value._metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
prizes: null == prizes
? _value._prizes
: prizes // ignore: cast_nullable_to_non_nullable
as List<GamePrize>,
createdAt: null == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
@ -189,9 +202,11 @@ class _$GameImpl implements _Game {
required this.type,
required this.isActive,
required final Map<String, dynamic> metadata,
required final List<GamePrize> prizes,
required this.createdAt,
required this.updatedAt,
}) : _metadata = metadata;
}) : _metadata = metadata,
_prizes = prizes;
@override
final String id;
@ -209,6 +224,14 @@ class _$GameImpl implements _Game {
return EqualUnmodifiableMapView(_metadata);
}
final List<GamePrize> _prizes;
@override
List<GamePrize> get prizes {
if (_prizes is EqualUnmodifiableListView) return _prizes;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_prizes);
}
@override
final String createdAt;
@override
@ -216,7 +239,7 @@ class _$GameImpl implements _Game {
@override
String toString() {
return 'Game(id: $id, name: $name, type: $type, isActive: $isActive, metadata: $metadata, createdAt: $createdAt, updatedAt: $updatedAt)';
return 'Game(id: $id, name: $name, type: $type, isActive: $isActive, metadata: $metadata, prizes: $prizes, createdAt: $createdAt, updatedAt: $updatedAt)';
}
@override
@ -230,6 +253,7 @@ class _$GameImpl implements _Game {
(identical(other.isActive, isActive) ||
other.isActive == isActive) &&
const DeepCollectionEquality().equals(other._metadata, _metadata) &&
const DeepCollectionEquality().equals(other._prizes, _prizes) &&
(identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) &&
(identical(other.updatedAt, updatedAt) ||
@ -244,6 +268,7 @@ class _$GameImpl implements _Game {
type,
isActive,
const DeepCollectionEquality().hash(_metadata),
const DeepCollectionEquality().hash(_prizes),
createdAt,
updatedAt,
);
@ -264,6 +289,7 @@ abstract class _Game implements Game {
required final String type,
required final bool isActive,
required final Map<String, dynamic> metadata,
required final List<GamePrize> prizes,
required final String createdAt,
required final String updatedAt,
}) = _$GameImpl;
@ -279,6 +305,8 @@ abstract class _Game implements Game {
@override
Map<String, dynamic> get metadata;
@override
List<GamePrize> get prizes;
@override
String get createdAt;
@override
String get updatedAt;
@ -296,12 +324,7 @@ mixin _$GamePrize {
String get id => throw _privateConstructorUsedError;
String get gameId => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError;
int get weight => throw _privateConstructorUsedError;
int get stock => throw _privateConstructorUsedError;
int get maxStock => throw _privateConstructorUsedError;
int get threshold => throw _privateConstructorUsedError;
Map<String, dynamic> get metadata => throw _privateConstructorUsedError;
Game get game => throw _privateConstructorUsedError;
String get createdAt => throw _privateConstructorUsedError;
String get updatedAt => throw _privateConstructorUsedError;
@ -321,17 +344,10 @@ abstract class $GamePrizeCopyWith<$Res> {
String id,
String gameId,
String name,
int weight,
int stock,
int maxStock,
int threshold,
Map<String, dynamic> metadata,
Game game,
String createdAt,
String updatedAt,
});
$GameCopyWith<$Res> get game;
}
/// @nodoc
@ -352,12 +368,7 @@ class _$GamePrizeCopyWithImpl<$Res, $Val extends GamePrize>
Object? id = null,
Object? gameId = null,
Object? name = null,
Object? weight = null,
Object? stock = null,
Object? maxStock = null,
Object? threshold = null,
Object? metadata = null,
Object? game = null,
Object? createdAt = null,
Object? updatedAt = null,
}) {
@ -375,30 +386,10 @@ class _$GamePrizeCopyWithImpl<$Res, $Val extends GamePrize>
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
weight: null == weight
? _value.weight
: weight // ignore: cast_nullable_to_non_nullable
as int,
stock: null == stock
? _value.stock
: stock // ignore: cast_nullable_to_non_nullable
as int,
maxStock: null == maxStock
? _value.maxStock
: maxStock // ignore: cast_nullable_to_non_nullable
as int,
threshold: null == threshold
? _value.threshold
: threshold // ignore: cast_nullable_to_non_nullable
as int,
metadata: null == metadata
? _value.metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
game: null == game
? _value.game
: game // ignore: cast_nullable_to_non_nullable
as Game,
createdAt: null == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
@ -411,16 +402,6 @@ class _$GamePrizeCopyWithImpl<$Res, $Val extends GamePrize>
as $Val,
);
}
/// Create a copy of GamePrize
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$GameCopyWith<$Res> get game {
return $GameCopyWith<$Res>(_value.game, (value) {
return _then(_value.copyWith(game: value) as $Val);
});
}
}
/// @nodoc
@ -436,18 +417,10 @@ abstract class _$$GamePrizeImplCopyWith<$Res>
String id,
String gameId,
String name,
int weight,
int stock,
int maxStock,
int threshold,
Map<String, dynamic> metadata,
Game game,
String createdAt,
String updatedAt,
});
@override
$GameCopyWith<$Res> get game;
}
/// @nodoc
@ -467,12 +440,7 @@ class __$$GamePrizeImplCopyWithImpl<$Res>
Object? id = null,
Object? gameId = null,
Object? name = null,
Object? weight = null,
Object? stock = null,
Object? maxStock = null,
Object? threshold = null,
Object? metadata = null,
Object? game = null,
Object? createdAt = null,
Object? updatedAt = null,
}) {
@ -490,30 +458,10 @@ class __$$GamePrizeImplCopyWithImpl<$Res>
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
weight: null == weight
? _value.weight
: weight // ignore: cast_nullable_to_non_nullable
as int,
stock: null == stock
? _value.stock
: stock // ignore: cast_nullable_to_non_nullable
as int,
maxStock: null == maxStock
? _value.maxStock
: maxStock // ignore: cast_nullable_to_non_nullable
as int,
threshold: null == threshold
? _value.threshold
: threshold // ignore: cast_nullable_to_non_nullable
as int,
metadata: null == metadata
? _value._metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
game: null == game
? _value.game
: game // ignore: cast_nullable_to_non_nullable
as Game,
createdAt: null == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
@ -534,12 +482,7 @@ class _$GamePrizeImpl implements _GamePrize {
required this.id,
required this.gameId,
required this.name,
required this.weight,
required this.stock,
required this.maxStock,
required this.threshold,
required final Map<String, dynamic> metadata,
required this.game,
required this.createdAt,
required this.updatedAt,
}) : _metadata = metadata;
@ -550,14 +493,6 @@ class _$GamePrizeImpl implements _GamePrize {
final String gameId;
@override
final String name;
@override
final int weight;
@override
final int stock;
@override
final int maxStock;
@override
final int threshold;
final Map<String, dynamic> _metadata;
@override
Map<String, dynamic> get metadata {
@ -566,8 +501,6 @@ class _$GamePrizeImpl implements _GamePrize {
return EqualUnmodifiableMapView(_metadata);
}
@override
final Game game;
@override
final String createdAt;
@override
@ -575,7 +508,7 @@ class _$GamePrizeImpl implements _GamePrize {
@override
String toString() {
return 'GamePrize(id: $id, gameId: $gameId, name: $name, weight: $weight, stock: $stock, maxStock: $maxStock, threshold: $threshold, metadata: $metadata, game: $game, createdAt: $createdAt, updatedAt: $updatedAt)';
return 'GamePrize(id: $id, gameId: $gameId, name: $name, metadata: $metadata, createdAt: $createdAt, updatedAt: $updatedAt)';
}
@override
@ -586,14 +519,7 @@ class _$GamePrizeImpl implements _GamePrize {
(identical(other.id, id) || other.id == id) &&
(identical(other.gameId, gameId) || other.gameId == gameId) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.weight, weight) || other.weight == weight) &&
(identical(other.stock, stock) || other.stock == stock) &&
(identical(other.maxStock, maxStock) ||
other.maxStock == maxStock) &&
(identical(other.threshold, threshold) ||
other.threshold == threshold) &&
const DeepCollectionEquality().equals(other._metadata, _metadata) &&
(identical(other.game, game) || other.game == game) &&
(identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) &&
(identical(other.updatedAt, updatedAt) ||
@ -606,12 +532,7 @@ class _$GamePrizeImpl implements _GamePrize {
id,
gameId,
name,
weight,
stock,
maxStock,
threshold,
const DeepCollectionEquality().hash(_metadata),
game,
createdAt,
updatedAt,
);
@ -630,12 +551,7 @@ abstract class _GamePrize implements GamePrize {
required final String id,
required final String gameId,
required final String name,
required final int weight,
required final int stock,
required final int maxStock,
required final int threshold,
required final Map<String, dynamic> metadata,
required final Game game,
required final String createdAt,
required final String updatedAt,
}) = _$GamePrizeImpl;
@ -647,18 +563,8 @@ abstract class _GamePrize implements GamePrize {
@override
String get name;
@override
int get weight;
@override
int get stock;
@override
int get maxStock;
@override
int get threshold;
@override
Map<String, dynamic> get metadata;
@override
Game get game;
@override
String get createdAt;
@override
String get updatedAt;

View File

@ -1,7 +1,5 @@
part of '../game.dart';
abstract class IGameRepository {
Future<Either<GameFailure, List<GamePrize>>> gamePrizeByGameId({
required String id,
});
Future<Either<GameFailure, Game>> ferrisWheel();
}

View File

@ -17,12 +17,10 @@ class GameRemoteDataProvider {
GameRemoteDataProvider(this._apiClient);
Future<DC<GameFailure, List<GamePrizeDto>>> gamePrizeByGameId({
required String id,
}) async {
Future<DC<GameFailure, GameDto>> ferrisWheel() async {
try {
final response = await _apiClient.get(
'${ApiPath.gamePrizeByGameId}/$id',
ApiPath.ferrisWheel,
headers: getAuthorizationHeader(),
);
@ -38,12 +36,11 @@ class GameRemoteDataProvider {
);
}
final dto = (response.data['data'] as List)
.map((e) => GamePrizeDto.fromJson(e))
.toList();
final dto = GameDto.fromJson(response.data['data']['data']['game']);
return DC.data(dto);
} on ApiFailure catch (e, s) {
log('gamePrizeByGameId', name: _logName, error: e, stackTrace: s);
log('ferrisWheel', name: _logName, error: e, stackTrace: s);
return DC.error(GameFailure.serverError(e));
}
}

View File

@ -8,6 +8,7 @@ class GameDto with _$GameDto {
@JsonKey(name: 'type') String? type,
@JsonKey(name: 'is_active') bool? isActive,
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
@JsonKey(name: 'prizes') List<GamePrizeDto>? prizes,
@JsonKey(name: 'created_at') String? createdAt,
@JsonKey(name: 'updated_at') String? updatedAt,
}) = _GameDto;
@ -22,7 +23,8 @@ class GameDto with _$GameDto {
name: name ?? '',
type: type ?? '',
isActive: isActive ?? false,
metadata: metadata ?? const {},
metadata: metadata ?? {},
prizes: prizes?.map((e) => e.toDomain()).toList() ?? [],
createdAt: createdAt ?? '',
updatedAt: updatedAt ?? '',
);

View File

@ -6,12 +6,7 @@ class GamePrizeDto with _$GamePrizeDto {
@JsonKey(name: 'id') String? id,
@JsonKey(name: 'game_id') String? gameId,
@JsonKey(name: 'name') String? name,
@JsonKey(name: 'weight') int? weight,
@JsonKey(name: 'stock') int? stock,
@JsonKey(name: 'max_stock') int? maxStock,
@JsonKey(name: 'threshold') int? threshold,
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
@JsonKey(name: 'game') GameDto? game,
@JsonKey(name: 'created_at') String? createdAt,
@JsonKey(name: 'updated_at') String? updatedAt,
}) = _GamePrizeDto;
@ -21,17 +16,11 @@ class GamePrizeDto with _$GamePrizeDto {
const GamePrizeDto._();
/// mapping ke domain
GamePrize toDomain() => GamePrize(
id: id ?? '',
gameId: gameId ?? '',
name: name ?? '',
weight: weight ?? 0,
stock: stock ?? 0,
maxStock: maxStock ?? 0,
threshold: threshold ?? 0,
metadata: metadata ?? const {},
game: game?.toDomain() ?? Game.empty(),
metadata: metadata ?? {},
createdAt: createdAt ?? '',
updatedAt: updatedAt ?? '',
);

View File

@ -31,6 +31,8 @@ mixin _$GameDto {
bool? get isActive => throw _privateConstructorUsedError;
@JsonKey(name: 'metadata')
Map<String, dynamic>? get metadata => throw _privateConstructorUsedError;
@JsonKey(name: 'prizes')
List<GamePrizeDto>? get prizes => throw _privateConstructorUsedError;
@JsonKey(name: 'created_at')
String? get createdAt => throw _privateConstructorUsedError;
@JsonKey(name: 'updated_at')
@ -56,6 +58,7 @@ abstract class $GameDtoCopyWith<$Res> {
@JsonKey(name: 'type') String? type,
@JsonKey(name: 'is_active') bool? isActive,
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
@JsonKey(name: 'prizes') List<GamePrizeDto>? prizes,
@JsonKey(name: 'created_at') String? createdAt,
@JsonKey(name: 'updated_at') String? updatedAt,
});
@ -81,6 +84,7 @@ class _$GameDtoCopyWithImpl<$Res, $Val extends GameDto>
Object? type = freezed,
Object? isActive = freezed,
Object? metadata = freezed,
Object? prizes = freezed,
Object? createdAt = freezed,
Object? updatedAt = freezed,
}) {
@ -106,6 +110,10 @@ class _$GameDtoCopyWithImpl<$Res, $Val extends GameDto>
? _value.metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
prizes: freezed == prizes
? _value.prizes
: prizes // ignore: cast_nullable_to_non_nullable
as List<GamePrizeDto>?,
createdAt: freezed == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
@ -134,6 +142,7 @@ abstract class _$$GameDtoImplCopyWith<$Res> implements $GameDtoCopyWith<$Res> {
@JsonKey(name: 'type') String? type,
@JsonKey(name: 'is_active') bool? isActive,
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
@JsonKey(name: 'prizes') List<GamePrizeDto>? prizes,
@JsonKey(name: 'created_at') String? createdAt,
@JsonKey(name: 'updated_at') String? updatedAt,
});
@ -158,6 +167,7 @@ class __$$GameDtoImplCopyWithImpl<$Res>
Object? type = freezed,
Object? isActive = freezed,
Object? metadata = freezed,
Object? prizes = freezed,
Object? createdAt = freezed,
Object? updatedAt = freezed,
}) {
@ -183,6 +193,10 @@ class __$$GameDtoImplCopyWithImpl<$Res>
? _value._metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
prizes: freezed == prizes
? _value._prizes
: prizes // ignore: cast_nullable_to_non_nullable
as List<GamePrizeDto>?,
createdAt: freezed == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
@ -205,9 +219,11 @@ class _$GameDtoImpl extends _GameDto {
@JsonKey(name: 'type') this.type,
@JsonKey(name: 'is_active') this.isActive,
@JsonKey(name: 'metadata') final Map<String, dynamic>? metadata,
@JsonKey(name: 'prizes') final List<GamePrizeDto>? prizes,
@JsonKey(name: 'created_at') this.createdAt,
@JsonKey(name: 'updated_at') this.updatedAt,
}) : _metadata = metadata,
_prizes = prizes,
super._();
factory _$GameDtoImpl.fromJson(Map<String, dynamic> json) =>
@ -236,6 +252,17 @@ class _$GameDtoImpl extends _GameDto {
return EqualUnmodifiableMapView(value);
}
final List<GamePrizeDto>? _prizes;
@override
@JsonKey(name: 'prizes')
List<GamePrizeDto>? get prizes {
final value = _prizes;
if (value == null) return null;
if (_prizes is EqualUnmodifiableListView) return _prizes;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@override
@JsonKey(name: 'created_at')
final String? createdAt;
@ -245,7 +272,7 @@ class _$GameDtoImpl extends _GameDto {
@override
String toString() {
return 'GameDto(id: $id, name: $name, type: $type, isActive: $isActive, metadata: $metadata, createdAt: $createdAt, updatedAt: $updatedAt)';
return 'GameDto(id: $id, name: $name, type: $type, isActive: $isActive, metadata: $metadata, prizes: $prizes, createdAt: $createdAt, updatedAt: $updatedAt)';
}
@override
@ -259,6 +286,7 @@ class _$GameDtoImpl extends _GameDto {
(identical(other.isActive, isActive) ||
other.isActive == isActive) &&
const DeepCollectionEquality().equals(other._metadata, _metadata) &&
const DeepCollectionEquality().equals(other._prizes, _prizes) &&
(identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) &&
(identical(other.updatedAt, updatedAt) ||
@ -274,6 +302,7 @@ class _$GameDtoImpl extends _GameDto {
type,
isActive,
const DeepCollectionEquality().hash(_metadata),
const DeepCollectionEquality().hash(_prizes),
createdAt,
updatedAt,
);
@ -299,6 +328,7 @@ abstract class _GameDto extends GameDto {
@JsonKey(name: 'type') final String? type,
@JsonKey(name: 'is_active') final bool? isActive,
@JsonKey(name: 'metadata') final Map<String, dynamic>? metadata,
@JsonKey(name: 'prizes') final List<GamePrizeDto>? prizes,
@JsonKey(name: 'created_at') final String? createdAt,
@JsonKey(name: 'updated_at') final String? updatedAt,
}) = _$GameDtoImpl;
@ -322,6 +352,9 @@ abstract class _GameDto extends GameDto {
@JsonKey(name: 'metadata')
Map<String, dynamic>? get metadata;
@override
@JsonKey(name: 'prizes')
List<GamePrizeDto>? get prizes;
@override
@JsonKey(name: 'created_at')
String? get createdAt;
@override
@ -348,18 +381,8 @@ mixin _$GamePrizeDto {
String? get gameId => throw _privateConstructorUsedError;
@JsonKey(name: 'name')
String? get name => throw _privateConstructorUsedError;
@JsonKey(name: 'weight')
int? get weight => throw _privateConstructorUsedError;
@JsonKey(name: 'stock')
int? get stock => throw _privateConstructorUsedError;
@JsonKey(name: 'max_stock')
int? get maxStock => throw _privateConstructorUsedError;
@JsonKey(name: 'threshold')
int? get threshold => throw _privateConstructorUsedError;
@JsonKey(name: 'metadata')
Map<String, dynamic>? get metadata => throw _privateConstructorUsedError;
@JsonKey(name: 'game')
GameDto? get game => throw _privateConstructorUsedError;
@JsonKey(name: 'created_at')
String? get createdAt => throw _privateConstructorUsedError;
@JsonKey(name: 'updated_at')
@ -386,17 +409,10 @@ abstract class $GamePrizeDtoCopyWith<$Res> {
@JsonKey(name: 'id') String? id,
@JsonKey(name: 'game_id') String? gameId,
@JsonKey(name: 'name') String? name,
@JsonKey(name: 'weight') int? weight,
@JsonKey(name: 'stock') int? stock,
@JsonKey(name: 'max_stock') int? maxStock,
@JsonKey(name: 'threshold') int? threshold,
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
@JsonKey(name: 'game') GameDto? game,
@JsonKey(name: 'created_at') String? createdAt,
@JsonKey(name: 'updated_at') String? updatedAt,
});
$GameDtoCopyWith<$Res>? get game;
}
/// @nodoc
@ -417,12 +433,7 @@ class _$GamePrizeDtoCopyWithImpl<$Res, $Val extends GamePrizeDto>
Object? id = freezed,
Object? gameId = freezed,
Object? name = freezed,
Object? weight = freezed,
Object? stock = freezed,
Object? maxStock = freezed,
Object? threshold = freezed,
Object? metadata = freezed,
Object? game = freezed,
Object? createdAt = freezed,
Object? updatedAt = freezed,
}) {
@ -440,30 +451,10 @@ class _$GamePrizeDtoCopyWithImpl<$Res, $Val extends GamePrizeDto>
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String?,
weight: freezed == weight
? _value.weight
: weight // ignore: cast_nullable_to_non_nullable
as int?,
stock: freezed == stock
? _value.stock
: stock // ignore: cast_nullable_to_non_nullable
as int?,
maxStock: freezed == maxStock
? _value.maxStock
: maxStock // ignore: cast_nullable_to_non_nullable
as int?,
threshold: freezed == threshold
? _value.threshold
: threshold // ignore: cast_nullable_to_non_nullable
as int?,
metadata: freezed == metadata
? _value.metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
game: freezed == game
? _value.game
: game // ignore: cast_nullable_to_non_nullable
as GameDto?,
createdAt: freezed == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
@ -476,20 +467,6 @@ class _$GamePrizeDtoCopyWithImpl<$Res, $Val extends GamePrizeDto>
as $Val,
);
}
/// Create a copy of GamePrizeDto
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$GameDtoCopyWith<$Res>? get game {
if (_value.game == null) {
return null;
}
return $GameDtoCopyWith<$Res>(_value.game!, (value) {
return _then(_value.copyWith(game: value) as $Val);
});
}
}
/// @nodoc
@ -505,18 +482,10 @@ abstract class _$$GamePrizeDtoImplCopyWith<$Res>
@JsonKey(name: 'id') String? id,
@JsonKey(name: 'game_id') String? gameId,
@JsonKey(name: 'name') String? name,
@JsonKey(name: 'weight') int? weight,
@JsonKey(name: 'stock') int? stock,
@JsonKey(name: 'max_stock') int? maxStock,
@JsonKey(name: 'threshold') int? threshold,
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
@JsonKey(name: 'game') GameDto? game,
@JsonKey(name: 'created_at') String? createdAt,
@JsonKey(name: 'updated_at') String? updatedAt,
});
@override
$GameDtoCopyWith<$Res>? get game;
}
/// @nodoc
@ -536,12 +505,7 @@ class __$$GamePrizeDtoImplCopyWithImpl<$Res>
Object? id = freezed,
Object? gameId = freezed,
Object? name = freezed,
Object? weight = freezed,
Object? stock = freezed,
Object? maxStock = freezed,
Object? threshold = freezed,
Object? metadata = freezed,
Object? game = freezed,
Object? createdAt = freezed,
Object? updatedAt = freezed,
}) {
@ -559,30 +523,10 @@ class __$$GamePrizeDtoImplCopyWithImpl<$Res>
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String?,
weight: freezed == weight
? _value.weight
: weight // ignore: cast_nullable_to_non_nullable
as int?,
stock: freezed == stock
? _value.stock
: stock // ignore: cast_nullable_to_non_nullable
as int?,
maxStock: freezed == maxStock
? _value.maxStock
: maxStock // ignore: cast_nullable_to_non_nullable
as int?,
threshold: freezed == threshold
? _value.threshold
: threshold // ignore: cast_nullable_to_non_nullable
as int?,
metadata: freezed == metadata
? _value._metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
game: freezed == game
? _value.game
: game // ignore: cast_nullable_to_non_nullable
as GameDto?,
createdAt: freezed == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
@ -603,12 +547,7 @@ class _$GamePrizeDtoImpl extends _GamePrizeDto {
@JsonKey(name: 'id') this.id,
@JsonKey(name: 'game_id') this.gameId,
@JsonKey(name: 'name') this.name,
@JsonKey(name: 'weight') this.weight,
@JsonKey(name: 'stock') this.stock,
@JsonKey(name: 'max_stock') this.maxStock,
@JsonKey(name: 'threshold') this.threshold,
@JsonKey(name: 'metadata') final Map<String, dynamic>? metadata,
@JsonKey(name: 'game') this.game,
@JsonKey(name: 'created_at') this.createdAt,
@JsonKey(name: 'updated_at') this.updatedAt,
}) : _metadata = metadata,
@ -626,18 +565,6 @@ class _$GamePrizeDtoImpl extends _GamePrizeDto {
@override
@JsonKey(name: 'name')
final String? name;
@override
@JsonKey(name: 'weight')
final int? weight;
@override
@JsonKey(name: 'stock')
final int? stock;
@override
@JsonKey(name: 'max_stock')
final int? maxStock;
@override
@JsonKey(name: 'threshold')
final int? threshold;
final Map<String, dynamic>? _metadata;
@override
@JsonKey(name: 'metadata')
@ -649,9 +576,6 @@ class _$GamePrizeDtoImpl extends _GamePrizeDto {
return EqualUnmodifiableMapView(value);
}
@override
@JsonKey(name: 'game')
final GameDto? game;
@override
@JsonKey(name: 'created_at')
final String? createdAt;
@ -661,7 +585,7 @@ class _$GamePrizeDtoImpl extends _GamePrizeDto {
@override
String toString() {
return 'GamePrizeDto(id: $id, gameId: $gameId, name: $name, weight: $weight, stock: $stock, maxStock: $maxStock, threshold: $threshold, metadata: $metadata, game: $game, createdAt: $createdAt, updatedAt: $updatedAt)';
return 'GamePrizeDto(id: $id, gameId: $gameId, name: $name, metadata: $metadata, createdAt: $createdAt, updatedAt: $updatedAt)';
}
@override
@ -672,14 +596,7 @@ class _$GamePrizeDtoImpl extends _GamePrizeDto {
(identical(other.id, id) || other.id == id) &&
(identical(other.gameId, gameId) || other.gameId == gameId) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.weight, weight) || other.weight == weight) &&
(identical(other.stock, stock) || other.stock == stock) &&
(identical(other.maxStock, maxStock) ||
other.maxStock == maxStock) &&
(identical(other.threshold, threshold) ||
other.threshold == threshold) &&
const DeepCollectionEquality().equals(other._metadata, _metadata) &&
(identical(other.game, game) || other.game == game) &&
(identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) &&
(identical(other.updatedAt, updatedAt) ||
@ -693,12 +610,7 @@ class _$GamePrizeDtoImpl extends _GamePrizeDto {
id,
gameId,
name,
weight,
stock,
maxStock,
threshold,
const DeepCollectionEquality().hash(_metadata),
game,
createdAt,
updatedAt,
);
@ -722,12 +634,7 @@ abstract class _GamePrizeDto extends GamePrizeDto {
@JsonKey(name: 'id') final String? id,
@JsonKey(name: 'game_id') final String? gameId,
@JsonKey(name: 'name') final String? name,
@JsonKey(name: 'weight') final int? weight,
@JsonKey(name: 'stock') final int? stock,
@JsonKey(name: 'max_stock') final int? maxStock,
@JsonKey(name: 'threshold') final int? threshold,
@JsonKey(name: 'metadata') final Map<String, dynamic>? metadata,
@JsonKey(name: 'game') final GameDto? game,
@JsonKey(name: 'created_at') final String? createdAt,
@JsonKey(name: 'updated_at') final String? updatedAt,
}) = _$GamePrizeDtoImpl;
@ -746,24 +653,9 @@ abstract class _GamePrizeDto extends GamePrizeDto {
@JsonKey(name: 'name')
String? get name;
@override
@JsonKey(name: 'weight')
int? get weight;
@override
@JsonKey(name: 'stock')
int? get stock;
@override
@JsonKey(name: 'max_stock')
int? get maxStock;
@override
@JsonKey(name: 'threshold')
int? get threshold;
@override
@JsonKey(name: 'metadata')
Map<String, dynamic>? get metadata;
@override
@JsonKey(name: 'game')
GameDto? get game;
@override
@JsonKey(name: 'created_at')
String? get createdAt;
@override

View File

@ -13,6 +13,9 @@ _$GameDtoImpl _$$GameDtoImplFromJson(Map<String, dynamic> json) =>
type: json['type'] as String?,
isActive: json['is_active'] as bool?,
metadata: json['metadata'] as Map<String, dynamic>?,
prizes: (json['prizes'] as List<dynamic>?)
?.map((e) => GamePrizeDto.fromJson(e as Map<String, dynamic>))
.toList(),
createdAt: json['created_at'] as String?,
updatedAt: json['updated_at'] as String?,
);
@ -24,6 +27,7 @@ Map<String, dynamic> _$$GameDtoImplToJson(_$GameDtoImpl instance) =>
'type': instance.type,
'is_active': instance.isActive,
'metadata': instance.metadata,
'prizes': instance.prizes,
'created_at': instance.createdAt,
'updated_at': instance.updatedAt,
};
@ -33,14 +37,7 @@ _$GamePrizeDtoImpl _$$GamePrizeDtoImplFromJson(Map<String, dynamic> json) =>
id: json['id'] as String?,
gameId: json['game_id'] as String?,
name: json['name'] as String?,
weight: (json['weight'] as num?)?.toInt(),
stock: (json['stock'] as num?)?.toInt(),
maxStock: (json['max_stock'] as num?)?.toInt(),
threshold: (json['threshold'] as num?)?.toInt(),
metadata: json['metadata'] as Map<String, dynamic>?,
game: json['game'] == null
? null
: GameDto.fromJson(json['game'] as Map<String, dynamic>),
createdAt: json['created_at'] as String?,
updatedAt: json['updated_at'] as String?,
);
@ -50,12 +47,7 @@ Map<String, dynamic> _$$GamePrizeDtoImplToJson(_$GamePrizeDtoImpl instance) =>
'id': instance.id,
'game_id': instance.gameId,
'name': instance.name,
'weight': instance.weight,
'stock': instance.stock,
'max_stock': instance.maxStock,
'threshold': instance.threshold,
'metadata': instance.metadata,
'game': instance.game,
'created_at': instance.createdAt,
'updated_at': instance.updatedAt,
};

View File

@ -15,21 +15,19 @@ class GameRepository implements IGameRepository {
GameRepository(this._remoteDataProvider);
@override
Future<Either<GameFailure, List<GamePrize>>> gamePrizeByGameId({
required String id,
}) async {
Future<Either<GameFailure, Game>> ferrisWheel() async {
try {
final result = await _remoteDataProvider.gamePrizeByGameId(id: id);
final result = await _remoteDataProvider.ferrisWheel();
if (result.hasError) {
return left(result.error!);
}
final data = result.data!.map((e) => e.toDomain()).toList();
final data = result.data!.toDomain();
return right(data);
} catch (e, s) {
log('gamePrizeByGameId', name: _logName, error: e, stackTrace: s);
log('ferrisWheel', name: _logName, error: e, stackTrace: s);
return left(const GameFailure.unexpectedError());
}
}

View File

@ -28,8 +28,8 @@ import 'package:enaklo/application/auth/verify_form/verify_form_bloc.dart'
as _i521;
import 'package:enaklo/application/customer/customer_point_loader/customer_point_loader_bloc.dart'
as _i497;
import 'package:enaklo/application/game/game_price_loader/game_prize_loader_bloc.dart'
as _i925;
import 'package:enaklo/application/game/ferris_wheel_loader/ferris_wheel_loader_bloc.dart'
as _i1013;
import 'package:enaklo/common/api/api_client.dart' as _i842;
import 'package:enaklo/common/di/di_auto_route.dart' as _i619;
import 'package:enaklo/common/di/di_connectivity.dart' as _i644;
@ -103,8 +103,8 @@ extension GetItInjectableX on _i174.GetIt {
gh.factory<_i96.IGameRepository>(
() => _i547.GameRepository(gh<_i143.GameRemoteDataProvider>()),
);
gh.factory<_i925.GamePrizeLoaderBloc>(
() => _i925.GamePrizeLoaderBloc(gh<_i96.IGameRepository>()),
gh.factory<_i1013.FerrisWheelLoaderBloc>(
() => _i1013.FerrisWheelLoaderBloc(gh<_i96.IGameRepository>()),
);
gh.factory<_i995.IAuthRepository>(
() => _i879.AuthRepository(

View File

@ -6,12 +6,30 @@ import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'dart:math' as math;
import '../../../../application/auth/auth_bloc.dart';
import '../../../../application/game/game_price_loader/game_prize_loader_bloc.dart';
import '../../../../application/game/ferris_wheel_loader/ferris_wheel_loader_bloc.dart';
import '../../../../common/theme/theme.dart';
import '../../../../common/painter/wheel_painter.dart';
import '../../../../injection.dart';
import '../../../../domain/game/game.dart';
import 'data/model.dart';
// Simple models for UI state
class PrizeHistory {
final String prize;
final DateTime dateTime;
final int value;
final Color color;
final IconData icon;
final String? gamePrizeId;
PrizeHistory({
required this.prize,
required this.dateTime,
required this.value,
required this.color,
required this.icon,
this.gamePrizeId,
});
}
@RoutePage()
class FerrisWheelPage extends StatefulWidget implements AutoRouteWrapper {
@ -22,12 +40,9 @@ class FerrisWheelPage extends StatefulWidget implements AutoRouteWrapper {
@override
Widget wrappedRoute(BuildContext context) => BlocProvider(
create: (context) => getIt<GamePrizeLoaderBloc>()
..add(
const GamePrizeLoaderEvent.fetched(
'28d5aed3-4c1b-4b7b-bad5-67cdd4919dc2',
),
),
create: (context) =>
getIt<FerrisWheelLoaderBloc>()
..add(const FerrisWheelLoaderEvent.fetched()),
child: this,
);
}
@ -84,12 +99,10 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
void _initializeAudio() async {
try {
await _bgmPlayer.setSource(
AssetSource('audio/carnival/bgm/carnival_main_theme.mp3'),
);
await _bgmPlayer.setSource(AssetSource('audio/carnaval_main_theme.mp3'));
await _bgmPlayer.setReleaseMode(ReleaseMode.loop);
await _bgmPlayer.setVolume(0.5);
if (_isMusicEnabled) _bgmPlayer.resume();
if (_isMusicEnabled) await _bgmPlayer.resume();
} catch (e) {
print('Error initializing audio: $e');
}
@ -107,23 +120,10 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
}
}
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(GamePrize prize) {
int prizeValue = prize.metadata['value'] ?? prize.weight;
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 _playButtonTap() => _playSound('audio/button_tap.mp3', volume: 0.3);
void _playTokenSound() => _playSound('audio/token_sound.mp3', volume: 0.5);
void _playWheelSpin() => _playSound('audio/wheel_spin.mp3', volume: 0.8);
void _playWinSound() => _playSound('audio/win_medium.mp3', volume: 0.8);
void _toggleSound() {
setState(() => _isSoundEnabled = !_isSoundEnabled);
@ -170,66 +170,48 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
int _selectPrizeWithWeight() {
if (gamePrizes.isEmpty) return 0;
final availablePrizes = gamePrizes
.asMap()
.entries
.where((entry) => entry.value.stock > 0)
.toList();
// Jika GamePrize memiliki properti weight, gunakan ini:
// List<int> weights = gamePrizes.map((prize) => prize.weight ?? 1).toList();
if (availablePrizes.isEmpty) {
return math.Random().nextInt(gamePrizes.length);
// Untuk sementara, gunakan bobot default yang berbeda
// Hadiah langka (index awal) memiliki bobot lebih kecil
List<int> weights = [];
for (int i = 0; i < gamePrizes.length; i++) {
// Bobot menurun: hadiah pertama langka, hadiah terakhir mudah didapat
int weight = gamePrizes.length - i;
weights.add(weight);
}
int totalWeight = availablePrizes
.map((entry) => entry.value.weight)
.reduce((a, b) => a + b);
// Hitung total bobot
int totalWeight = weights.reduce((a, b) => a + b);
if (totalWeight <= 0) {
final randomEntry =
availablePrizes[math.Random().nextInt(availablePrizes.length)];
return randomEntry.key;
}
// Generate random number
int randomNum = math.Random().nextInt(totalWeight);
int randomWeight = math.Random().nextInt(totalWeight);
// Tentukan hadiah berdasarkan bobot
int currentWeight = 0;
for (final entry in availablePrizes) {
currentWeight += entry.value.weight;
if (randomWeight < currentWeight) {
return entry.key;
for (int i = 0; i < gamePrizes.length; i++) {
currentWeight += weights[i];
if (randomNum < currentWeight) {
return i;
}
}
return availablePrizes.last.key;
// Fallback (seharusnya tidak pernah terjadi)
return gamePrizes.length - 1;
}
Color _getPrizeColor(GamePrize prize, int index) {
final colorName = prize.metadata['color'] as String?;
switch (colorName?.toLowerCase()) {
case 'primary':
return AppColor.primary;
case 'info':
return AppColor.info;
case 'warning':
return AppColor.warning;
case 'success':
return AppColor.success;
case 'error':
return AppColor.error;
case 'secondary':
return AppColor.secondary;
default:
final colors = [
AppColor.primary,
AppColor.info,
AppColor.warning,
AppColor.success,
AppColor.primaryDark,
AppColor.secondary,
AppColor.error,
];
return colors[index % colors.length];
}
final colors = [
AppColor.primary,
AppColor.info,
AppColor.warning,
AppColor.success,
AppColor.primaryDark,
AppColor.secondary,
AppColor.error,
];
return colors[index % colors.length];
}
void _spinWheel() {
@ -245,23 +227,25 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
});
_idleRotationController.stop();
_idleRotationController.reset();
int targetSection = _selectPrizeWithWeight();
int selectedPrizeIndex = _selectPrizeWithWeight();
double sectionAngle = (2 * math.pi) / gamePrizes.length;
double targetAngle = (targetSection * sectionAngle) + (sectionAngle / 2);
double baseRotations = 4 + math.Random().nextDouble() * 3;
double currentIdleRotation = _idleRotationAnimation?.value ?? 0.0;
double finalRotation =
currentRotation +
currentIdleRotation +
(baseRotations * 2 * math.pi) +
targetAngle;
double currentPos = currentRotation;
_spinAnimation =
Tween<double>(
begin: currentRotation + currentIdleRotation,
end: finalRotation,
).animate(
// TAMBAH offset ke tengah section (bukan garis)
double targetForSelectedSection =
-(selectedPrizeIndex * sectionAngle) - (sectionAngle / 2);
double spins = 6 * 2 * math.pi;
double finalRotation = currentPos + spins + targetForSelectedSection;
while (finalRotation <= currentPos + spins) {
finalRotation += 2 * math.pi;
}
_spinAnimation = Tween<double>(begin: currentPos, end: finalRotation)
.animate(
CurvedAnimation(
parent: _rotationController,
curve: Curves.easeOutCubic,
@ -270,27 +254,20 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
_rotationController.reset();
_rotationController.animateTo(1.0).then((_) {
final wonPrize = gamePrizes[targetSection];
_playWinSound(wonPrize);
final wonPrize = gamePrizes[selectedPrizeIndex];
_playWinSound();
setState(() {
currentRotation = finalRotation;
isSpinning = false;
resultText = 'Selamat! Anda mendapat ${wonPrize.name}!';
if (wonPrize.stock > 0) {
gamePrizes[targetSection] = wonPrize.copyWith(
stock: wonPrize.stock - 1,
);
}
prizeHistory.insert(
0,
PrizeHistory(
prize: wonPrize.name,
dateTime: DateTime.now(),
value: wonPrize.metadata['value'] ?? wonPrize.weight,
color: _getPrizeColor(wonPrize, targetSection),
value: 100,
color: _getPrizeColor(wonPrize, selectedPrizeIndex),
icon: Icons.card_giftcard,
gamePrizeId: wonPrize.id,
),
@ -298,7 +275,6 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
});
_showWinDialog(wonPrize);
_idleRotationController.reset();
_idleRotationController.repeat();
});
}
@ -363,15 +339,6 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
color: AppColor.textWhite,
),
),
if (wonPrize.stock >= 0) ...[
const SizedBox(height: 8),
Text(
'Stok tersisa: ${wonPrize.stock}',
style: AppStyle.sm.copyWith(
color: AppColor.textWhite.withOpacity(0.8),
),
),
],
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
@ -406,7 +373,7 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
@override
Widget build(BuildContext context) {
return BlocBuilder<GamePrizeLoaderBloc, GamePrizeLoaderState>(
return BlocBuilder<FerrisWheelLoaderBloc, FerrisWheelLoaderState>(
builder: (context, state) {
if (state.isFetching) {
return Scaffold(
@ -425,10 +392,10 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
);
}
if (gamePrizes.isEmpty && state.gamePrize.isNotEmpty) {
if (gamePrizes.isEmpty && state.ferrisWheel.prizes.isNotEmpty) {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
gamePrizes = state.gamePrize;
gamePrizes = state.ferrisWheel.prizes;
});
});
}
@ -445,100 +412,9 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
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,
),
),
),
),
),
],
),
),
_buildHeader(),
_buildTabSelector(),
const SizedBox(height: 20),
// Content Area
Expanded(
child: currentTabIndex == 0
? _buildMainContent()
@ -555,254 +431,256 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
);
}
Widget _buildHeader() {
return 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,
),
),
],
),
);
}
Widget _buildTabSelector() {
return 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,
),
),
),
),
),
],
),
);
}
Widget _buildMainContent() {
return Column(
children: [
// User Info Card
Container(
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColor.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: AppColor.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BlocBuilder<AuthBloc, AuthState>(
builder: (context, auth) {
return Row(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: AppColor.primary,
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: Text(
'G',
style: AppStyle.lg.copyWith(
color: AppColor.textWhite,
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
auth.user.name,
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.textPrimary,
),
),
Text(
auth.user.phoneNumber,
style: AppStyle.sm.copyWith(
color: AppColor.textSecondary,
),
),
],
),
],
);
},
),
Row(
children: [
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: AppColor.warning,
shape: BoxShape.circle,
),
child: Icon(
Icons.circle,
size: 12,
color: AppColor.textWhite,
),
),
const SizedBox(width: 8),
Text(
'Token: $tokens',
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.textPrimary,
),
),
],
),
],
),
),
_buildUserInfoCard(),
Text(
resultText,
style: AppStyle.md.copyWith(color: AppColor.textWhite),
),
const SizedBox(height: 20),
// Wheel Section
Expanded(
child: Center(
child: gamePrizes.isEmpty
? SpinKitFadingCircle(color: AppColor.textWhite, size: 36)
: Stack(
alignment: Alignment.center,
: _buildWheelSection(),
),
),
_buildBottomBanner(),
],
);
}
Widget _buildUserInfoCard() {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColor.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: AppColor.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BlocBuilder<AuthBloc, AuthState>(
builder: (context, auth) {
return Row(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: AppColor.primary,
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: Text(
'G',
style: AppStyle.lg.copyWith(
color: AppColor.textWhite,
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Glow Effect
AnimatedBuilder(
animation: _glowController,
builder: (context, child) => Container(
width: 340,
height: 340,
decoration: BoxDecoration(
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: AppColor.white.withOpacity(
0.3 + 0.2 * _glowController.value,
),
blurRadius: 40,
spreadRadius: 10,
),
],
),
Text(
auth.user.name,
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.textPrimary,
),
),
// Spinning Wheel
AnimatedBuilder(
animation: isSpinning
? _rotationController
: _idleRotationController,
builder: (context, child) {
double rotationAngle = isSpinning
? (_spinAnimation?.value ?? currentRotation)
: (currentRotation +
(_idleRotationAnimation?.value ?? 0.0));
return Transform.rotate(
angle: rotationAngle,
child: CustomPaint(
size: const Size(320, 320),
painter: WheelPainter(
gamePrizes: gamePrizes,
getPrizeColor: _getPrizeColor,
),
),
);
},
),
// Spin Button
AnimatedBuilder(
animation:
_pulseAnimation ??
const AlwaysStoppedAnimation(1.0),
builder: (context, child) => Transform.scale(
scale: _pulseAnimation?.value ?? 1.0,
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [AppColor.warning, AppColor.warning],
),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: AppColor.black.withOpacity(0.3),
blurRadius: 15,
offset: const Offset(0, 6),
),
BoxShadow(
color: AppColor.warning.withOpacity(0.5),
blurRadius: 20,
spreadRadius:
(_pulseAnimation?.value ?? 1.0) * 5,
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(50),
onTap: _spinWheel,
child: Center(
child: Text(
'SPIN',
style: AppStyle.lg.copyWith(
color: AppColor.textWhite,
fontWeight: FontWeight.bold,
letterSpacing: 1,
),
),
),
),
),
),
),
),
// Pointer
Positioned(
top: 30,
child: Container(
width: 0,
height: 0,
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 15,
color: Colors.transparent,
),
right: BorderSide(
width: 15,
color: Colors.transparent,
),
bottom: BorderSide(
width: 30,
color: AppColor.error,
),
),
),
Text(
auth.user.phoneNumber,
style: AppStyle.sm.copyWith(
color: AppColor.textSecondary,
),
),
],
),
],
);
},
),
Row(
children: [
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: AppColor.warning,
shape: BoxShape.circle,
),
child: Icon(Icons.circle, size: 12, color: AppColor.textWhite),
),
const SizedBox(width: 8),
Text(
'Token: $tokens',
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.textPrimary,
),
),
],
),
],
),
);
}
Widget _buildWheelSection() {
return Stack(
alignment: Alignment.center,
children: [
// Glow Effect
AnimatedBuilder(
animation: _glowController,
builder: (context, child) => Container(
width: 340,
height: 340,
decoration: BoxDecoration(
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: AppColor.white.withOpacity(
0.3 + 0.2 * _glowController.value,
),
blurRadius: 40,
spreadRadius: 10,
),
],
),
),
),
// Spinning Wheel
AnimatedBuilder(
animation: isSpinning ? _rotationController : _idleRotationController,
builder: (context, child) {
double rotationAngle = isSpinning
? (_spinAnimation?.value ?? currentRotation)
: (currentRotation + (_idleRotationAnimation?.value ?? 0.0));
// Bottom Banner
Container(
margin: const EdgeInsets.all(20),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [AppColor.warning, AppColor.warning],
),
borderRadius: BorderRadius.circular(15),
),
child: Text(
'Spin 30x lagi buat mainin spesial spin',
textAlign: TextAlign.center,
style: AppStyle.lg.copyWith(
color: AppColor.textWhite,
fontWeight: FontWeight.bold,
return Transform.rotate(
angle: rotationAngle,
child: CustomPaint(
size: const Size(320, 320),
painter: WheelPainter(
gamePrizes: gamePrizes,
getPrizeColor: _getPrizeColor,
),
),
);
},
),
// Spin Button
_buildSpinButton(),
// Pointer
Positioned(
top: 30,
child: Container(
width: 0,
height: 0,
decoration: BoxDecoration(
border: Border(
left: BorderSide(width: 15, color: Colors.transparent),
right: BorderSide(width: 15, color: Colors.transparent),
bottom: BorderSide(width: 30, color: AppColor.error),
),
),
),
),
@ -810,6 +688,73 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
);
}
Widget _buildSpinButton() {
return AnimatedBuilder(
animation: _pulseAnimation ?? const AlwaysStoppedAnimation(1.0),
builder: (context, child) => Transform.scale(
scale: _pulseAnimation?.value ?? 1.0,
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [AppColor.warning, AppColor.warning],
),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: AppColor.black.withOpacity(0.3),
blurRadius: 15,
offset: const Offset(0, 6),
),
BoxShadow(
color: AppColor.warning.withOpacity(0.5),
blurRadius: 20,
spreadRadius: (_pulseAnimation?.value ?? 1.0) * 5,
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(50),
onTap: _spinWheel,
child: Center(
child: Text(
'SPIN',
style: AppStyle.lg.copyWith(
color: AppColor.textWhite,
fontWeight: FontWeight.bold,
letterSpacing: 1,
),
),
),
),
),
),
),
);
}
Widget _buildBottomBanner() {
return Container(
margin: const EdgeInsets.all(20),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: LinearGradient(colors: [AppColor.warning, AppColor.warning]),
borderRadius: BorderRadius.circular(15),
),
child: Text(
'Spin 30x lagi buat mainin spesial spin',
textAlign: TextAlign.center,
style: AppStyle.lg.copyWith(
color: AppColor.textWhite,
fontWeight: FontWeight.bold,
),
),
);
}
Widget _buildPrizeListContent() {
return Container(
margin: const EdgeInsets.all(20),
@ -892,17 +837,15 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
),
),
Text(
'Nilai: ${prize.metadata['value'] ?? prize.weight}',
'ID: ${prize.id}',
style: AppStyle.sm.copyWith(
color: AppColor.textSecondary,
),
),
Text(
'Stok: ${prize.stock}/${prize.maxStock}',
'Game ID: ${prize.gameId}',
style: AppStyle.xs.copyWith(
color: prize.stock > 0
? AppColor.success
: AppColor.error,
color: AppColor.textSecondary,
),
),
],
@ -921,7 +864,7 @@ class _FerrisWheelPageState extends State<FerrisWheelPage>
borderRadius: BorderRadius.circular(15),
),
child: Text(
'Weight: ${prize.weight}',
'Hadiah',
style: AppStyle.xs.copyWith(
color: _getPrizeColor(prize, index),
fontWeight: FontWeight.bold,