feat: add carpet
This commit is contained in:
@@ -8,7 +8,7 @@ class AppConfig {
|
||||
/// با تغییر شبکه/IP مک، مقدار dart-define یا همین پیشفرض را عوض کنید.
|
||||
static const String baseUrl = String.fromEnvironment(
|
||||
'BASE_URL',
|
||||
defaultValue: 'http://192.168.8.210:8080',
|
||||
defaultValue: 'http://192.168.1.105:8080',
|
||||
);
|
||||
|
||||
static String get apiUrl => '$baseUrl/api';
|
||||
|
||||
@@ -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/carpet_api_provider.dart';
|
||||
import '../../feature/shop/data/repository/shop_repository_impl.dart';
|
||||
import '../../feature/shop/domain/repository/shop_repository.dart';
|
||||
import '../../feature/shop/domain/use_cases/ad_reward_usecase.dart';
|
||||
@@ -61,6 +62,7 @@ Future<void> setupLocator() async {
|
||||
locator.registerSingleton<AuthApiProvider>(AuthApiProvider());
|
||||
locator.registerSingleton<WalletApiProvider>(WalletApiProvider());
|
||||
locator.registerSingleton<ShopApiProvider>(ShopApiProvider());
|
||||
locator.registerSingleton<CarpetApiProvider>(CarpetApiProvider());
|
||||
locator.registerSingleton<ProfileApiProvider>(ProfileApiProvider());
|
||||
locator.registerSingleton<RankedApiProvider>(RankedApiProvider());
|
||||
locator.registerSingleton<ChatApiProvider>(ChatApiProvider());
|
||||
|
||||
@@ -57,6 +57,22 @@ class CardImages {
|
||||
}
|
||||
}
|
||||
|
||||
/// تصویرِ فرش (برای سطحِ میز) از backend: `/carpets/<id>.jpg`. کششده.
|
||||
static Future<Sprite?> carpet(String id) {
|
||||
return _cache.putIfAbsent('carpet/$id', () async {
|
||||
try {
|
||||
final res = await _dio
|
||||
.get<List<int>>('${AppConfig.baseUrl}/carpets/$id.jpg');
|
||||
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');
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static Future<ui.Image> _decode(Uint8List bytes) async {
|
||||
final codec = await ui.instantiateImageCodec(bytes);
|
||||
final frame = await codec.getNextFrame();
|
||||
|
||||
@@ -53,11 +53,17 @@ class AppTheme {
|
||||
final base = ThemeData.dark(useMaterial3: true);
|
||||
return base.copyWith(
|
||||
scaffoldBackgroundColor: AppColors.bg,
|
||||
primaryColor: AppColors.accent,
|
||||
primaryColor: AppColors.lapis,
|
||||
canvasColor: AppColors.bg,
|
||||
cardColor: AppColors.panel,
|
||||
dividerColor: AppColors.goldFaint,
|
||||
colorScheme: base.colorScheme.copyWith(
|
||||
primary: AppColors.gold,
|
||||
secondary: AppColors.accent,
|
||||
onPrimary: AppColors.lapisInk,
|
||||
secondary: AppColors.lapis,
|
||||
tertiary: AppColors.accent,
|
||||
surface: AppColors.panel,
|
||||
onSurface: AppColors.text,
|
||||
),
|
||||
textTheme: base.textTheme.apply(
|
||||
fontFamily: fontFamily,
|
||||
@@ -69,6 +75,36 @@ class AppTheme {
|
||||
foregroundColor: AppColors.gold,
|
||||
centerTitle: true,
|
||||
),
|
||||
// دیالوگ/منو/اسنکبار با همان قابِ لاجورد + طلا.
|
||||
dialogTheme: DialogThemeData(
|
||||
backgroundColor: AppColors.panel,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
side: const BorderSide(color: AppColors.gold, width: 1.5),
|
||||
),
|
||||
titleTextStyle: const TextStyle(
|
||||
fontFamily: fontFamily,
|
||||
color: AppColors.gold,
|
||||
fontSize: 19,
|
||||
fontWeight: FontWeight.bold),
|
||||
contentTextStyle:
|
||||
const TextStyle(fontFamily: fontFamily, color: AppColors.text),
|
||||
),
|
||||
snackBarTheme: const SnackBarThemeData(
|
||||
backgroundColor: AppColors.lapisMid,
|
||||
contentTextStyle: TextStyle(fontFamily: fontFamily, color: AppColors.text),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
tabBarTheme: const TabBarThemeData(
|
||||
labelColor: AppColors.gold,
|
||||
unselectedLabelColor: Colors.white60,
|
||||
indicatorColor: AppColors.gold,
|
||||
),
|
||||
bottomSheetTheme: const BottomSheetThemeData(
|
||||
backgroundColor: AppColors.panel,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
filled: true,
|
||||
fillColor: AppColors.bgDark.withValues(alpha: 0.6),
|
||||
|
||||
@@ -1,8 +1,41 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../service/app_sounds.dart';
|
||||
import '../theme/app_theme.dart';
|
||||
|
||||
/// دکمهی بازگشتِ سراسری: قابِ طلایی هماهنگ با تمِ بازی. اگر [onTap] داده نشود،
|
||||
/// بهصورت پیشفرض یک صفحه به عقب میرود (context.pop()).
|
||||
class AppBackButton extends StatelessWidget {
|
||||
final VoidCallback? onTap;
|
||||
final double size;
|
||||
const AppBackButton({super.key, this.onTap, this.size = 46});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
AppSounds.button();
|
||||
if (onTap != null) {
|
||||
onTap!();
|
||||
} else {
|
||||
context.pop();
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
width: size,
|
||||
height: size,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.panel,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColors.gold, width: 1.5),
|
||||
),
|
||||
child: const Icon(Icons.arrow_back, color: AppColors.gold),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// مجموعهی ویجتهای گرافیکیِ مشترک برای ظاهرِ بازیِ (Unity-style):
|
||||
/// پسزمینهی گرادیانی، پنل براق، دکمهی جواهرگون، و عنوانِ درخشان.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user