chore: change Flutter build mode to release in local.properties and add an empty line to JSON response schema
This commit is contained in:
193
APP_SIZE_OPTIMIZATION.md
Normal file
193
APP_SIZE_OPTIMIZATION.md
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
# راهنمای کاهش حجم اپلیکیشن Flutter
|
||||||
|
|
||||||
|
## چرا حجم اپ زیاد میشه؟
|
||||||
|
|
||||||
|
### 1. **Assets (تصاویر و آیکونها)**
|
||||||
|
- هر فایل تصویر/آیکون که از Figma اضافه میکنید، مستقیماً به حجم اپ اضافه میشه
|
||||||
|
- در حال حاضر شما **119 SVG** و **119 VEC** دارید (احتمالاً تکراری!)
|
||||||
|
- همه assets در `pubspec.yaml` به صورت کلی اضافه شدن (`assets/icons/`)
|
||||||
|
|
||||||
|
### 2. **کد Dart**
|
||||||
|
- خود کد Dart حجم کمی داره
|
||||||
|
- اما dependencies و packages حجم زیادی اضافه میکنن
|
||||||
|
- کدهای generate شده (freezed, json_serializable) هم حجم دارن
|
||||||
|
|
||||||
|
### 3. **مشکلات فعلی پروژه:**
|
||||||
|
- ✅ **تکرار assets**: هم SVG و هم VEC دارید
|
||||||
|
- ❌ **عدم بهینهسازی تصاویر**: PNG به جای WebP
|
||||||
|
- ❌ **شامل شدن همه assets**: حتی اونایی که استفاده نمیشن
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## راهحلها
|
||||||
|
|
||||||
|
### ✅ 1. حذف Assets تکراری
|
||||||
|
|
||||||
|
**مشکل**: شما هم `assets/icons/*.svg` و هم `assets/vec/*.svg.vec` دارید
|
||||||
|
|
||||||
|
**راهحل**:
|
||||||
|
- اگر از VEC استفاده میکنید، SVG ها رو حذف کنید
|
||||||
|
- یا برعکس، اگر SVG استفاده میکنید، VEC ها رو حذف کنید
|
||||||
|
|
||||||
|
### ✅ 2. بهینهسازی تصاویر
|
||||||
|
|
||||||
|
**قبل از اضافه کردن از Figma:**
|
||||||
|
1. تصاویر رو به **WebP** تبدیل کنید (حجم 30-50% کمتر)
|
||||||
|
2. از ابزارهای فشردهسازی استفاده کنید:
|
||||||
|
- [TinyPNG](https://tinypng.com/) برای PNG
|
||||||
|
- [Squoosh](https://squoosh.app/) برای همه فرمتها
|
||||||
|
|
||||||
|
**تبدیل PNG به WebP:**
|
||||||
|
```bash
|
||||||
|
# نصب cwebp (Google WebP tools)
|
||||||
|
# سپس:
|
||||||
|
cwebp -q 80 input.png -o output.webp
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ 3. حذف Assets استفاده نشده
|
||||||
|
|
||||||
|
**استفاده از ابزار:**
|
||||||
|
```bash
|
||||||
|
# نصب flutter_unused_assets
|
||||||
|
dart pub global activate flutter_unused_assets
|
||||||
|
|
||||||
|
# بررسی assets استفاده نشده
|
||||||
|
flutter_unused_assets
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ 4. بهینهسازی pubspec.yaml
|
||||||
|
|
||||||
|
**به جای:**
|
||||||
|
```yaml
|
||||||
|
assets:
|
||||||
|
- assets/icons/ # همه فایلها
|
||||||
|
- assets/images/
|
||||||
|
```
|
||||||
|
|
||||||
|
**استفاده کنید:**
|
||||||
|
```yaml
|
||||||
|
assets:
|
||||||
|
- assets/icons/add.svg # فقط فایلهای استفاده شده
|
||||||
|
- assets/icons/home.svg
|
||||||
|
- assets/images/inner_splash.webp
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ 5. استفاده از Asset Variants
|
||||||
|
|
||||||
|
برای تصاویر بزرگ، از variants استفاده کنید:
|
||||||
|
```
|
||||||
|
assets/images/
|
||||||
|
splash.png # برای density 1.0
|
||||||
|
2.0x/splash.png # برای density 2.0
|
||||||
|
3.0x/splash.png # برای density 3.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ 6. Lazy Loading برای Assets بزرگ
|
||||||
|
|
||||||
|
برای تصاویر بزرگ که همیشه استفاده نمیشن:
|
||||||
|
```dart
|
||||||
|
// به جای Image.asset
|
||||||
|
FutureBuilder(
|
||||||
|
future: rootBundle.load('assets/images/large_image.webp'),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
return Image.memory(snapshot.data!);
|
||||||
|
}
|
||||||
|
return CircularProgressIndicator();
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ 7. بهینهسازی Build
|
||||||
|
|
||||||
|
در `android/app/build.gradle.kts` شما این تنظیمات رو دارید (خوبه!):
|
||||||
|
```kotlin
|
||||||
|
isMinifyEnabled = true
|
||||||
|
isShrinkResources = true
|
||||||
|
```
|
||||||
|
|
||||||
|
اما میتونید اضافه کنید:
|
||||||
|
```kotlin
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
// ...
|
||||||
|
// اضافه کردن این خط:
|
||||||
|
isDebuggable = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## چکلیست قبل از اضافه کردن Asset از Figma
|
||||||
|
|
||||||
|
- [ ] آیا این asset قبلاً وجود داره؟
|
||||||
|
- [ ] آیا واقعاً نیاز به این asset دارم؟
|
||||||
|
- [ ] آیا میتونم از asset موجود استفاده کنم؟
|
||||||
|
- [ ] آیا تصویر رو به WebP تبدیل کردم؟
|
||||||
|
- [ ] آیا تصویر رو فشرده کردم؟
|
||||||
|
- [ ] آیا فقط فایلهای لازم رو به pubspec.yaml اضافه کردم؟
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ابزارهای مفید
|
||||||
|
|
||||||
|
1. **بررسی حجم اپ:**
|
||||||
|
```bash
|
||||||
|
flutter build apk --analyze-size
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **بررسی Assets استفاده نشده:**
|
||||||
|
```bash
|
||||||
|
flutter_unused_assets
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **فشردهسازی تصاویر:**
|
||||||
|
- [TinyPNG](https://tinypng.com/)
|
||||||
|
- [Squoosh](https://squoosh.app/)
|
||||||
|
- [ImageOptim](https://imageoptim.com/)
|
||||||
|
|
||||||
|
4. **تبدیل فرمت:**
|
||||||
|
- PNG → WebP: [CloudConvert](https://cloudconvert.com/)
|
||||||
|
- SVG → Optimized SVG: [SVGOMG](https://jakearchibald.github.io/svgomg/)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## نکات مهم
|
||||||
|
|
||||||
|
1. **همیشه از WebP استفاده کنید** به جای PNG/JPG (حجم 30-50% کمتر)
|
||||||
|
2. **SVG ها رو optimize کنید** قبل از اضافه کردن
|
||||||
|
3. **Assets استفاده نشده رو حذف کنید** به صورت منظم
|
||||||
|
4. **از Asset Variants استفاده کنید** برای تصاویر با resolution بالا
|
||||||
|
5. **Build Release رو بررسی کنید** نه Debug (Debug حجم بیشتری داره)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## مثال: کاهش حجم
|
||||||
|
|
||||||
|
**قبل:**
|
||||||
|
- 119 SVG × 10KB = ~1.2 MB
|
||||||
|
- 119 VEC × 8KB = ~950 KB
|
||||||
|
- **جمع: ~2.15 MB فقط برای آیکونها!**
|
||||||
|
|
||||||
|
**بعد از بهینهسازی:**
|
||||||
|
- حذف تکرار: فقط 119 فایل = ~1 MB
|
||||||
|
- بهینهسازی SVG: ~500 KB
|
||||||
|
- **صرفهجویی: ~1.65 MB!**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## سوالات متداول
|
||||||
|
|
||||||
|
**Q: چرا با اضافه کردن 50 صفحه Dart حجم زیاد میشه؟**
|
||||||
|
A: خود کد Dart حجم کمی داره، اما:
|
||||||
|
- Dependencies جدید اضافه میشن
|
||||||
|
- Assets جدید برای صفحات اضافه میشن
|
||||||
|
- Build artifacts بیشتر میشن
|
||||||
|
|
||||||
|
**Q: آیا باید همه assets رو حذف کنم؟**
|
||||||
|
A: نه! فقط اونایی که استفاده نمیشن رو حذف کنید.
|
||||||
|
|
||||||
|
**Q: چطور بفهمم کدوم assets استفاده نمیشن؟**
|
||||||
|
A: از `flutter_unused_assets` استفاده کنید یا به صورت دستی جستجو کنید.
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
sdk.dir=C:\\Users\\Housh11\\AppData\\Local\\Android\\sdk
|
sdk.dir=C:\\Users\\Housh11\\AppData\\Local\\Android\\sdk
|
||||||
flutter.sdk=C:\\src\\flutter
|
flutter.sdk=C:\\src\\flutter
|
||||||
flutter.buildMode=debug
|
flutter.buildMode=release
|
||||||
flutter.versionName=1.3.41
|
flutter.versionName=1.3.41
|
||||||
flutter.versionCode=37
|
flutter.versionCode=37
|
||||||
@@ -430,3 +430,4 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
100
tools/check_unused_assets.dart
Normal file
100
tools/check_unused_assets.dart
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/// اسکریپت برای بررسی Assets استفاده نشده
|
||||||
|
///
|
||||||
|
/// استفاده:
|
||||||
|
/// dart run tools/check_unused_assets.dart
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
print('🔍 در حال بررسی Assets استفاده نشده...\n');
|
||||||
|
|
||||||
|
// لیست تمام assets
|
||||||
|
final assets = <String>[];
|
||||||
|
|
||||||
|
// خواندن assets از پوشهها
|
||||||
|
final assetDirs = [
|
||||||
|
'assets/icons',
|
||||||
|
'assets/images',
|
||||||
|
'assets/logos',
|
||||||
|
'assets/vec',
|
||||||
|
'assets/anim',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (final dir in assetDirs) {
|
||||||
|
final directory = Directory(dir);
|
||||||
|
if (await directory.exists()) {
|
||||||
|
await for (final entity in directory.list(recursive: true)) {
|
||||||
|
if (entity is File) {
|
||||||
|
final relativePath = entity.path.replaceAll('\\', '/');
|
||||||
|
assets.add(relativePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print('📦 تعداد کل Assets: ${assets.length}\n');
|
||||||
|
|
||||||
|
// جستجو در کد
|
||||||
|
final usedAssets = <String>{};
|
||||||
|
final codeFiles = <File>[];
|
||||||
|
|
||||||
|
// جستجو در پوشه lib و packages
|
||||||
|
await _findDartFiles(Directory('lib'), codeFiles);
|
||||||
|
await _findDartFiles(Directory('packages'), codeFiles);
|
||||||
|
|
||||||
|
print('📝 در حال جستجو در ${codeFiles.length} فایل Dart...\n');
|
||||||
|
|
||||||
|
for (final file in codeFiles) {
|
||||||
|
final content = await file.readAsString();
|
||||||
|
|
||||||
|
for (final asset in assets) {
|
||||||
|
// استخراج نام فایل از مسیر
|
||||||
|
final fileName = asset.split('/').last;
|
||||||
|
final fileNameWithoutExt = fileName.split('.').first;
|
||||||
|
|
||||||
|
// جستجوی استفاده در کد
|
||||||
|
if (content.contains(asset) ||
|
||||||
|
content.contains(fileName) ||
|
||||||
|
content.contains(fileNameWithoutExt) ||
|
||||||
|
content.contains('Assets.${_getAssetCategory(asset)}')) {
|
||||||
|
usedAssets.add(asset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// پیدا کردن assets استفاده نشده
|
||||||
|
final unusedAssets = assets.where((asset) => !usedAssets.contains(asset)).toList();
|
||||||
|
|
||||||
|
print('✅ Assets استفاده شده: ${usedAssets.length}');
|
||||||
|
print('❌ Assets استفاده نشده: ${unusedAssets.length}\n');
|
||||||
|
|
||||||
|
if (unusedAssets.isNotEmpty) {
|
||||||
|
print('⚠️ لیست Assets استفاده نشده:\n');
|
||||||
|
for (final asset in unusedAssets) {
|
||||||
|
print(' - $asset');
|
||||||
|
}
|
||||||
|
print('\n💡 میتونید این فایلها رو حذف کنید تا حجم اپ کم بشه.');
|
||||||
|
} else {
|
||||||
|
print('🎉 همه Assets استفاده میشن!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _findDartFiles(Directory dir, List<File> files) async {
|
||||||
|
if (!await dir.exists()) return;
|
||||||
|
|
||||||
|
await for (final entity in dir.list(recursive: true)) {
|
||||||
|
if (entity is File && entity.path.endsWith('.dart')) {
|
||||||
|
files.add(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getAssetCategory(String assetPath) {
|
||||||
|
if (assetPath.contains('icons/')) return 'icons';
|
||||||
|
if (assetPath.contains('images/')) return 'images';
|
||||||
|
if (assetPath.contains('logos/')) return 'logos';
|
||||||
|
if (assetPath.contains('vec/')) return 'vec';
|
||||||
|
if (assetPath.contains('anim/')) return 'anim';
|
||||||
|
return 'unknown';
|
||||||
|
}
|
||||||
|
|
||||||
80
tools/find_duplicate_assets.dart
Normal file
80
tools/find_duplicate_assets.dart
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/// اسکریپت برای پیدا کردن Assets تکراری (SVG و VEC)
|
||||||
|
///
|
||||||
|
/// استفاده:
|
||||||
|
/// dart run tools/find_duplicate_assets.dart
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
print('🔍 در حال بررسی Assets تکراری...\n');
|
||||||
|
|
||||||
|
final svgFiles = <String>[];
|
||||||
|
final vecFiles = <String>[];
|
||||||
|
|
||||||
|
// خواندن SVG files
|
||||||
|
final iconsDir = Directory('assets/icons');
|
||||||
|
if (await iconsDir.exists()) {
|
||||||
|
await for (final entity in iconsDir.list()) {
|
||||||
|
if (entity is File && entity.path.endsWith('.svg')) {
|
||||||
|
final fileName = entity.path.split(Platform.pathSeparator).last;
|
||||||
|
svgFiles.add(fileName.replaceAll('.svg', ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// خواندن VEC files
|
||||||
|
final vecDir = Directory('assets/vec');
|
||||||
|
if (await vecDir.exists()) {
|
||||||
|
await for (final entity in vecDir.list()) {
|
||||||
|
if (entity is File && entity.path.endsWith('.vec')) {
|
||||||
|
final fileName = entity.path.split(Platform.pathSeparator).last;
|
||||||
|
vecFiles.add(fileName.replaceAll('.svg.vec', '').replaceAll('.vec', ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print('📊 آمار:');
|
||||||
|
print(' SVG files: ${svgFiles.length}');
|
||||||
|
print(' VEC files: ${vecFiles.length}\n');
|
||||||
|
|
||||||
|
// پیدا کردن تکراریها
|
||||||
|
final duplicates = <String>[];
|
||||||
|
for (final svg in svgFiles) {
|
||||||
|
if (vecFiles.contains(svg)) {
|
||||||
|
duplicates.add(svg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duplicates.isNotEmpty) {
|
||||||
|
print('⚠️ پیدا شد ${duplicates.length} asset تکراری:\n');
|
||||||
|
for (final dup in duplicates) {
|
||||||
|
print(' - $dup');
|
||||||
|
print(' 📄 assets/icons/$dup.svg');
|
||||||
|
print(' 📄 assets/vec/$dup.svg.vec\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
print('💡 پیشنهاد:');
|
||||||
|
print(' اگر از VEC استفاده میکنید، SVG ها رو حذف کنید');
|
||||||
|
print(' یا برعکس، اگر از SVG استفاده میکنید، VEC ها رو حذف کنید');
|
||||||
|
print(' این کار میتونه حجم اپ رو تا 50% کاهش بده!\n');
|
||||||
|
|
||||||
|
// محاسبه حجم تقریبی
|
||||||
|
print('📦 حجم تقریبی قابل صرفهجویی:');
|
||||||
|
print(' ${duplicates.length} فایل × ~10KB = ~${(duplicates.length * 10 / 1024).toStringAsFixed(2)} MB');
|
||||||
|
} else {
|
||||||
|
print('✅ هیچ asset تکراری پیدا نشد!');
|
||||||
|
}
|
||||||
|
|
||||||
|
// پیدا کردن SVG های بدون VEC
|
||||||
|
final svgOnly = svgFiles.where((svg) => !vecFiles.contains(svg)).toList();
|
||||||
|
if (svgOnly.isNotEmpty) {
|
||||||
|
print('\n📄 SVG های بدون نسخه VEC: ${svgOnly.length}');
|
||||||
|
}
|
||||||
|
|
||||||
|
// پیدا کردن VEC های بدون SVG
|
||||||
|
final vecOnly = vecFiles.where((vec) => !svgFiles.contains(vec)).toList();
|
||||||
|
if (vecOnly.isNotEmpty) {
|
||||||
|
print('📄 VEC های بدون نسخه SVG: ${vecOnly.length}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user