feat: add characters
This commit is contained in:
@@ -31,6 +31,7 @@ import '../../feature/ranked/domain/repository/ranked_repository.dart';
|
||||
import '../../feature/ranked/domain/use_cases/get_leaderboard_usecase.dart';
|
||||
import '../../feature/ranked/presentation/bloc/leaderboard_bloc.dart';
|
||||
import '../../feature/shop/data/data_source/remote/shop_api_provider.dart';
|
||||
import '../../feature/shop/data/data_source/remote/avatar_api_provider.dart';
|
||||
import '../../feature/shop/data/data_source/remote/carpet_api_provider.dart';
|
||||
import '../../feature/shop/data/data_source/remote/frame_api_provider.dart';
|
||||
import '../../feature/tournament/data/data_source/remote/tournament_api_provider.dart';
|
||||
@@ -71,6 +72,7 @@ Future<void> setupLocator() async {
|
||||
locator.registerSingleton<ShopApiProvider>(ShopApiProvider());
|
||||
locator.registerSingleton<CarpetApiProvider>(CarpetApiProvider());
|
||||
locator.registerSingleton<FrameApiProvider>(FrameApiProvider());
|
||||
locator.registerSingleton<AvatarApiProvider>(AvatarApiProvider());
|
||||
locator.registerSingleton<ProfileApiProvider>(ProfileApiProvider());
|
||||
locator.registerSingleton<RankedApiProvider>(RankedApiProvider());
|
||||
locator.registerSingleton<ChatApiProvider>(ChatApiProvider());
|
||||
|
||||
@@ -57,17 +57,17 @@ class CardImages {
|
||||
}
|
||||
}
|
||||
|
||||
/// تصویرِ فرش (برای سطحِ میز) از backend: `/carpets/<id>.jpg`. کششده.
|
||||
static Future<Sprite?> carpet(String id) {
|
||||
return _cache.putIfAbsent('carpet/$id', () async {
|
||||
/// تصویرِ فرش (برای سطحِ میز) از URL کاملِ backend. کششده با کلیدِ URL.
|
||||
static Future<Sprite?> carpetByUrl(String url) {
|
||||
if (url.isEmpty) return Future.value(null);
|
||||
return _cache.putIfAbsent('carpet/$url', () async {
|
||||
try {
|
||||
final res = await _dio
|
||||
.get<List<int>>('${AppConfig.baseUrl}/carpets/$id.jpg');
|
||||
final res = await _dio.get<List<int>>(url);
|
||||
final data = res.data;
|
||||
if (data == null || data.isEmpty) return null;
|
||||
return Sprite(await _decode(Uint8List.fromList(data)));
|
||||
} catch (e) {
|
||||
debugPrint('CardImages: carpet/$id failed: $e');
|
||||
debugPrint('CardImages: carpet $url failed: $e');
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:random_avatar/random_avatar.dart';
|
||||
|
||||
/// آواتارِ یک بازیکن در کلِ بازی: اگر شخصیتِ آواتاری خریده و انتخاب کرده باشد
|
||||
/// ([imageUrl] یک URL کاملِ svg/png از سرور) همان را نشان میدهد؛ وگرنه به آواتارِ
|
||||
/// تصادفیِ پیشفرض بر اساسِ [seed] برمیگردد.
|
||||
///
|
||||
/// URLِ تصویر همیشه از backend میآید (URL-safe)؛ کلاینت آن را نمیسازد. یک منبعِ
|
||||
/// واحد برای رندرِ آواتار در سرِ میز، پروفایل، لابی و فروشگاه (svg با flutter_svg،
|
||||
/// png/jpg با Image.network).
|
||||
class PlayerAvatar extends StatelessWidget {
|
||||
final String seed; // seed آواتارِ تصادفی (fallback)
|
||||
final String imageUrl; // URL کاملِ تصویرِ شخصیت (از سرور) — خالی ⇒ تصادفی
|
||||
final double? size; // اگر null، فضای موجود را پر میکند
|
||||
|
||||
const PlayerAvatar({
|
||||
super.key,
|
||||
required this.seed,
|
||||
this.imageUrl = '',
|
||||
this.size,
|
||||
});
|
||||
|
||||
bool get _hasCharacter => imageUrl.isNotEmpty;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Widget child;
|
||||
if (_hasCharacter) {
|
||||
child =
|
||||
imageUrl.toLowerCase().endsWith('.svg')
|
||||
? SvgPicture.network(
|
||||
imageUrl,
|
||||
fit: BoxFit.cover,
|
||||
placeholderBuilder: (_) => _fallback(),
|
||||
)
|
||||
: Image.network(
|
||||
imageUrl,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => _fallback(),
|
||||
);
|
||||
} else {
|
||||
child = _fallback();
|
||||
}
|
||||
final clipped = ClipOval(child: SizedBox.expand(child: child));
|
||||
if (size == null) return clipped;
|
||||
return SizedBox(width: size, height: size, child: clipped);
|
||||
}
|
||||
|
||||
Widget _fallback() => RandomAvatar(seed.isEmpty ? 'hakem-1' : seed);
|
||||
}
|
||||
Reference in New Issue
Block a user