زیادہ تر اینڈرائیڈ ٹیوٹوریلز "یہ ایمولیٹر پر چلتا ہے” پر رکتے ہیں۔ لیکن Google Play پر ایک خالی پروجیکٹ کی ایپ کو فہرست میں شامل کرنا جہاں حقیقی لوگ اسے انسٹال کر سکتے ہیں اس میں بعد کا حصہ شامل ہوتا ہے، جس پر شاذ و نادر ہی ایک جگہ بات کی جاتی ہے: ریلیز بلڈز، سائننگ کیز، ایپ بنڈلز، اسٹور لسٹنگ، پالیسی فارمز، اور نئے ڈویلپر اکاؤنٹس کے لیے مطلوبہ ٹیسٹنگ پیریڈز۔
پچھلے کچھ سالوں میں اینڈرائیڈ ڈویلپمنٹ میں بہت کچھ بدل گیا ہے۔ Jetpack Compose اب ڈیفالٹ UI ٹول کٹ ہے، XML لے آؤٹ میراثی ہیں، کوٹلن واحد زبان ہے جو آفیشل ٹیمپلیٹس میں استعمال ہوتی ہے، اور Google Play نے نئے ڈویلپرز کے لیے اپنی ضروریات کو سخت کر دیا ہے۔
سے شروع ہونے والی پچھلی گائیڈ findViewById یا آپ ایک APK اپ لوڈ کے ساتھ ختم ہوجاتے ہیں جو اب اس کے کام کرنے کے طریقے سے میل نہیں کھاتا ہے۔
یہ ہینڈ بک آپ کو پورے راستے کی رہنمائی کرتی ہے۔ Jetpack Compose، Room، ViewModel، اور Navigation 3 کا استعمال کرتے ہوئے ایک چھوٹی لیکن مکمل نوٹس ایپ بنائیں، کچھ ٹیسٹ لکھیں، ایک دستخط شدہ ریلیز بنڈل بنائیں، اور پھر اسے Google Play Console کے ذریعے پروڈکشن ریلیز تک لے جائیں۔
انڈیکس
شرائط
آپ کو پروگرامنگ کے بنیادی تصورات سے واقف ہونا چاہیے اور کم از کم پہلے کوٹلن کو پڑھا ہو گا۔ پچھلے Android تجربے کی ضرورت نہیں ہے۔ اینڈرائیڈ سے متعلق تمام تصورات کی وضاحت اسی طرح کی جاتی ہے جیسے وہ ظاہر ہوتے ہیں۔
ٹولز کے لیے آپ کو ضرورت ہو گی:
-
کم از کم 16 جی بی ریم کے ساتھ میکوس، ونڈوز یا لینکس چلانے والا کمپیوٹر (8 جی بی کام کرے گا، لیکن ایمولیٹر سست ہوگا)
-
اینڈرائیڈ اسٹوڈیو کا تازہ ترین مستحکم ورژن
-
ایک فزیکل اینڈرائیڈ فون (ڈیولپمنٹ کے دوران اختیاری، لیکن بعد میں آپ کے Play کنسول اکاؤنٹ کی تصدیق کے لیے ضروری ہے)
-
Google اکاؤنٹ کے لیے $25 اور ایک بار Google Play ڈویلپر رجسٹریشن فیس
کیا تعمیر کرنا ہے
نمونہ ایپ کا نام Quick Notes ہے۔ آپ کو ان نوٹوں کی فہرست نظر آئے گی جب ان کی آخری ترمیم کی گئی تھی، اور فلوٹنگ ایکشن بٹن آپ کو نئے نوٹ بنانے، ان میں ترمیم کرنے کے لیے نوٹوں پر ٹیپ کرنے، اور نوٹوں کو حذف کرنے دیتے ہیں۔ ہر چیز کو مقامی طور پر ڈیوائس پر SQLite ڈیٹا بیس میں اسٹور کیا جاتا ہے، لہذا ایپ آف لائن کام کرتی ہے اور اسے بیک اینڈ کی ضرورت نہیں ہوتی ہے۔
نوٹس ایپ سادہ لگ سکتی ہے، لیکن اس میں حقیقی ایپ کے لیے درکار تمام حصوں کا احاطہ کیا گیا ہے: فہرست اسکرینز اور تفصیلات اسکرینز، بیک بٹن کا استعمال کرتے ہوئے نیویگیشن، مستقل اسٹوریج، وہ حالت جو کنفیگریشن تبدیلیوں میں برقرار رہتی ہے جیسے اسکرین کی گردش، اور صارف کا ان پٹ۔ ایک بار جب آپ یہ سمجھ لیں کہ یہ ٹکڑے ایک ساتھ کیسے فٹ ہوتے ہیں، تو آپ اپنے نوٹ کو کسی اور چیز میں تبدیل کر سکتے ہیں۔
آپ جس اعلیٰ سطحی فن تعمیر کے ساتھ ختم ہوتے ہیں وہ ہے:
+-------------------------------------------------------------+
| UI Layer |
| MainActivity -> NavDisplay -> NoteListScreen |
| -> NoteEditorScreen |
| observes StateFlow, sends events |
+------------------------------+------------------------------+
|
v
+-------------------------------------------------------------+
| NotesViewModel |
| holds UI state, survives rotation, launches coroutines |
+------------------------------+------------------------------+
|
v
+-------------------------------------------------------------+
| Data Layer |
| NoteRepository -> NoteDao -> NotesDatabase (Room/SQLite) |
+-------------------------------------------------------------+
خاکہ تین تہوں کو عمودی طور پر سجا ہوا دکھاتا ہے، انحصار کی سمت کی نشاندہی کرنے کے لیے تیر نیچے کی طرف اشارہ کرتے ہیں۔
اوپری UI پرت میں سرگرمیاں اور دو اسکرینیں شامل ہیں۔ یہ صرف ViewModel کے ساتھ بات چیت کرتا ہے اور ڈیٹا بیس کے ساتھ براہ راست بات چیت نہیں کرتا ہے۔
وسط میں ویو ماڈل ریاست کا مالک ہے جو اسکرین دکھاتا ہے اور ایسے واقعات سے بچ جاتا ہے جیسے گردشیں جو اسکرین کو تباہ کرسکتی ہیں۔
نیچے کی ڈیٹا لیئر روم کو لپیٹ دیتی ہے، SQLite کے اوپر ایک ان پٹ لیئر۔ ڈیٹا فلو اسٹیک کو اس طرح بیک اپ کرتا ہے: Flow نوٹ کریں کہ جب ڈیٹا بیس تبدیل ہوتا ہے تو UI خود بخود اپ ڈیٹ ہو جائے گا۔
یہ پرتوں والا فن تعمیر ہے جس کی گوگل اپنی آفیشل ایپ آرکیٹیکچر گائیڈ میں تجویز کرتا ہے۔
ترقی کا ماحول کیسے ترتیب دیا جائے۔
developer.android.com/studio سے اینڈرائیڈ اسٹوڈیو ڈاؤن لوڈ کریں اور انسٹالر چلائیں۔ پہلی بار، سیٹ اپ وزرڈ Android SDK اور پلیٹ فارم ٹولز (شامل) ڈاؤن لوڈ کرتا ہے۔ adb) اور ایمولیٹر کے لیے سسٹم کی تصاویر۔ پہلے سے طے شدہ کو قبول کریں جب تک کہ نہ کرنے کی کوئی خاص وجہ نہ ہو۔ اینڈرائیڈ اسٹوڈیو اپنے JDK کے ساتھ بنڈل آتا ہے، لہذا آپ کو جاوا کو الگ سے انسٹال کرنے کی ضرورت نہیں ہے۔
سیٹ اپ مکمل ہونے کے بعد، SDK مینیجر کو یہاں کھولیں: ٹولز > SDK مینیجر یقینی بنائیں کہ آپ کے پاس جدید ترین مستحکم Android پلیٹ فارم انسٹال ہے۔ لکھنے کے وقت، اس کا مطلب ہے Android 16 (API لیول 36) یا اس سے اوپر۔
گوگل پلے کو نئی ایپس اور ایپ اپ ڈیٹس کی ضرورت ہوتی ہے جو ہر سال تازہ ترین API لیول کو نشانہ بناتے ہیں، خاص طور پر اگست کے آخر میں۔ شروع کرنے سے پہلے، ٹارگٹ API لیول کے تقاضوں کا صفحہ دیکھیں تاکہ یہ معلوم ہو سکے کہ آپ کو کس نمبر کو ہدف بنانا چاہیے۔
اگلا، ہم ایک ایمولیٹر بناتے ہیں۔ ٹولز > ڈیوائس مینیجر. Google Play سروسز سے تازہ ترین Pixel فون پروفائل اور سسٹم کی تازہ ترین تصویر منتخب کریں۔
اصلی فون پر ٹیسٹ کرنے کے لیے، اپنے آلے پر ڈویلپر کے اختیارات کو فعال کرنے کے لیے تھپتھپائیں۔ تعمیر نمبر سات بار ترتیبات > فون کے بارے میںپھر اسے آن کریں۔ USB ڈیبگنگ (یا وائرلیس ڈیبگنگ) ڈویلپر کے اختیارات کے مینو میں۔
نیا کمپوز پروجیکٹ کیسے بنایا جائے۔
اینڈرائیڈ اسٹوڈیو میں، منتخب کریں: فائل> نیا> نیا پروجیکٹ اور خالی سرگرمی ٹیمپلیٹ یہ ٹیمپلیٹ کمپوز پر مبنی ہے۔ اسی طرح کا نام "Empty Views Activity” پرانے XML لے آؤٹ سسٹم کا استعمال کرتا ہے، اس لیے یقینی بنائیں کہ آپ صحیح انتخاب کرتے ہیں۔
اپنے پروجیکٹ کی تفصیلات درج کریں۔ نام وہی ہے جو صارفین کو نظر آئے گا (فوری نوٹ)۔ پیکیج کا نام Google Play پر آپ کی ایپ کے لیے ایک مستقل، عالمی سطح پر منفرد شناخت کنندہ ہے، جو درج ذیل ریورس ڈومین فارمیٹ میں لکھا گیا ہے: com.yourname.quicknotes. ایک بار شائع ہونے کے بعد آپ اسے تبدیل نہیں کر سکتے، لہذا احتیاط سے انتخاب کریں۔ کم از کم SDK کو API 24 پر سیٹ کریں، جو کہ تقریباً تمام فعال اینڈرائیڈ ڈیوائسز پر لاگو ہوتا ہے، اور کوٹلن ڈی ایس ایل میں تعمیر کنفیگریشن لینگویج رکھیں۔
کلک کریں ختم پہلی گریڈل مطابقت پذیری مکمل ہونے کا انتظار کریں۔ آپ جو پروجیکٹ حاصل کرتے ہیں وہ تقریبا اس طرح ہے:
QuickNotes/
├── app/
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/com/yourname/quicknotes/
│ │ │ ├── MainActivity.kt
│ │ │ └── ui/theme/
│ │ └── res/
│ ├── test/
│ └── androidTest/
├── gradle/
│ └── libs.versions.toml
├── build.gradle.kts
└── settings.gradle.kts
درخت آپ کے نئے پروجیکٹ کے لیے اہم فائلیں دکھاتا ہے۔
کہ app فولڈر ایک واحد ماڈیول ہے جو ایک قابل انسٹال ایپ بن جاتا ہے، build.gradle.kts اینڈرائیڈ کنفیگریشن (SDK لیول، ورژن نمبر، انحصار) رکھتا ہے۔
src/main پروڈکشن کوڈ پر مشتمل ہے۔ AndroidManifest.xml یہ آپ کے ایپ کے اجزاء کو سسٹم میں بیان کرتا ہے۔ res یہ آئیکنز اور سٹرنگز جیسے وسائل کے لیے ایک فولڈر ہے۔
کہ test فولڈر آپ کے کمپیوٹر پر چلنے والے یونٹ ٹیسٹ کے لیے ہے، androidTest ڈیوائس یا ایمولیٹر پر چلنے والے ٹیسٹوں کے لیے۔
جڑ میں gradle/libs.versions.toml تمام انحصاری ورژن کے ساتھ ایک ورژن کیٹلاگ۔ settings.gradle.kts پروجیکٹ میں ماڈیولز کی فہرست۔ کوٹلن فائلیں درج ذیل فولڈرز کے نیچے موجود ہیں۔ java تاریخی وجوہات کی بنا پر، یہ معمول ہے۔
سبز دبائیں چلائیں بٹن ایک مختصر تعمیر کے بعد، ایمولیٹر کو "ہیلو اینڈرائیڈ!” سکرین پر یہ تصدیق کرے گا کہ ٹول چین کام کر رہا ہے۔
ورژن کیٹلاگ کا استعمال کرتے ہوئے انحصار کو کیسے منظم کریں۔
جدید اینڈرائیڈ پروجیکٹ لائبریری کے تمام ورژنز کو ایک فائل میں رکھتے ہیں۔ gradle/libs.versions.tomlاپنے بلڈ اسکرپٹ میں کسی عرف کے ذریعہ اس لائبریری کا حوالہ دیں۔
کیٹلاگ کھولیں اور مندرجات کو اس سے تبدیل کریں:
[versions]
agp = "9.0.0"
kotlin = "2.3.0"
ksp = "2.3.4"
composeBom = "2026.01.00"
activityCompose = "1.12.2"
lifecycle = "2.10.0"
navigation3 = "1.0.0"
room = "2.8.4"
serialization = "1.9.0"
junit = "4.13.2"
androidxJunit = "1.3.0"
[libraries]
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-compose-material-icons-core = { group = "androidx.compose.material", name = "material-icons-core" }
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" }
androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" }
androidx-navigation3-runtime = { group = "androidx.navigation3", name = "navigation3-runtime", version.ref = "navigation3" }
androidx-navigation3-ui = { group = "androidx.navigation3", name = "navigation3-ui", version.ref = "navigation3" }
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
kotlinx-serialization-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core", version.ref = "serialization" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidxJunit" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
room = { id = "androidx.room", version.ref = "room" }
کیٹلاگ کے تین حصے ہیں: [versions] ہر ورژن نمبر کو ایک بار نام دیں۔ [libraries] Maven کوآرڈینیٹس اور مختصر عرفی ناموں کا نقشہ بنائیں [plugins] گریڈل پلگ ان کے لیے بھی یہی ہے۔
Gradle ہر عرف میں ڈیش کو نقطوں سے بدل دیتا ہے۔ androidx-room-runtime بن جاتا ہے۔ libs.androidx.room.runtime تعمیر اسکرپٹ میں۔ کمپوز لائبریریوں کا اپنا کوئی ورژن نہیں ہوتا ہے کیونکہ وہ کمپوز بل آف میٹریلز (BOM) سے نکالی جاتی ہیں، جو کمپوز لائبریریوں کے سیٹ کو اینکر کرتی ہیں جن کا ایک ساتھ تجربہ کیا جاتا ہے۔ کوٹلن کمپوز کمپائلر پلگ ان کوٹلن 2.0 کے بعد سے ہی کوٹلن کے ساتھ ایک ورژن شیئر کرتا ہے۔
KSP ایک تشریحی پروسیسر ہے جسے روم ڈیٹا بیس کوڈ بنانے کے لیے استعمال کرتا ہے۔ اس مضمون کے لکھے جانے کے وقت یہاں کے ورژن نمبر سب سے زیادہ موجودہ ہیں۔ اینڈروئیڈ اسٹوڈیو کیٹلاگ میں فرسودہ آئٹمز کو نمایاں کرتا ہے، لہذا آپ کو تازہ ترین مستحکم ورژن پر جانے کی پیشکش کو قبول کرنا چاہیے۔
اب اپنی جڑ کو اپ ڈیٹ کریں۔ build.gradle.kts لہذا آپ اپنے ایپ ماڈیول میں پلگ ان استعمال کر سکتے ہیں۔
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.compose) apply false
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.ksp) apply false
alias(libs.plugins.room) apply false
}
یہ فائل ہر پلگ ان کو درج ذیل قرار دیتی ہے: apply falseپلگ ان کو روٹ پروجیکٹ پر لاگو کرنے کے بجائے بلڈ کلاس پاتھ پر رکھتا ہے۔ ہر ماڈیول پھر ان پلگ انز کو منتخب کرتا ہے جن کی اسے درحقیقت ضرورت ہوتی ہے۔
آپ دیکھ سکتے ہیں کہ کچھ بھی نہیں ہے۔ org.jetbrains.kotlin.android پلگ ان اینڈرائیڈ گریڈل پلگ ان 9 کے ساتھ شروع کرتے ہوئے، کوٹلن سپورٹ اینڈرائیڈ پلگ ان میں شامل ہے، اس لیے اب آپ کو علیحدہ کوٹلن اینڈرائیڈ پلگ ان کی ضرورت نہیں ہے۔ یہاں تک کہ اگر آپ AGP 8 میں ایک پرانے پروجیکٹ پر کام کر رہے ہیں، تو آپ دیکھیں گے کہ وہ پلگ ان لاگو ہوا ہے اور یہ ٹھیک ہے۔
پھر app/build.gradle.kts:
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
alias(libs.plugins.room)
}
android {
namespace = "com.yourname.quicknotes"
compileSdk = 36
defaultConfig {
applicationId = "com.yourname.quicknotes"
minSdk = 24
targetSdk = 36
versionCode = 1
versionName = "1.0.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
buildFeatures {
compose = true
}
}
room {
schemaDirectory("$projectDir/schemas")
}
dependencies {
val composeBom = platform(libs.androidx.compose.bom)
implementation(composeBom)
androidTestImplementation(composeBom)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.material.icons.core)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.navigation3.runtime)
implementation(libs.androidx.navigation3.ui)
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
implementation(libs.kotlinx.serialization.core)
ksp(libs.androidx.room.compiler)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest)
}
اسے غور سے پڑھیں کیونکہ یہ آپ کے پروجیکٹ کے لیے سب سے اہم کنفیگریشن فائل ہے۔
کہ namespace تیار کردہ پیکیج کے لیے استعمال کردہ پیکیج۔ R کلاس کے دوران applicationId آپ کے آلے اور Google Play پر ایپ کی ID۔ وہ عام طور پر ملتے ہیں.
compileSdk API کی سطح جس پر کوڈ مرتب کیا جاتا ہے۔ targetSdk پلیٹ فارم کے رویے کے بارے میں Android کو مطلع کرتا ہے جس پر آپ کی ایپ کا تجربہ کیا گیا ہے۔ جب اٹھاتے ہیں۔ targetSdkیہ نئے طرز عمل کو چالو کرتا ہے جیسے زبردستی کنارے سے کنارے ڈرائنگ۔
versionCode یہ ایک عدد عدد ہے جسے ہر بار جب آپ Play پر اپ لوڈ کرتے ہیں تو اس میں اضافہ ہونا چاہیے۔ versionName ایک انسانی پڑھنے کے قابل سٹرنگ جو صارف کو نظر آتی ہے۔
کہ release بلاک R8 کو چالو کرتا ہے، جو آپ کے کوڈ کو کم کرتا، مبہم اور بہتر بناتا ہے۔ isShrinkResources غیر استعمال شدہ وسائل کو ہٹا دیں۔
کہ room بلاک روم پلگ ان کو بتاتا ہے کہ ڈیٹا بیس اسکیما کا JSON سنیپ شاٹ کہاں بنانا ہے جس کی ضرورت بعد میں منتقلی لکھتے وقت ہوگی۔
انحصار میں platform(libs.androidx.compose.bom) اس بات کو یقینی بنانے کے لیے BOM کا اطلاق کریں کہ کمپوز لائبریریاں سیدھ میں ہیں اور ksp(...) تعمیراتی وقت پر روم کمپائلر چلاتا ہے۔ کہ debugImplementation اس لائن میں کمپوز پیش نظارہ ٹولز اور ٹیسٹ مینی فیسٹس صرف ڈیبگ بلڈز میں ہوتے ہیں، اس لیے وہ صارفین کے لیے دستیاب نہیں ہیں۔
کلک کریں ابھی مطابقت پذیری کریں۔ اگر آپ ایڈیٹر کے ٹاپ بینر میں کامیاب مطابقت پذیری دیکھتے ہیں، تو آپ کوڈ لکھنے کے لیے تیار ہیں۔
جیٹ پیک کمپوز UI کے بارے میں کیسے سوچتا ہے۔
اپنی اسکرینیں لکھنے سے پہلے، اس ماڈل کو سمجھنا مددگار ہے جس پر کمپوز بنایا گیا ہے۔ اس کی وجہ یہ ہے کہ یہ پچھلے ویو سسٹم سے مختلف ہے۔
کمپوز میں، UI ریاست کا ایک فنکشن ہے۔ تشریح شدہ افعال لکھیں۔ @Composable بیان کرتا ہے کہ موجودہ ڈیٹا کے لیے اسکرین کیسی نظر آئے گی۔ آپ کبھی منظر تک نہیں پہنچتے اور کبھی کال نہیں کرتے setText اس پر اس کے بجائے، جب ڈیٹا تبدیل ہوتا ہے، تو کمپوز نئے ڈیٹا کے ساتھ فنکشن کو دوبارہ کال کرتا ہے اور صرف اسکرین کے ان حصوں کو اپ ڈیٹ کرتا ہے جو حقیقت میں تبدیل ہوئے ہیں۔ اس ریڈو کو ری کنفیگریشن کہا جاتا ہے۔
یہاں سب سے چھوٹی مفید مثال ہے:
@Composable
fun Counter() {
var count by remember { mutableIntStateOf(0) }
Button(onClick = { count++ }) {
Text("Clicked $count times")
}
}
کہ Counter فنکشن مندرجہ ذیل ریاستوں کا اعلان کرتا ہے: mutableIntStateOf(0). اس حالت کے لیے تمام پڑھی ہوئی گھڑیاں تحریر کریں۔ جب بٹن ہوتا ہے۔ onClick لیمبڈا اضافہ countکمپوز یہ جانتا ہے۔ Counter پچھلی قیمت کو پڑھ کر ریزرو۔ Counter دوبارہ دوڑنا، Text یہ نیا نمبر دکھاتا ہے۔
کہ remember کال ری کنفیگریشنز میں قدر کو برقرار رکھنے کے لیے ہے۔ اس کے بغیر، count ہر بار جب فنکشن کو انجام دیا جاتا ہے تو اسے 0 پر دوبارہ ترتیب دیا جاتا ہے۔
کہ by کلیدی الفاظ کوٹلن پراپرٹی ڈیلیگیٹس ہیں جنہیں پڑھا اور لکھا جا سکتا ہے۔ count اسے باقاعدہ متغیر کی طرح لکھنے کے بجائے count.value.
remember ری کنفیگریشنز محفوظ ہیں، لیکن فون کو گھمانے جیسی کنفیگریشن تبدیلیاں محفوظ نہیں ہیں۔ کیونکہ اس صورت میں اینڈرائیڈ سرگرمی کو تباہ اور دوبارہ بناتا ہے۔ سادہ اقدار کے لیے جو گردش کے بعد باقی رہنی چاہیے، استعمال کریں: rememberSaveable اس کے بجائے۔ مزید اہم چیزوں کے لیے، جیسے ڈیٹا بیس سے بھرا ہوا ڈیٹا، آپ ویو ماڈل میں حالت کو برقرار رکھتے ہیں جو سرگرمی سے کہیں زیادہ ہے۔ اس ایپ میں آپ دونوں استعمال کریں گے۔
ایک اور کلیدی نظریہ ریاست کو لہرانا ہے۔ کمپوز ایبلز جو اپنی ریاست کے مالک ہیں دوبارہ استعمال کرنا مشکل اور جانچنا مشکل ہے۔ ایک بہتر نمونہ ریاست کو پیرامیٹر کے طور پر اور ایونٹ کو لیمبڈا کے طور پر منتقل کرنا ہے۔ لہذا، کمپوز ایبل صرف وہی دکھاتا ہے جو فراہم کیا جاتا ہے اور رپورٹ کرتا ہے کہ صارف نے کیا کیا۔ بھیجنے والا طے کرتا ہے کہ ملک کہاں رہتا ہے۔ آپ اس پیٹرن کو فہرست اسکرین میں دیکھ سکتے ہیں جو ٹیسٹنگ کو آسان بناتا ہے۔
اپنی ایپ کو کنفیگر کرنے کا طریقہ
ذیل میں درج ذیل پیکیجز بنائیں: com.yourname.quicknotes پروجیکٹ پینل میں پیکیج پر دائیں کلک کریں اور نیا > پیکیج:
com.yourname.quicknotes
├── data/
│ ├── Note.kt
│ ├── NoteDao.kt
│ ├── NotesDatabase.kt
│ └── NoteRepository.kt
├── ui/
│ ├── NotesViewModel.kt
│ ├── NoteListScreen.kt
│ ├── NoteEditorScreen.kt
│ └── theme/
├── navigation/
│ └── NavKeys.kt
├── MainActivity.kt
└── NotesApp.kt
ترتیب کے لحاظ سے فائلوں کو گروپ کرتا ہے۔
کہ data پیکیج میں اسٹوریج سے متعلق ہر چیز پر مشتمل ہے۔ Note ڈیٹا ایکسیس آبجیکٹ (DAO) جو آئٹمز، سوالات، روم ڈیٹا بیس کلاسز، اور اسٹوریج کی وضاحت کرتا ہے جسے ایپ کا باقی حصہ کمرے سے براہ راست بات چیت کرنے کے بجائے استعمال کرتا ہے۔
کہ ui پیکج میں ایک ViewModel، دو اسکرینیں، اور ایک تھیم فائل ہے جو ٹیمپلیٹ کے ذریعے تیار کی گئی ہے۔
کہ navigation پیکیج میں چابیاں ہیں جو ہر اسکرین کی شناخت کرتی ہیں۔ MainActivity ایک واحد سرگرمی جو تمام کمپوز UI کی میزبانی کرتی ہے۔ NotesApp یہ ایک رواج ہے۔ Application کلاس پوری ایپ کے لیے ایک بار ڈیٹا بیس بنانے کے لیے استعمال ہوتی ہے۔
بڑی ایپس کے لیے، آپ ممکنہ طور پر اسے علیحدہ گریڈل ماڈیولز میں تقسیم کریں گے، لیکن یہاں ایک پیکیج کافی ہوگا۔
کمرہ کا استعمال کرتے ہوئے مقامی طور پر ڈیٹا کیسے اسٹور کیا جائے۔
کمرہ جیٹ پیک پرسسٹینس لائبریری ہے۔ آپ اپنے ٹیبلز کو کوٹلن کلاسز اور اپنے سوالات کو تشریح شدہ انٹرفیس طریقوں کے طور پر بیان کرتے ہیں، اور کمرہ کمپائل کے وقت SQLite کوڈ تیار کرتا ہے۔ تعمیر کے دوران سوالات کی توثیق کی جاتی ہے، لہذا کالم کے نام میں ٹائپنگ صارف کے فون پر کریش ہونے کی بجائے کمپائلر کی غلطی ہوگی۔
درج ذیل اداروں کے ساتھ شروع کریں: data/Note.kt:
package com.yourname.quicknotes.data
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "notes")
data class Note(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val title: String,
val body: String,
val updatedAt: Long = System.currentTimeMillis()
)
@Entity روم کو بتائیں کہ یہ کلاس درج ذیل ٹیبل پر نقشہ بناتی ہے: notesہر وصف ایک کالم بن جاتا ہے۔
کہ id فیلڈ بنیادی کلید ہے۔ autoGenerate = true جب آپ اس طرح کی ID کے ساتھ قطار داخل کرتے ہیں، تو آپ SQLite سے ایک نئی ID تفویض کرنے کو کہتے ہیں۔ 0. اسی لیے id پہلے سے طے شدہ ہے۔ 0: نئے نوٹوں کے محفوظ ہونے تک ان کی کوئی حقیقی شناخت نہیں ہوتی۔
کہ updatedAt پراپرٹی ملی سیکنڈ میں ٹائم اسٹیمپ اسٹور کرتی ہے، جس کی ڈیفالٹ ویلیو ہوتی ہے۔ Note ایک آبجیکٹ بنایا گیا ہے جو ایپ کو یہ بتاتا ہے کہ کون سے نوٹوں میں حال ہی میں ترمیم کی گئی ہے۔
عام استعمال کرتے ہوئے data class آپ کو دیتا ہے equals، hashCodeاور copy کمپوز اس کا استعمال اس بات کا تعین کرنے کے لیے کرتا ہے کہ آیا کوئی فہرست آئٹم تبدیل ہوا ہے یا نہیں۔
اگلا، ہم استفسار کی وضاحت کرتے ہیں۔ data/NoteDao.kt:
package com.yourname.quicknotes.data
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Query
import androidx.room.Upsert
import kotlinx.coroutines.flow.Flow
@Dao
interface NoteDao {
@Query("SELECT * FROM notes ORDER BY updatedAt DESC")
fun observeAll(): Flow>
@Query("SELECT * FROM notes WHERE id = :id")
suspend fun getById(id: Long): Note?
@Upsert
suspend fun upsert(note: Note)
@Delete
suspend fun delete(note: Note)
}
DAO ایک انٹرفیس ہے جسے آپ کا نفاذ کمرہ آپ کے لیے لکھتا ہے۔ کہ observeAll طریقہ یہ ہے۔ Flow ایک سادہ فہرست کے بجائے >
Flow یہ کوٹلن کوروٹینز کا ایک سلسلہ ہے جو جب بھی بنیادی ڈیٹا میں تبدیلی کرتا ہے تو نئی قدروں کا اخراج ہوتا ہے، اس لیے جب بھی کوئی نوٹ داخل کیا جاتا ہے، اپ ڈیٹ کیا جاتا ہے یا حذف کیا جاتا ہے، روم خود بخود استفسار کو دوبارہ چلاتا ہے اور ایک نئی فہرست کا اخراج کرتا ہے۔ یہ فہرست کی اسکرینوں کو بغیر کسی دستی ریفریش منطق کے مطابقت پذیر رکھنے کے لیے ہے۔
دوسرے طریقے دکھائے گئے ہیں۔ suspendاس کا مطلب ہے کہ اسے کوروٹین سے بلایا جانا چاہیے۔ کمرہ اسے بیک گراؤنڈ تھریڈ پر چلاتا ہے، لہذا ڈیٹا بیس آپریشنز مین تھریڈ کو بلاک نہیں کرتے یا UI کو منجمد نہیں کرتے۔
@Upsert اگر بنیادی کلید موجود نہیں ہے تو ایک قطار داخل کرتا ہے اور اگر موجود ہے تو اسے اپ ڈیٹ کرتا ہے۔ یہ آپ کو ایک ہی طریقہ میں نوٹ تخلیق اور ترمیم دونوں کو سنبھالنے کی اجازت دیتا ہے۔
کہ :id دوسری استفسار کا نحو سٹرنگ کنکٹیشن کے بغیر طریقہ کار کے پیرامیٹرز کو SQL بیان سے محفوظ طریقے سے باندھتا ہے۔
اب ڈیٹا بیس بنائیں۔ data/NotesDatabase.kt:
package com.yourname.quicknotes.data
import androidx.room.Database
import androidx.room.RoomDatabase
@Database(entities = [Note::class], version = 1, exportSchema = true)
abstract class NotesDatabase : RoomDatabase() {
abstract fun noteDao(): NoteDao
}
کہ @Database تبصرے میں ڈیٹا بیس میں موجود تمام اداروں اور ان کے سکیما ورژن نمبرز کی فہرست دی گئی ہے۔ کمرہ مندرجہ ذیل نفاذ کے ساتھ اس تجریدی کلاس کا ایک کنکریٹ ذیلی طبقہ تخلیق کرتا ہے: noteDao().
کہ version ایپ ایکٹیویٹ ہونے کے بعد فیلڈز اہم ہیں۔ اگر آپ بعد میں کالم شامل کریں۔ Noteآپ کو ورژن بڑھانے اور منتقلی فراہم کرنے کی ضرورت ہے۔ بصورت دیگر، کمرہ آپ کے آلے پر موجود ڈیٹا بیس کو کھولنے سے انکار کر دے گا۔
exportSchema = true ہر اسکیما ورژن کے لیے JSON تفصیل schemas یہ وہ فولڈر ہے جسے گریڈل نے ترتیب دیا ہے۔ کمرہ ان فائلوں کو ہجرت کی جانچ پڑتال کے لیے استعمال کرتا ہے اور خود بخود انہیں بناتا ہے، لہذا آپ ان فولڈرز کو ورژن کنٹرول میں بھیج دیتے ہیں۔
ریپوزٹریز اور ایپلیکیشن کلاسز کا استعمال کرتے ہوئے ڈیٹا کو کیسے بے نقاب کیا جائے۔
ذخیرہ ViewModel اور کمرے کے درمیان ایک پتلی پرت ہے۔ ایپ بنیادی طور پر کالز کو فارورڈ کرتی ہے، لیکن آپ بعد میں UI کوڈ کو چھوئے بغیر کیشنگ، نیٹ ورک سنکرونائزیشن، یا توثیق سب کو ایک جگہ پر شامل کر سکتے ہیں۔ بنانا data/NoteRepository.kt:
package com.yourname.quicknotes.data
import kotlinx.coroutines.flow.Flow
class NoteRepository(private val noteDao: NoteDao) {
fun observeNotes(): Flow> = noteDao.observeAll()
suspend fun getNote(id: Long): Note? = noteDao.getById(id)
suspend fun save(note: Note) = noteDao.upsert(note)
suspend fun delete(note: Note) = noteDao.delete(note)
}
ذخیرہ ہے۔ NoteDao کنسٹرکٹر خود بنانے کے بجائے کنسٹرکٹر میں۔ یہ انحصار انجیکشن کی سب سے آسان شکل ہے۔ چونکہ انحصار پاس کیا جاتا ہے، ٹیسٹوں کے نتیجے میں جعلی DAOs کو ذخیرہ میں منتقل کیا جا سکتا ہے۔ عوامی طریقوں کی تعریف ڈومین زبان میں کی گئی ہے (save، observeNotesڈیٹا بیس کی زبان کے بجائے (upsert، observeAll) یہ کمرے کی تفصیلات کو باقی ایپ میں لیک ہونے سے روکتا ہے۔
چونکہ SQLite کو کھولنا مہنگا ہے اور متعدد مثالوں کے کریش ہونے کا سبب بن سکتا ہے، اس لیے ہر ایپ کے عمل میں ڈیٹا بیس کو بالکل ایک بار بنایا جانا چاہیے۔ انحصار انجیکشن فریم ورک کے بغیر ایسا کرنے کا ایک عام طریقہ حسب ضرورت ہے۔ Application ذیلی کلاس بنانا NotesApp.kt جڑ پیکیج میں:
package com.yourname.quicknotes
import android.app.Application
import androidx.room.Room
import com.yourname.quicknotes.data.NoteRepository
import com.yourname.quicknotes.data.NotesDatabase
class NotesApp : Application() {
private val database: NotesDatabase by lazy {
Room.databaseBuilder(this, NotesDatabase::class.java, "notes.db").build()
}
val repository: NoteRepository by lazy {
NoteRepository(database.noteDao())
}
}
اینڈرائیڈ ایک مثال بناتا ہے۔ Application سرگرمی شروع ہونے سے پہلے ایک کلاس بنائیں اور اسے عمل کی زندگی کے لیے رکھیں۔ یہ آپ کی پوری ایپ میں سنگل ٹونز کے لیے قدرتی گھر بنا دیتا ہے۔
کہ by lazy ایجنٹ پہلی بار ڈیٹا بیس کی تخلیق کو موخر کرتا ہے۔ repository رسائی حاصل کی گئی، ایپ کو تیز تر بنانا۔
Room.databaseBuilder سیاق و سباق، ڈیٹا بیس کلاس اور فائل کا نام لیتا ہے اور build() تیار کردہ نفاذ کو لوٹاتا ہے۔ بڑی ایپس کے لیے، ہلٹ یا کوئین ان کنکشنز کا نظم کر سکتے ہیں، لیکن ایک دستی طریقہ یہ ظاہر کرتا ہے کہ یہ فریم ورک خود کار طریقے سے کیا کرتا ہے۔
اس کلاس کو اینڈرائیڈ پر استعمال کرنے کے لیے، اسے یہاں رجسٹر کریں: AndroidManifest.xml شامل کرکے android:name سے منسوب ٹیگ:
صرف لائنیں جو آپ شامل کرتے ہیں وہ ہیں: android:name=".NotesApp". معروف ڈاٹ ایپ کے نام کی جگہ کا مخفف ہے، لہذا اینڈرائیڈ اسے اس طرح حل کرتا ہے: com.yourname.quicknotes.NotesApp.
دیگر خصوصیات ٹیمپلیٹ سے آئی ہیں، بشمول لانچر آئیکن، آئیکن کے نیچے دکھائے جانے والا ایپ لیبل، دائیں سے بائیں لے آؤٹ سپورٹ، اور کمپوز کے پہلے فریم کو ڈرا کرنے سے پہلے استعمال ہونے والی ڈیفالٹ تھیم۔ اگر تم بھول جاؤ android:name پراپرٹی، اینڈرائیڈ ڈیفالٹ ویلیو استعمال کرتا ہے۔ Application جب میں کسی کلاس میں منتقل کرنے کی کوشش کرتا ہوں تو ایپ پہلی بار کریش ہو جاتی ہے۔ NotesApp.
ViewModel کا استعمال کرتے ہوئے UI حالت کا نظم کیسے کریں۔
کوئی راستہ نہیں ViewModel اسکرین کی حالت کو برقرار رکھتا ہے اور کنفیگریشن تبدیلیوں کو برقرار رکھتا ہے۔ جب صارف فون کو گھماتا ہے، تو سرگرمی تباہ ہو جاتی ہے اور دوبارہ بنائی جاتی ہے، لیکن ViewModel میموری میں رہتا ہے لہذا نوٹوں کی فہرست کو دوبارہ لوڈ کرنے کی ضرورت نہیں ہے۔ بنانا ui/NotesViewModel.kt:
package com.yourname.quicknotes.ui
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.yourname.quicknotes.NotesApp
import com.yourname.quicknotes.data.Note
import com.yourname.quicknotes.data.NoteRepository
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
class NotesViewModel(private val repository: NoteRepository) : ViewModel() {
val notes: StateFlow> = repository.observeNotes()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = emptyList()
)
suspend fun getNote(id: Long): Note? = repository.getNote(id)
fun saveNote(id: Long?, title: String, body: String) {
viewModelScope.launch {
repository.save(Note(id = id ?: 0, title = title.trim(), body = body.trim()))
}
}
fun deleteNote(note: Note) {
viewModelScope.launch {
repository.delete(note)
}
}
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val app = this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as NotesApp
NotesViewModel(app.repository)
}
}
}
}
یہ سبق تین خیالات پر مشتمل ہے:
پہلا یہ ہے۔ notes جائیداد repository.observeNotes() سردی پکڑو Flowیہ تب تک کام کرتا ہے جب تک کوئی اسے جمع کر رہا ہو۔ کہ stateIn آپریٹر اسے اس میں تبدیل کرتا ہے: StateFlowیہ ہمیشہ موجودہ اقدار (خالی فہرست سے شروع کرتے ہوئے) رکھتا ہے جسے UI فوری طور پر پڑھ سکتا ہے۔ SharingStarted.WhileSubscribed(5_000) ڈیٹا بیس کے استفسارات کو UI کے مشاہدہ کے دوران اور رکنے کے بعد 5 سیکنڈ تک فعال رکھیں۔ 5 سیکنڈ کی رعایتی مدت کا مطلب یہ ہے کہ تیزی سے گردش کرنے کی وجہ سے سوالات میں خلل یا دوبارہ آغاز نہیں کیا جائے گا، اور اگر ایپ کو زیادہ دیر تک پس منظر میں چھوڑ دیا جائے تو استفسارات جاری کیے جائیں گے۔
دوسرا خیال یہ ہے۔ viewModelScope. محفوظ کریں اور حذف کریں۔ suspend چونکہ یہ ایک فنکشن ہے، ایک کورٹین کی ضرورت ہے۔ viewModelScope ViewModel کی زندگی کے ساتھ منسلک ایک کوروٹین دائرہ کار؛ جب ViewModel صاف ہو جاتا ہے، اس دائرہ کار میں شروع ہونے والے تمام آپریشنز خود بخود منسوخ ہو جاتے ہیں۔
میں saveNote، null ID کا مطلب ہے "یہ ایک نیا نوٹ ہے”، تو 0 کمرہ ایک نئی ID بناتا ہے۔ ایک غیر کالعدم ID ایک موجودہ قطار کو اپ ڈیٹ کرتی ہے۔ کیونکہ نیا Note ہر بار جب آپ محفوظ کرتے ہیں تو ایک چیز بنائی جاتی ہے۔ updatedAt اسے موجودہ وقت کے ساتھ تازہ کیا جائے گا۔
تیسرا خیال ہے۔ Factory. اینڈروئیڈ ایک ViewModel بناتا ہے تاکہ یہ ViewModel کو گردشوں میں برقرار رکھ سکے۔ یعنی آپ کنسٹرکٹر کو براہ راست کال نہیں کر سکتے۔
فیکٹری اینڈرائیڈ کو بتاتی ہے کہ ضرورت پڑنے پر ویو ماڈل کیسے بنایا جائے۔ کہ viewModelFactory اور initializer بلڈرز اس مضمون کو پڑھیں Application تخلیق ایڈ آئٹم سے ایک مثال کاسٹ کریں۔ NotesAppذخیرہ کنسٹرکٹر کو منتقل کریں۔
نوٹ لسٹ اسکرین کو کنفیگر کرنے کا طریقہ
فہرست اسکرین کو دو کمپوز ایبلز میں تقسیم کیا گیا ہے۔ سٹیٹ فل ریپر ویو ماڈل سے جڑتا ہے اور سٹیٹ لیس مواد کمپوز ایبل اصل ڈرائنگ کرتا ہے۔ بنانا ui/NoteListScreen.kt:
package com.yourname.quicknotes.ui
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.yourname.quicknotes.data.Note
@Composable
fun NoteListScreen(
viewModel: NotesViewModel,
onAddNote: () -> Unit,
onOpenNote: (Long) -> Unit
) {
val notes by viewModel.notes.collectAsStateWithLifecycle()
NoteListContent(
notes = notes,
onAddNote = onAddNote,
onOpenNote = onOpenNote,
onDeleteNote = viewModel::deleteNote
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NoteListContent(
notes: List,
onAddNote: () -> Unit,
onOpenNote: (Long) -> Unit,
onDeleteNote: (Note) -> Unit
) {
Scaffold(
topBar = { TopAppBar(title = { Text("Quick Notes") }) },
floatingActionButton = {
FloatingActionButton(onClick = onAddNote) {
Icon(Icons.Default.Add, contentDescription = "Add note")
}
}
) { innerPadding ->
if (notes.isEmpty()) {
Box(
modifier = Modifier.fillMaxSize().padding(innerPadding),
contentAlignment = Alignment.Center
) {
Text("No notes yet", style = MaterialTheme.typography.bodyLarge)
}
} else {
LazyColumn(
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier.fillMaxSize().padding(innerPadding)
) {
items(notes, key = { it.id }) { note ->
NoteCard(
note = note,
onClick = { onOpenNote(note.id) },
onDelete = { onDeleteNote(note) }
)
}
}
}
}
}
@Composable
private fun NoteCard(note: Note, onClick: () -> Unit, onDelete: () -> Unit) {
Card(modifier = Modifier.fillMaxWidth().clickable(onClick = onClick)) {
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column(modifier = Modifier.weight(1f)) {
Text(note.title, style = MaterialTheme.typography.titleMedium)
if (note.body.isNotBlank()) {
Text(
note.body,
style = MaterialTheme.typography.bodyMedium,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
}
IconButton(onClick = onDelete) {
Icon(Icons.Default.Delete, contentDescription = "Delete ${note.title}")
}
}
}
}
NoteListScreen یہ واحد حصہ ہے جسے میں ViewModel کے بارے میں جانتا ہوں۔ یہ کال کرتا ہے collectAsStateWithLifecycle()یہ ہے StateFlow معمول کے برعکس کمپوز سٹیٹ پر سوئچ کرتا ہے۔ collectAsState()جب ایپ بیک گراؤنڈ میں جاتی ہے تو کلیکشن رک جاتا ہے۔ اس سے بیٹری کی بچت ہوتی ہے اور اسکرینوں پر ڈیٹا بیس کی کارروائیوں کو روکتا ہے جسے کوئی نہیں دیکھ سکتا۔ پھر ایک باقاعدہ فہرست اور کچھ لیمبڈا NoteListContent.
کہ viewModel::deleteNote نحو ایک فنکشن حوالہ ہے، تحریر کا ایک مختصر طریقہ۔ { note -> viewModel.deleteNote(note) }.
NoteListContent میں ایک بے وطن شخص ہوں۔ یہ ہر چیز کو پیرامیٹرز کے طور پر حاصل کرتا ہے اور لیمبڈا کے ذریعے کلکس کی اطلاع دیتا ہے، جو کہ دراصل اسٹیٹ ہوسٹنگ ہے۔ اس میں، Scaffold ایک میٹریل 3 لے آؤٹ جو شمار کرتا ہے اور ٹاپ ایپ بار اور فلوٹنگ ایکشن بٹن کے لیے سلاٹس فراہم کرتا ہے۔ innerPadding لہذا مواد اس بار کے پیچھے یا سسٹم اسٹیٹس اور نیویگیشن بار کے پیچھے نہیں ہے۔
اگر فہرست خالی ہے تو، اسکرین کے بیچ میں ایک پیغام ظاہر ہوگا۔ بصورت دیگر استعمال کریں: LazyColumnصرف موجودہ نظر آنے والی قطاروں کو کنفیگر کرتا ہے۔ RecyclerView پرانے ویو سسٹم میں۔
کہ key = { it.id } استدلال ہر قطار کے لیے ایک مستحکم ID فراہم کرتا ہے، اس لیے جب کوئی نوٹ حذف ہو جاتا ہے، تو کمپوز کو بالکل معلوم ہوتا ہے کہ کس قطار کو ہٹانا ہے، بجائے اس کے کہ اس کے نیچے موجود ہر چیز کو دوبارہ کھینچا جائے۔
NoteCard ایک نوٹ کھینچیں۔ Modifier زنجیریں ترتیب اور اعمال کو ترتیب دیتی ہیں۔ fillMaxWidth() کارڈ کو پوری سکرین پر پھیلائیں۔ clickable پورے کارڈ کو نلکوں کے لیے جوابدہ بناتا ہے۔
اندرونی Row، Modifier.weight(1f) ٹیکسٹ کالم کو ہدایت دیں کہ وہ ڈیلیٹ بٹن کی پیمائش کے بعد باقی تمام افقی جگہ کو لے لے۔ کہ contentDescription آئیکن کی قدر کو ٹاک بیک، Android کے اسکرین ریڈر کا استعمال کرتے ہوئے بلند آواز سے پڑھا جاتا ہے۔ ڈیلیٹ بٹن کی تفصیل میں نوٹ ٹائٹل شامل کرنے کا مطلب ہے کہ ٹاک بیک صارفین "ڈیلیٹ” بٹنوں کی ایک ہی قطار کے بجائے "گروسری ڈیلیٹ” سنیں گے۔
نوٹ ایڈیٹر اسکرین کیسے بنائیں
ایڈیٹر نئے اور موجودہ دونوں تبصروں پر کارروائی کرتا ہے۔ بنانا ui/NoteEditorScreen.kt:
package com.yourname.quicknotes.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NoteEditorScreen(
noteId: Long?,
viewModel: NotesViewModel,
onDone: () -> Unit
) {
var title by rememberSaveable { mutableStateOf("") }
var body by rememberSaveable { mutableStateOf("") }
var loaded by rememberSaveable { mutableStateOf(noteId == null) }
LaunchedEffect(noteId) {
if (noteId != null && !loaded) {
viewModel.getNote(noteId)?.let {
title = it.title
body = it.body
}
loaded = true
}
}
Scaffold(
topBar = {
TopAppBar(
title = { Text(if (noteId == null) "New note" else "Edit note") },
navigationIcon = {
IconButton(onClick = onDone) {
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
}
},
actions = {
TextButton(
onClick = {
viewModel.saveNote(noteId, title, body)
onDone()
},
enabled = title.isNotBlank()
) {
Text("Save")
}
}
)
}
) { innerPadding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.imePadding()
.padding(16.dp)
) {
OutlinedTextField(
value = title,
onValueChange = { title = it },
label = { Text("Title") },
singleLine = true,
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.height(12.dp))
OutlinedTextField(
value = body,
onValueChange = { body = it },
label = { Text("Note") },
modifier = Modifier.fillMaxWidth().weight(1f)
)
}
}
}
ایڈیٹر آپ کے داخل کردہ متن کو مقامی رکھتا ہے۔ rememberSaveableاس لیے آدھے لکھے ہوئے نوٹ گردش کے بعد بھی برقرار رہتے ہیں۔ کہ loaded پرچم ٹریک کرتا ہے کہ آیا کوئی موجودہ نوٹ پہلے ہی ڈیٹا بیس سے پڑھا جا چکا ہے۔ یہ اس طرح شروع ہوتا ہے: true نیا نوٹ (لوڈ کرنے کے لیے کچھ نہیں) اور false موجودہ والوں کے لیے۔
LaunchedEffect(noteId) یہ کورٹین چلاتا ہے جب کمپوز ایبل پہلی بار ظاہر ہوتا ہے اور اسے دوبارہ چلاتا ہے جب: noteId تبدیلی اس میں، اسکرین ViewModel سے ایک نوٹ مانگتی ہے اور ٹائٹل اور باڈی کو ٹیکسٹ فیلڈ میں کاپی کرتی ہے۔
چیک کر رہا ہے۔ loaded پری لوڈنگ اہم ہے۔ اس تصدیق کے بغیر، اسکرین کو گھمانے سے اثر دوبارہ شروع ہو جائے گا اور صارف نے ڈیٹا بیس میں محفوظ کردہ ورژن کے ساتھ جو درج کیا ہے اسے اوور رائٹ کر دیا جائے گا۔
OutlinedTextField مواد 3 ٹیکسٹ ان پٹ۔ تحریر ٹیکسٹ فیلڈ ایک کنٹرول شدہ جزو ہے۔ فیلڈ کچھ بھی دکھاتا ہے۔ value اسے داخل کریں اور جب صارف کچھ داخل کرے گا تو اسے بلایا جائے گا۔ onValueChange نئے متن کے ساتھ۔ اگر آپ اپ ڈیٹ کرنا بھول گئے ہیں۔ title اس کال بیک میں، فیلڈ منجمد دکھائی دیتی ہے کیونکہ کمپوز پچھلی قدر کو دوبارہ بناتا رہتا ہے۔ جب ٹائٹل خالی ہوتا ہے تو سیو بٹن کو غیر فعال کر دیا جاتا ہے اور جب دبایا جاتا ہے تو یہ محفوظ ہو جاتا ہے اور ویو ماڈل کے ذریعے بلایا جاتا ہے۔ onDone واپس جانے کے لیے۔
imePadding() آن اسکرین کی بورڈ کی اونچائی کے برابر نیچے جگہ شامل کریں، تاکہ کی بورڈ کے پیچھے چھپنے کے بجائے باڈی فیلڈ سکڑ جائے۔ پیچھے کا تیر AutoMirrored آئیکنز کا ایک سیٹ جو خود بخود دائیں سے بائیں تبدیل ہو جاتا ہے، بشمول عربی اور عبرانی۔
نیویگیشن 3 کے ساتھ نیویگیشن کیسے شامل کریں۔
نیویگیشن 3 Jetpack ٹیم کی طرف سے کمپوز فرسٹ نیویگیشن لائبریری ہے۔ پچھلی نیویگیشن کمپوز لائبریری کو ٹکڑوں کے لیے ڈیزائن کیے گئے سسٹم سے ڈھال لیا گیا تھا اور بیک اسٹیک کو اندرونی طور پر ملکیت سمجھا گیا تھا۔
ایکسپلوریشن 3 اس کے برعکس طریقہ اختیار کرتا ہے۔ بیک اسٹیک ایک باقاعدہ فہرست ہے جو صارف کی ملکیت ہے اور نیویگیشن کا مطلب ہے اس فہرست میں سے آئٹمز کو شامل کرنا یا ہٹانا۔ لائبریری فہرست کا مشاہدہ کرتی ہے اور سب سے اوپر کلید کے لیے ایک اسکرین دکھاتی ہے۔
سب سے پہلے، ہر اسکرین کے لیے کلیدوں کی وضاحت کریں۔ navigation/NavKeys.kt:
package com.yourname.quicknotes.navigation
import androidx.navigation3.runtime.NavKey
import kotlinx.serialization.Serializable
@Serializable
data object NoteList : NavKey
@Serializable
data class NoteEditor(val noteId: Long? = null) : NavKey
ہر ہدف ایک قسم ہے جو لاگو کرتا ہے: NavKey. NoteList چونکہ یہ کوئی دلیل نہیں لیتا ہے۔ data objectپڑھنے کے قابل سنگلٹن toString.
NoteEditor ایک اختیاری نوٹ ID پاس کرتا ہے۔ null اس کا مطلب ہے "ایک نیا نوٹ بنائیں۔” چونکہ آرگیومینٹس URL پاتھ میں موجود سٹرنگز کے بجائے ٹائپ کی گئی خصوصیات ہیں، کمپائلر غلط قسموں کو پاس کرنے جیسی غلطیوں کو پکڑتا ہے۔
کہ @Serializable تشریحات نیویگیشن 3 کو بیک اسٹیک کو بچانے کی اجازت دیتی ہیں جب اینڈرائیڈ ایپ کو پس منظر میں مار دیتا ہے اور صارف کے واپس آنے پر اسے بحال کرتا ہے۔ اسی لیے سیریلائزیشن پلگ ان کو پہلے شامل کیا گیا تھا۔
اب تبدیل کریں۔ MainActivity.kt کے ساتھ:
package com.yourname.quicknotes
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.runtime.Composable
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.runtime.rememberNavBackStack
import androidx.navigation3.ui.NavDisplay
import com.yourname.quicknotes.navigation.NoteEditor
import com.yourname.quicknotes.navigation.NoteList
import com.yourname.quicknotes.ui.NoteEditorScreen
import com.yourname.quicknotes.ui.NoteListScreen
import com.yourname.quicknotes.ui.NotesViewModel
import com.yourname.quicknotes.ui.theme.QuickNotesTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
QuickNotesTheme {
QuickNotesNavHost()
}
}
}
}
@Composable
fun QuickNotesNavHost() {
val backStack = rememberNavBackStack(NoteList)
val notesViewModel: NotesViewModel = viewModel(factory = NotesViewModel.Factory)
NavDisplay(
backStack = backStack,
onBack = { backStack.removeLastOrNull() },
entryProvider = entryProvider {
entry {
NoteListScreen(
viewModel = notesViewModel,
onAddNote = { backStack.add(NoteEditor()) },
onOpenNote = { id -> backStack.add(NoteEditor(id)) }
)
}
entry { key ->
NoteEditorScreen(
noteId = key.noteId,
viewModel = notesViewModel,
onDone = { backStack.removeLastOrNull() }
)
}
}
)
}
MainActivity اب آپ صرف ایک میزبان ہیں۔ enableEdgeToEdge() ایپ کو اسٹیٹس بار اور نیویگیشن بار کے پیچھے کھینچنے پر مجبور کرتا ہے (اگلے حصے میں اس پر مزید)۔ setContent موجودہ XML لے آؤٹ کو ایک ٹیمپلیٹ سے تیار کردہ تھیم میں لپیٹے ہوئے کمپوز ٹری سے بدل دیتا ہے۔
QuickNotesNavHost یہ وہ جگہ ہے جہاں نیویگیشن ہوتی ہے۔ rememberNavBackStack(NoteList) ایک ذخیرہ کرنے کے قابل بیک اسٹیک بناتا ہے جو فہرست اسکرین سے شروع ہوتا ہے۔ ویو ماڈل ایک بار اس فیکٹری کے ساتھ بنایا جاتا ہے جسے ہم نے پہلے بنایا تھا اور دونوں اسکرینوں پر اس کا اشتراک کیا جاتا ہے کیونکہ یہ ایک ہی نوٹس کے ساتھ کام کرتا ہے۔
NavDisplay اسٹیک کے اوپری حصے میں کلید کے لیے ایک آئٹم پیش کرتا ہے۔ کہ entryProvider بلاک ہر کلیدی قسم کو ایک کمپوز ایبل کے ساتھ نقشہ بناتا ہے: entry. کے لیے NoteEditorلیمبڈا کو ایک اہم مثال ملتی ہے، لہذا key.noteId براہ راست ان پٹ دلائل فراہم کرتا ہے۔
آگے بڑھنا ہے backStack.add(...) اور واپس کیا جاتا ہے backStack.removeLastOrNull(). یہ بنیادی نیویگیشن کے لیے مکمل API ہے۔
onBack کال کیا جاتا ہے جب صارف سسٹم بیک بٹن دباتا ہے یا بیک ایکشن کرتا ہے، ٹاپ لیول آئٹم کو ہٹا دیا جاتا ہے۔ نیویگیشن 3 لک بیک کو بھی سپورٹ کرتا ہے، ایک اینیمیشن جو پچھلی اسکرین پر جھانکتی ہے جب صارف بغیر کسی اضافی کوڈ کے سوائپ کرتا ہے۔
ابھی ایپ چلانے کی کوشش کریں۔ آپ کو ایک نوٹ شامل کرنے کے قابل ہونا چاہئے، دیکھیں کہ آیا نوٹ فہرست میں ظاہر ہوتا ہے، اس میں ترمیم کرنے کے لیے نوٹ پر ٹیپ کریں، نوٹ کو حذف کریں، اپنے آلے کو کچھ بھی کھوئے بغیر گھمائیں، اور ایپ کو بند کریں اور دوبارہ کھولیں اور آپ کے تمام نوٹ ابھی بھی برقرار ہیں۔
تھیم کیسے ترتیب دیں اور Edge-to-Edge ڈسپلے کو ہینڈل کریں۔
آپ کا ٹیمپلیٹ بنا دیا گیا ہے۔ ui/theme پیکج Color.kt، Type.ktاور Theme.kt. تھیم کی خصوصیات تقریباً درج ذیل ہیں:
@Composable
fun QuickNotesTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
MaterialTheme ہم ذیل میں تمام میٹریل 3 اجزاء کو رنگ اور نوع ٹائپ فراہم کرتے ہیں، کالوں کی اجازت دیتے ہیں جیسے: MaterialTheme.typography.titleMedium آپ کی سکرین کے کام میں۔
کہ when بلاکس رنگ سکیم کا انتخاب کرتے ہیں۔ Android 12 (API 31، VERSION_CODES.S) اور تازہ ترین ڈائنامک کلرز صارف کے وال پیپر سے ایک پیلیٹ تیار کرتے ہیں، جس سے ایپ کو باقی فون سے مماثلت ملتی ہے۔ اگر آپ اسے پچھلے ورژن میں ترتیب دیتے ہیں یا dynamicColor کو falseپننگ پر واپس جائیں۔ LightColorScheme اور DarkColorScheme میں بیان کیا گیا ہے۔ Color.kt. isSystemInDarkTheme() سسٹم ڈارک موڈ سیٹنگز پر عمل کریں۔
اگر آپ ہر جگہ مستقل برانڈ کے رنگ چاہتے ہیں، dynamicColor = false رنگوں میں ترمیم کریں۔ Color.kt. مٹیریل تھیم بلڈر ایک بیج کے رنگ سے مکمل ہلکے اور گہرے رنگوں کی سکیمیں تیار کر سکتا ہے۔
Edge-to-Edge کا مطلب یہ ہے کہ ایپ سسٹم کی سلاخوں کے پیچھے کھینچی گئی ہے بجائے کہ ان کے درمیان والے باکس کے اندر۔ اینڈرائیڈ 15 اور اس سے زیادہ کو نشانہ بنانے والی ایپس Edge-to-Edge ہیں، اور آپٹ آؤٹ فیچر اینڈرائیڈ 16 سے شروع ہو گیا ہے۔
کہ enableEdgeToEdge() کال MainActivity پرانے Android ورژنز میں برتاؤ کو یکساں بناتا ہے اور سسٹم بار کے آئیکنز کو صحیح ہلکے یا گہرے رنگوں پر سیٹ کرتا ہے۔ کیونکہ یہ دونوں سکرین استعمال کرتا ہے۔ Scaffold اور اسے لاگو کریں innerPaddingآپ کا مواد پہلے ہی بارز سے گریز کر رہا ہے۔
جب بغیر اسکرین کی تعمیر Scaffoldاس طرح موڈیفائر استعمال کریں: Modifier.safeDrawingPadding() یا Modifier.systemBarsPadding() ٹیکسٹ اور بٹنز کو اسٹیٹس بار اور ایکشن ایریا کے نیچے ظاہر ہونے سے روکتا ہے۔
کمپوز ایبل کی جانچ کیسے کریں۔
کیونکہ NoteListContent اگر یہ بے وطن ہے، تو آپ جعلی فہرست پاس کرکے ڈیٹا بیس یا ViewModel کے بغیر اس کی جانچ کر سکتے ہیں۔ بنانا app/src/androidTest/java/com/yourname/quicknotes/NoteListContentTest.kt:
package com.yourname.quicknotes
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import com.yourname.quicknotes.data.Note
import com.yourname.quicknotes.ui.NoteListContent
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
class NoteListContentTest {
@get:Rule
val composeRule = createComposeRule()
@Test
fun showsEmptyMessageWhenThereAreNoNotes() {
composeRule.setContent {
NoteListContent(notes = emptyList(), onAddNote = {}, onOpenNote = {}, onDeleteNote = {})
}
composeRule.onNodeWithText("No notes yet").assertIsDisplayed()
}
@Test
fun tappingANoteReportsItsId() {
var openedId: Long? = null
val notes = listOf(Note(id = 42, title = "Groceries", body = "Milk, eggs"))
composeRule.setContent {
NoteListContent(
notes = notes,
onAddNote = {},
onOpenNote = { openedId = it },
onDeleteNote = {}
)
}
composeRule.onNodeWithText("Groceries").performClick()
assertEquals(42L, openedId)
}
@Test
fun deleteButtonIsLabelledForScreenReaders() {
val notes = listOf(Note(id = 1, title = "Groceries", body = ""))
composeRule.setContent {
NoteListContent(notes = notes, onAddNote = {}, onOpenNote = {}, onDeleteNote = {})
}
composeRule.onNodeWithContentDescription("Delete Groceries").assertIsDisplayed()
}
}
createComposeRule() ایک خالی سرگرمی ترتیب دیں۔ setContent تمام کمپوز ایبلز کو تنہائی میں پیش کیا جا سکتا ہے۔ تحریری ٹیسٹ ایک سیمنٹک ٹری کے ذریعے عناصر کو تلاش کرتے ہیں، جو ایکسیبلٹی سروس کے ذریعہ پڑھا جانے والا وہی ڈھانچہ ہے۔ onNodeWithText مرئی متن کے ذریعہ نوڈس تلاش کریں۔ onNodeWithContentDescription قابل رسائی لیبل کے ساتھ ایک تلاش کریں۔
پہلا ٹیسٹ چیک کرتا ہے کہ آیا فہرست خالی ہونے پر خالی حالت ظاہر ہوتی ہے۔ دوسرے ٹیسٹ میں، ہم ایک لیمبڈا پاس کرتے ہیں جو موصول ہونے والی ID کو ریکارڈ کرتا ہے، نوٹ کو تھپتھپائیں، اور چیک کریں کہ اسکرین صحیح ID کی اطلاع دیتی ہے۔ تیسرا ٹیسٹ چیک کرتا ہے کہ آیا ڈیلیٹ بٹن میں کوئی معنی خیز لیبل ہے جو ایکسیسبیلٹی چیک کے طور پر بھی کام کرتا ہے۔ ان میں سے کوئی بھی ٹیسٹ روم یا ویو ماڈل کو نہیں چھوتا ہے۔ NoteListContent.
فائل پر دائیں کلک کریں اور ٹیسٹ چلانے کے لیے اسے منتخب کریں۔ ‘NoteListContentTest’ چلائیںیا ٹرمینل میں:
./gradlew connectedDebugAndroidTest
یہ گریڈل ٹاسک آپ کی ایپ کا ڈیبگ ورژن اور ایک علیحدہ ٹیسٹ APK بناتا ہے، منسلک ایمولیٹر یا فون دونوں پر انسٹال کرتا ہے، اور آپ کے تمام ٹیسٹ اس پر چلاتا ہے: androidTestHTML رپورٹس بنائیں۔ app/build/reports/androidTests/connected/. انسٹرومینٹیشن ٹیسٹنگ کے لیے چلانے والے آلے کی ضرورت ہوتی ہے۔ ایسی منطق کے لیے جو اینڈرائیڈ پر منحصر نہیں ہے، جیسے کہ توثیق کے اصول یا فارمیٹنگ مددگار، یونٹ ٹیسٹ استعمال کریں۔ test فولڈر تلاش کریں اور اسے چلائیں۔ ./gradlew testDebugUnitTestیہ آپ کے کمپیوٹر پر سیکنڈوں میں چلے گا۔
ریلیز کے لیے اپنی ایپ کو کیسے تیار کریں۔
ڈیبگ بلڈز ترقی کے لیے بہترین ہیں، لیکن کوئی بھی آپ کی ایپ انسٹال کرنے سے پہلے آپ کو کچھ چیزوں کا خیال رکھنے کی ضرورت ہے۔
ڈیفالٹ ایپ آئیکن کو تبدیل کریں۔
ٹیمپلیٹ ایک سادہ سبز اینڈرائیڈ آئیکن کے ساتھ آتا ہے۔ دائیں کلک کریں۔ res ایک فولڈر منتخب کریں نیا > تصویری اثاثہ. امیج اثاثہ اسٹوڈیو الگ الگ پیش منظر اور پس منظر کی تہوں کے ساتھ انکولی شبیہیں بناتا ہے، لہذا لانچر انہیں دائروں، لہروں یا دیگر شکلوں کے طور پر ماسک کر سکتا ہے۔
کچھ لانچرز کناروں کو کاٹ دیتے ہیں، لہذا اپنے آرٹ ورک کے اہم حصوں کو پیش منظر میں دکھائے گئے مرکزی محفوظ علاقے میں رکھیں۔ ایک ٹھوس رنگ کی تہہ بھی فراہم کریں۔ اینڈرائیڈ 13 اور اس سے اوپر والے اسے تھیم آئیکنز کے لیے استعمال کرتے ہیں۔ لانچر وال پیپر سے ملنے کے لیے تمام آئیکنز کو رنگ دیتا ہے، اور تھیم کے بغیر ایپس اس فون پر جگہ سے باہر نظر آئیں گی۔
ہارڈ کوڈ شدہ تاروں کو وسائل میں منتقل کریں۔
اس دستاویز میں اسکرینیں درج ذیل ہارڈ کوڈڈ سٹرنگ استعمال کرتی ہیں: "Quick Notes" اور "No notes yet" آپ کے کوڈ کو پڑھنے کے قابل رکھتا ہے۔ ایک حقیقی ایپ میں، آپ اسے کسی اور ایپ میں منتقل کرنا چاہیں گے۔ res/values/strings.xml اور ایک ساتھ پڑھیں stringResource(R.string.no_notes).
یہی وہ چیز ہے جو ترجمہ کو ممکن بناتی ہے کیونکہ Android صحیح کو چنتا ہے۔ strings.xml خود بخود آپ کی زبان کے لیے۔ اینڈروئیڈ اسٹوڈیو لنٹ وارننگز کے ساتھ ہارڈ کوڈ شدہ تاروں کو جھنڈا لگاتا ہے، اور آپ انہیں استعمال کرکے نکال سکتے ہیں: سٹرنگ وسائل نکالنا فوری ٹھیک کریں۔
R8 کا استعمال کرتے ہوئے ریلیز کی تعمیرات کو چیک کریں۔
میں نے اسے چالو کیا۔ isMinifyEnabled ریلیز بلڈ ٹائپ میں جو R8 کو آن کرتی ہے۔ R8 غیر استعمال شدہ کوڈ کو ہٹاتا ہے، کلاس اور طریقہ کے ناموں کو مختصر کرتا ہے، اور باقی کوڈ کو بہتر بناتا ہے۔ یہ عام طور پر آپ کی ایپ کو نمایاں طور پر چھوٹا اور تیز بناتا ہے، لیکن اس کوڈ کو توڑ سکتا ہے جو عکاسی پر انحصار کرتا ہے۔ اس کی وجہ یہ ہے کہ R8 یہ نہیں جان سکتا کہ کلاس استعمال کی جاتی ہے جب تک کہ کوئی چیز اس کا براہ راست حوالہ نہ دے۔
روم، کمپوز، اور kotlinx.serialization اپنے Keep کے اصول فراہم کرتے ہیں، اس لیے اس ایپ کے لیے کسی اضافی کنفیگریشن کی ضرورت نہیں ہے۔ اگر آپ ایک لائبریری شامل کر رہے ہیں جو عکاسی کا استعمال کرتی ہے، تو اس کی دستاویزات کو شامل کرنے کے لیے قواعد کے لیے چیک کریں۔ proguard-rules.pro.
R8 کے مسائل کی نشاندہی کرنے کا طریقہ یہ ہے کہ اسے اپ لوڈ کرنے سے پہلے ریلیز بلڈ کو چلایا جائے۔ منتخب کریں release تناؤ تعمیر > مختلف قسم کی تعمیر کو منتخب کریں۔ (آپ ڈیبگ سائننگ کلید استعمال کریں گے جب تک کہ آپ اگلے سیکشن میں ریلیز سائننگ سیٹ اپ نہیں کرتے۔) اسے انسٹال کریں اور تمام اسکرینوں پر کلک کریں۔ کریش جو صرف ریلیز بلڈز میں ظاہر ہوتے ہیں تقریباً ہمیشہ دیکھ بھال کے قوانین کی کمی کی وجہ سے ہوتے ہیں۔
غیر ضروری اجازتوں کو ہٹا دیں۔
ضم شدہ مینی فیسٹ کھولیں ( ضم شدہ مینی فیسٹ نیچے ٹیب AndroidManifest.xml) درج کردہ اجازتوں کو چیک کریں۔ لائبریریاں غیر مطلوب اجازتیں شامل کر سکتی ہیں، اور ان کی جانب سے اعلان کردہ کسی بھی اجازت کو Play Console کے اعلامیے میں جائز قرار دیا جانا چاہیے۔ کوئیک نوٹس ہر چیز کو مقامی طور پر اسٹور کرتا ہے اور اسے کسی اجازت کی ضرورت نہیں ہے۔ INTERNET. مزید برآں، کم اجازتوں سے Play کنسول کے ڈیٹا سیکیورٹی فارمز کو پُر کرنا آسان ہوجاتا ہے۔
اپ لوڈ کلید کیسے بنائیں اور اپنی ایپ پر دستخط کریں۔
انسٹالیشن سے پہلے تمام اینڈرائیڈ ایپس کو خفیہ طور پر سائن کیا جانا چاہیے۔ گوگل پلے ایک دو کلیدی نظام استعمال کرتا ہے جسے Play App Signature کہتے ہیں۔ آپ اپ لوڈ کلید کا استعمال کرتے ہوئے اپنے اپ لوڈز پر دستخط کرتے ہیں جسے آپ تخلیق اور اسٹور کرتے ہیں۔ گوگل اپ لوڈ کی تصدیق کرتا ہے، دستخط ہٹاتا ہے، اور ایپ سائننگ کلید کا استعمال کرتے ہوئے ایپ پر دوبارہ دستخط کرتا ہے جسے Google اپنے محفوظ انفراسٹرکچر میں اسٹور کرتا ہے۔ صارفین کو صرف ایپ سائننگ کی نظر آتی ہے۔
اس تقسیم کا فائدہ بحالی ہے۔ اگر آپ کی اپ لوڈ کلید گم ہو جاتی ہے یا لیک ہو جاتی ہے، تو آپ Google سے اسے Play Console میں دوبارہ ترتیب دینے کے لیے کہہ سکتے ہیں اور آپ کے صارفین کو اپ ڈیٹس موصول ہوتے رہیں گے۔ Play App Signing کے موجود ہونے سے پہلے، اپنی منفرد سائننگ کلید کو کھونے کا مطلب ہے کہ آپ اپنی ایپ کو دوبارہ کبھی اپ ڈیٹ نہیں کر سکتے۔
نئی ایپس کے لیے Play App Signing کی ضرورت ہے، اور یہی چیز Android App Bundle کو ممکن بناتی ہے کیونکہ Google کو ہر ڈیوائس کے لیے ایک بہتر APK بنانے کے لیے سائننگ کلید کی ضرورت ہوتی ہے۔
یہ استعمال کرتے ہوئے اپنے ٹرمینل میں اپ لوڈ کلید بنائیں: keytoolیہ اینڈرائیڈ اسٹوڈیو کے ساتھ بنڈل JDK کے ساتھ آتا ہے۔
keytool -genkeypair -v \
-keystore ~/keys/quicknotes-upload.jks \
-alias upload \
-keyalg RSA -keysize 2048 \
-validity 10000
یہ کمانڈ درج ذیل جگہ پر جاوا کی اسٹور فائل بناتی ہے۔ ~/keys/quicknotes-upload.jks عرف کے ساتھ ایک کلیدی جوڑا پر مشتمل ہے۔ upload. -keyalg RSA -keysize 2048 وہ کلیدی قسم اور طاقت سیٹ کریں جس کی Google Play کو توقع ہے۔ -validity 10000 یہ کلید کو تقریباً 27 سال تک کارآمد بناتا ہے۔ Google Play کو آپ کی کلید کم از کم اکتوبر 2033 تک درست رہنے کی ضرورت ہے۔
ٹول آپ کو آپ کے کی اسٹور پاس ورڈ کا اشارہ کرے گا اور سرٹیفکیٹ میں محفوظ کردہ نام اور تنظیم کے بارے میں پوچھے گا۔ اگر keytool یہ آپ کے راستے میں نہیں ہے۔ اینڈرائیڈ اسٹوڈیو بنائیں > ایک دستخط شدہ ایپ بنڈل یا APK بنائیں وزرڈ ڈائیلاگ باکس کے ذریعے ایک ہی کلید بنا سکتا ہے۔
اپنے کی اسٹور کو اپنے پروجیکٹ فولڈر سے باہر کہیں اسٹور کریں اور اسے پاس ورڈ مینیجر یا انکرپٹڈ اسٹوریج میں بیک اپ کریں۔ کبھی بھی گٹ کا عہد نہ کریں۔
اگلا keystore.properties اپنے دستخط کی تفصیلات رکھنے کے لیے، فائل کو اپنے پروجیکٹ کے روٹ میں محفوظ کریں۔
storeFile=/Users/yourname/keys/quicknotes-upload.jks
storePassword=your-keystore-password
keyAlias=upload
keyPassword=your-key-password
یہ فائل ان چار اقدار کا نقشہ بناتی ہے جن کی گریڈل کو ایک تعمیر پر دستخط کرنے کی ضرورت ہوتی ہے: کی اسٹور پاتھ، کی اسٹور پاس ورڈ، اندرونی کلیدی عرف، اور متعلقہ کلیدی پاس ورڈ۔ انہیں علیحدہ فائلوں میں رکھنے سے آپ رازوں کا اشتراک کیے بغیر پروجیکٹس کا اشتراک کرسکتے ہیں۔ شامل کریں keystore.properties اور *.jks آپ کو .gitignore جلد ہی
پھر فائل لوڈ کریں۔ app/build.gradle.kts ایک ریلیز دستخط کنفیگریشن بنائیں۔
import java.util.Properties
val keystorePropertiesFile = rootProject.file("keystore.properties")
val keystoreProperties = Properties().apply {
if (keystorePropertiesFile.exists()) {
keystorePropertiesFile.inputStream().use { load(it) }
}
}
android {
// namespace, compileSdk, defaultConfig as before
signingConfigs {
if (keystorePropertiesFile.exists()) {
create("release") {
storeFile = file(keystoreProperties.getProperty("storeFile"))
storePassword = keystoreProperties.getProperty("storePassword")
keyAlias = keystoreProperties.getProperty("keyAlias")
keyPassword = keystoreProperties.getProperty("keyPassword")
}
}
}
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
signingConfig = signingConfigs.findByName("release")
}
}
}
کہ import فائل کے اوپری حصے میں، یعنی plugins بلاک اسکرپٹ یہ ہے: keystore.properties کی طرف سے Properties فائل ایک آبجیکٹ ہے اگر یہ موجود ہے۔
اندر signingConfigsایک نامزد کنفیگریشن بناتا ہے۔ release لیکن یہ صرف اس صورت میں ممکن ہے جب فائل موجود ہو۔ یہ تحفظ اہم ہے کیونکہ نہ تو ریپوزٹری کو کلون کرنے والے ٹیم کے ممبران اور نہ ہی ٹیسٹ چلانے والے CI سرور کے پاس کلیدی اسٹور ہے اور ڈیبگ آپریشنز کے لیے ابھی بھی بلڈ کو کام کرنے کی ضرورت ہے۔
ریلیز کی تعمیر کی قسم استعمال کرتی ہے: findByName("release")یہ لوٹتا ہے: null جب ترتیب موجود نہیں ہے تو ناکام ہونے کے بجائے۔ CI پائپ لائنز عام طور پر ان اقدار کو فائلوں کے بجائے ماحولیاتی متغیرات یا خفیہ مینیجرز سے پڑھتی ہیں۔
اینڈرائیڈ ایپ بنڈل کیسے بنایا جائے۔
Google Play نئی ایپس کے لیے APKs کو قبول نہیں کرتا ہے۔ اس کے بجائے، ایک اینڈرائیڈ ایپ بنڈل (AAB) اپ لوڈ کریں۔ بنڈل میں اسکرین کی تمام کثافتوں، سی پی یو آرکیٹیکچرز، اور زبانوں کے لیے تمام مرتب کردہ کوڈ اور وسائل شامل ہیں۔ جب کوئی صارف آپ کی ایپ انسٹال کرتا ہے، تو Google Play ایک APK بناتا ہے جس میں صرف وہی ہوتا ہے جو اس مخصوص ڈیوائس کے لیے ضروری ہوتا ہے۔ انگریزی لوکیلز اور اعلی کثافت والی اسکرین والے فون جاپانی تار یا کم کثافت والی تصاویر ڈاؤن لوڈ نہیں کریں گے، جس کے نتیجے میں ڈاؤن لوڈ سائز چھوٹے ہوں گے۔
ٹرمینل میں بنڈل بنائیں۔
./gradlew bundleRelease
یہ ٹاسک ریلیز ویرینٹ کو مرتب کرتا ہے، R8 چلاتا ہے، اپ لوڈ کلید کے ساتھ نتائج پر دستخط کرتا ہے، اور بنڈل کو محفوظ کرتا ہے۔ app/build/outputs/bundle/release/app-release.aab. آپ بھی استعمال کر سکتے ہیں بنائیں > ایک دستخط شدہ ایپ بنڈل یا APK بنائیں اینڈرائیڈ اسٹوڈیو وزرڈ کے ذریعے وہی کام کرتا ہے۔ اگر دستخط کی غلطی کی وجہ سے تعمیر ناکام ہوجاتی ہے، تو راستے اور پاس ورڈ کو دوبارہ چیک کریں: keystore.properties.
براہ کرم اپ لوڈ کرنے سے پہلے اس میں اضافہ کریں۔ versionCode ہر نئی تعمیر کے لیے جو آپ Play پر بھیجتے ہیں۔ اپنے پہلے اپ لوڈ کے لیے آپ استعمال کر سکتے ہیں: 1اگلا ہے۔ 2 یا زیادہ، وغیرہ۔ پلے بنڈلز کو مسترد کرتا ہے جو: versionCode ہم اسے پہلے ہی دوسرے ٹیسٹ ٹریکس پر دیکھ چکے ہیں۔ versionName یہ کچھ بھی ہوسکتا ہے، لیکن سیمنٹک ورژن یہ ہے: 1.0.0، 1.0.1اور 1.1.0 آپ اور آپ کے صارفین کے لیے پیروی کرنا آسان ہے۔
گوگل پلے ڈویلپر اکاؤنٹ کیسے بنایا جائے۔
play.google.com/console پر جائیں اور اس گوگل اکاؤنٹ کے ساتھ سائن ان کریں جس کے ساتھ آپ ڈویلپر اکاؤنٹ کے مالک بننا چاہتے ہیں۔ چونکہ بعد میں ملکیت کو منتقل کرنا تکلیف دہ ہے، ہم آپ کے ذاتی Gmail کو استعمال کرنے کے بجائے ایک وقف شدہ اکاؤنٹ بنانے کی تجویز کرتے ہیں۔
آپ سے ذاتی اکاؤنٹ اور تنظیمی اکاؤنٹ کے درمیان انتخاب کرنے کو کہا جائے گا۔ ذاتی اکاؤنٹس صرف ذاتی استعمال کے لیے ہیں۔ تنظیمی اکاؤنٹس رجسٹرڈ کاروباروں کے لیے ہیں اور ان کے لیے ایک DUNS نمبر درکار ہے، جو ڈن اور بریڈسٹریٹ کی طرف سے فراہم کردہ ایک مفت کاروباری شناخت کنندہ ہے۔ اس نمبر کو حاصل کرنے میں ایک ہفتہ یا اس سے زیادہ وقت لگ سکتا ہے۔
آپ کے انتخاب کاغذی کارروائی سے زیادہ متاثر کرتے ہیں۔ نئے انفرادی اکاؤنٹس کو پروڈکشن میں شائع کرنے سے پہلے ایک پرائیویٹ ٹیسٹنگ کا دورانیہ مکمل کرنا چاہیے (جیسا کہ ذیل میں بیان کیا گیا ہے)؛ تنظیمی اکاؤنٹس اس ضرورت سے مستثنیٰ ہیں۔ آپ کے ڈویلپر کا نام، تنظیم، اور، منیٹائزڈ ذاتی اکاؤنٹس کے لیے، آپ کا پتہ آپ کے اسٹور کی فہرست میں عوامی طور پر ظاہر کیا جائے گا۔
یک وقتی رجسٹریشن فیس $25 ادا کریں اور شناخت کی تصدیق مکمل کریں۔ Google آپ سے آپ کا قانونی نام اور پتہ پوچھے گا جو آپ کی حکومت کی طرف سے جاری کردہ ID سے مماثل ہے، آپ کے رابطے کے ای میل اور فون نمبر کی تصدیق کرے گا، اور ذاتی اکاؤنٹس کے لیے، آپ سے Play Console ایپ کو انسٹال اور سائن ان کر کے اس بات کی تصدیق کرنے کے لیے کہے گا کہ آپ کو ایک فزیکل اینڈرائیڈ ڈیوائس تک رسائی حاصل ہے۔ تصدیق میں عام طور پر کچھ دن لگتے ہیں۔ آپ اس کے مکمل ہونے تک شائع نہیں کر سکتے، لہذا یہ مرحلہ جلد شروع کریں، یہاں تک کہ آپ کی ایپ مکمل ہو جائے۔
پورے Android ایکو سسٹم میں شناخت کی تصدیق زیادہ اہم ہوتی جا رہی ہے۔ 2026 کے اواخر سے، Google تصدیق شدہ اینڈرائیڈ ڈیوائسز پر انسٹال کردہ تمام ایپس کے لیے ڈویلپر سرٹیفیکیشن کو رول آؤٹ کرنا شروع کر دے گا، بشمول Google Play سے باہر تقسیم کردہ ایپس، بہت کم ممالک میں۔ اگر آپ Play کے ذریعے تقسیم کر رہے ہیں اور آپ نے Play Console کی توثیق مکمل کر لی ہے، تو آپ پہلے ہی احاطہ کر چکے ہیں، لیکن آپ کے اکاؤنٹ کی تفصیلات درست ہونے کو یقینی بنانے کی ایک اور وجہ ہے۔
Play Console میں ایپ کیسے بنائی جائے۔
ایک بار جب آپ کے اکاؤنٹ کی تصدیق ہو جائے تو، اگلا پر کلک کریں۔ ایک ایپ بنائیں Play Console کے ہوم پیج پر، اپنی ایپ کا نام درج کریں (30 حروف تک) جیسا کہ آپ اسے Google Play پر ظاہر کرنا چاہتے ہیں، اپنی پسند کی زبان منتخب کریں، اور پھر ایپ بلکہ کھیلمنتخب کریں مفت یا ادا کیا.
یہاں محتاط رہیں۔ آپ ایک بامعاوضہ ایپ کو بعد میں مفت میں تبدیل کر سکتے ہیں، لیکن مفت ایپ کو کبھی بھی ادا شدہ ایپ میں تبدیل نہیں کیا جا سکتا۔ اگر آپ چاہتے ہیں کہ بل وصول کیا جائے تو اس کے بجائے درون ایپ خریداریاں یا سبسکرپشنز استعمال کریں۔ ڈیولپر پروگرام کی پالیسیوں اور یو ایس ایکسپورٹ لا اسٹیٹمنٹ سے اتفاق کریں اور اگلا پر کلک کریں۔ ایک ایپ بنائیں.
ایپ ڈیش بورڈ ظاہر ہوتا ہے، سیکشن کے لحاظ سے گروپ کردہ کاموں کی چیک لسٹ دکھاتا ہے۔ Play Console ایک معقول ترتیب میں آپ کو اس کے ذریعے لے جاتا ہے، لیکن اگلے دو حصے ان حصوں کی نشاندہی کرتے ہیں جو لوگوں کو سب سے زیادہ الجھن میں ڈالتے ہیں۔
اپنے ایپ کے مواد کا اعلان کیسے مکمل کریں۔
نیچے پالیسیاں اور پروگرام > ایپ کا مواد (ڈیش بورڈ چیک لسٹ سے بھی منسلک) گوگل آپ کی ایپ کے بارے میں سوالات کا ایک سلسلہ پوچھتا ہے۔ تمام ایپس کو کسی بھی ٹریک پر ریلیز کرنے سے پہلے اندرونی جانچ سمیت تمام مراحل کو مکمل کرنا چاہیے۔
کہ رازداری کی پالیسی آپ کو اس صفحہ کے لیے ایک عوامی URL کی ضرورت ہوگی جو یہ بتاتا ہو کہ آپ کی ایپ کون سا ڈیٹا اکٹھا کرتی ہے اور اسے کیسے استعمال کرتی ہے۔ یہ ضروری ہے یہاں تک کہ اگر ایپ کچھ بھی جمع نہیں کرتی ہے۔ فوری نوٹس کے لیے، ایک مختصر صفحہ کافی ہوگا: نوٹس صرف آپ کے آلے پر محفوظ کیے جائیں گے اور کوئی ڈیٹا اکٹھا یا شیئر نہیں کیا جائے گا۔ آپ اسے گٹ ہب پیج، گوگل سائٹس، یا آپ کی اپنی ویب سائٹ کے طور پر میزبانی کر سکتے ہیں، لیکن یہ ایک کام کرنے والا، عوامی ویب صفحہ ہونا چاہیے، نہ کہ پی ڈی ایف یا گوگل دستاویز جس کے لیے لاگ ان کی ضرورت ہے۔
ایپ تک رسائی ایپ کے حصے آپ سے پوچھتے ہیں کہ کیا آپ کو لاگ ان کرنے کی ضرورت ہے۔ اگر ایسا ہے تو، آپ کو جانچ کی اسناد فراہم کرنے کی ضرورت ہوگی تاکہ جائزہ لینے والا تمام اسکرینوں تک رسائی حاصل کر سکے۔ QuickNote کے لیے کوئی لاگ ان نہیں ہے، لہذا خصوصی رسائی کے بغیر تمام خصوصیات کو استعمال کرنے کا انتخاب کریں۔
اشتہار پوچھیں کہ کیا آپ کی ایپ میں اشتہارات ہیں، بشمول SDKs کے اشتہارات جو آپ نے خود نہیں لکھے۔ اشتہارات پر مشتمل ایپس کو آپ کے اسٹور کی فہرست میں ‘اشتہار پر مشتمل ہے’ کا لیبل لگایا جائے گا اور جائزہ لینے والوں کے ذریعہ ان کا جائزہ لیا جائے گا، لہذا براہ کرم ایمانداری سے جواب دیں۔
مواد کی درجہ بندی تشدد، جنسی مواد، زبان، کنٹرول شدہ مادہ، صارف کی بات چیت، اور اسی طرح کے موضوعات پر IARC کے سوالنامے۔ آپ کے جوابات دنیا بھر کے خطوں کے لیے عمر کی درجہ بندی پیدا کرتے ہیں (شمالی امریکہ میں ESRB، یورپ میں PEGI وغیرہ)۔ صارف کی تخلیق کردہ اشتراک کے بغیر نوٹس ایپس کو ہر جگہ سب سے کم درجہ دیا جاتا ہے۔
ہدف کے سامعین اور مواد پوچھیں کہ ایپ کس عمر کے گروپ کے لیے بنائی گئی ہے۔ اگر آپ کی ایپ میں 13 سال سے کم عمر کے بچے شامل ہیں، تو یہ فیملی پالیسی کے تحت آتی ہے، جس میں اشتہارات، ڈیٹا اکٹھا کرنے اور SDKs سے متعلق تقاضے شامل ہوتے ہیں۔ عام مقصد کی افادیت کے لیے، 18+ یا 13 سے شروع ہونے والی رینج کا انتخاب کریں، جب تک کہ بچے آپ کے حقیقی سامعین نہ ہوں۔
ڈیٹا کی حفاظت یہ سب سے تفصیلی شکل ہے۔ اپنی اسٹور کی فہرست میں ایک ‘ڈیٹا سیکیورٹی’ سیکشن بنائیں اور پوچھیں کہ آیا آپ کی ایپ صارف کے ہر قسم کے ڈیٹا کو جمع کرتی ہے یا شیئر کرتی ہے، بشمول مقام، روابط، ذاتی معلومات، اور ایپ کی سرگرمی، اور کن مقاصد کے لیے۔ کیچ یہ ہے کہ آپ اپنی ایپ میں موجود تمام لائبریریوں سے جمع کیے گئے ڈیٹا کے ذمہ دار ہیں، نہ صرف اپنے کوڈ کے۔ تجزیات، خرابی کی اطلاع دہندگی، اور اشتہاری SDKs سبھی ڈیٹا اکٹھا کرتے ہیں اور ان کا اعلان ہونا ضروری ہے۔
زیادہ تر بڑے SDKs ایسے صفحات شائع کرتے ہیں جو اس بات کی وضاحت کرتے ہیں کہ کیا اعلان کرنا ہے۔ کوئیک نوٹس ایسی کسی لائبریری کا استعمال نہیں کرتا ہے اور نیٹ ورک کالز نہیں کرتا ہے، لہذا ایماندارانہ جواب یہ ہے کہ کوئی ڈیٹا اکٹھا یا شیئر نہیں کیا جاتا ہے۔
بقیہ اعلانات (نیوز ایپ، گورنمنٹ ایپ، فنانشل فیچرز، ہیلتھ ایپ، وغیرہ) ہاں یا نہیں میں سوالات ہیں۔ نوٹ لینے والی ایپس کے لیے، ہر ایک کا جواب نہیں ہے۔
اپنے اسٹور کی فہرست کو کیسے ترتیب دیں۔
اسٹور رجسٹریشن کی معلومات (نیچے) صارف کی ترقی> اسٹور کی معلومات> ڈیفالٹ اسٹور کی فہرستیہ وہی ہے جو لوگ اسے انسٹال کرنے سے پہلے دیکھتے ہیں۔ آپ کو درج ذیل متن اور گرافکس کی ضرورت ہوگی:
| میراث | تقاضے |
|---|---|
| ایپ کا نام | 30 حروف تک |
| مختصر وضاحت | 80 حروف تک |
| مکمل تفصیل | 4000 حروف تک |
| ایپ کا آئیکن | 512 x 512px PNG، 32 بٹ (بشمول الفا)، 1MB تک |
| نمایاں گرافکس | 1024 x 500px JPEG یا 24-bit PNG، کوئی الفا نہیں۔ |
| موبائل فون اسکرین شاٹ | 2 سے 8 تصاویر، 16:9 یا 9:16، 320 سے 3840 پکسلز فی سائیڈ |
بائیں کالم میں ہر مطلوبہ اثاثہ کی فہرست ہے، اور دائیں کالم میں گوگل کی موجودہ حدود کی فہرست ہے۔
مختصر تفصیل تلاش کے نتائج میں آپ کی ایپ کے نام کے بالکل نیچے ظاہر ہوگی، اس لیے اسے ایک لائن کی تفصیل کے طور پر سمجھیں اور اپنے اہم ترین مطلوبہ الفاظ درج کریں۔ مکمل تفصیل وہ ہے جہاں فعالیت کو سادہ زبان میں بیان کیا گیا ہے۔ گوگل پلے انہیں تلاش کے لیے انڈیکس کرتا ہے، لیکن مطلوبہ الفاظ کو شامل کرنا ان کی میٹا ڈیٹا پالیسیوں کی خلاف ورزی کرتا ہے اور اس کے نتیجے میں آپ کی فہرست کو مسترد کیا جا سکتا ہے۔
512px کا آئیکن ایپ کے اندر موجود لانچر آئیکن سے الگ ہونا چاہیے، لیکن اس کی شکل ایک جیسی ہونی چاہیے۔ گوگل راؤنڈنگ ماسک کو خود ہی لاگو کرے گا، لہذا پہلے سے گول کونوں یا سائے کے بغیر ایک مکمل مربع اپ لوڈ کریں۔ فیچر گرافک کچھ لے آؤٹس میں فہرست کے اوپری حصے میں ظاہر ہوتا ہے اور اس وقت ظاہر ہوتا ہے جب آپ کی ایپ کو فروغ دیا جاتا ہے۔
انسٹال کرنے یا نہ کرنے کا فیصلہ کرنے میں اسکرین شاٹس سب سے بڑا عنصر ہیں۔ ایمولیٹر ٹول بار پر کیمرہ بٹن کا استعمال کرتے ہوئے ایمولیٹر سے کیپچر لینے پر غور کریں اور مختصر عنوانات شامل کریں جو یہ بیان کریں کہ ہر اسکرین کیا کرتی ہے۔ اس بات کو یقینی بنانے کے لیے کہ آپ کی ایپ ٹیبلیٹس اور Chromebooks پر اچھی طرح دکھاتی ہے، Play Console میں ٹیبلیٹ کے اسکرین شاٹس کے لیے ایک علیحدہ سلاٹ ہے، یہ فراہم کرنے سے ان آلات پر آپ کی فہرستیں بہتر ہوں گی۔
بھی اسٹور کی ترتیبات ایپ کیٹیگریز والا صفحہ (فوری نوٹس کے لیے بہترین پیداواری صلاحیت)، ٹیگز، اور رابطہ ای میل پتے۔ رابطے کی ای میلز فہرست میں عوامی طور پر دکھائے جاتے ہیں۔
داخلی اور نجی ٹیسٹ کیسے چلائیں۔
گوگل پلے میں متعدد ریلیز ٹریک ہیں۔ اندرونی ٹیسٹنگ کا ہدف 100 ٹیسٹرز تک ہے جو ہمیں ای میل کے ذریعے شامل کرتے ہیں، اور تعمیرات عام طور پر کم سے کم جائزے کے ساتھ منٹوں میں دستیاب ہوتے ہیں۔ پرائیویٹ ٹیسٹنگ بڑے گروپوں کو نشانہ بناتی ہے جنہیں آپ ای میل فہرستوں یا گوگل گروپس کے ذریعے کنٹرول کرتے ہیں۔ عوامی جانچ کسی کو بھی اپنے اسٹور کی فہرست سے حصہ لینے کی اجازت دیتی ہے۔ پروڈکشن ایک عوامی ریلیز ہے۔
اندرونی جانچ کے ساتھ شروع کریں۔ تحریک ٹیسٹنگ اور ریلیز > ٹیسٹنگ > اندرونی ٹیسٹنگٹیسٹرز کی ایک ای میل فہرست بنائیں (آپ خود کو شامل کر سکتے ہیں) ایک نئی ریلیز بنائیں. پہلی بار، آپ کو Play کنسول میں اپنے Play ایپ کے دستخط کی توثیق کرنے کے لیے کہا جائے گا۔ پہلے سے طے شدہ کو قبول کریں تاکہ Google آپ کی ایپ سائننگ کلید بنا اور محفوظ کر سکے۔ پھر app-release.aabاپنے ریلیز نوٹس درج کریں اور اپنا ریلیز ورژن جاری کریں۔
اپنا آپٹ ان لنک یہاں شیئر کریں: ٹیسٹر ٹیسٹر کے ساتھ ٹیپ کریں۔ اپنے براؤزر میں دعوت نامہ قبول کرنے کے بعد، آپ کسی بھی دوسری ایپ کی طرح گوگل پلے سے ایپ انسٹال کر سکتے ہیں۔ سائیڈ لوڈنگ کے بجائے پلے کے ذریعے انسٹال کرنا ثابت کرتا ہے کہ بنڈل پلے ایپ سائننگ کے ذریعے اینڈ ٹو اینڈ کام کرتا ہے۔
اگر آپ ذاتی ڈویلپر اکاؤنٹ بناتے ہیں، تو آپ ابھی پروڈکشن میں شائع نہیں کر پائیں گے۔ 13 نومبر 2023 کے بعد بنائے گئے ذاتی اکاؤنٹس کو کم از کم 12 ٹیسٹرز کے ساتھ نجی ٹیسٹ چلانے کی ضرورت ہوگی جنہوں نے پچھلے 14 دنوں میں مسلسل رضامندی دی ہے۔ گوگل نے یہ قاعدہ کم کوالٹی، ترک شدہ ایپس کو کم کرنے کے لیے متعارف کرایا، یہ ایک ایسا قدم ہے جو زیادہ تر نئے ڈویلپرز کو حیران کر دیتا ہے۔
اسے نیچے ترتیب دیں۔ ٹیسٹنگ اور ریلیز > ٹیسٹنگ > پرائیویٹ ٹیسٹنگ. ایک ٹریک بنائیں، اپنی ای میل لسٹ یا گوگل گروپ کے ذریعے ٹیسٹرز کو شامل کریں، اور اپنی ریلیز اپ لوڈ کریں (آپ اپنی داخلی جانچ میں اسی تعمیر کو فروغ دے سکتے ہیں)۔
ٹیسٹرز کو جلد بھرتی کریں۔ دوست، خاندان، ساتھی، اور ڈویلپر کمیونٹیز مشترکہ ذرائع ہیں۔ 12 ٹیسٹرز کو درحقیقت لنک کے ذریعے آپٹ ان کرنے کی ضرورت ہے، اور اسی طرح کتنے ٹیسٹرز آپٹ ان رہتے ہیں، لہذا اگر کوئی 10 دن کو چلا جاتا ہے، تو گھڑی متاثر ہوگی۔ ایک درجن یا اس سے زیادہ کی منصوبہ بندی کریں تاکہ آپ کے پاس گنجائش باقی رہے۔
اپنے 14 دن کو نتیجہ خیز استعمال کریں۔ اپنے ٹیسٹرز سے آپ کو تاثرات بھیجنے اور کسی بھی تنازعات کا مشاہدہ کرنے کو کہیں۔ نگرانی اور بہتری > اینڈرائیڈ وائٹلزجب آپ مسائل کو حل کرتے ہیں تو اپ ڈیٹ شدہ تعمیرات کو پش کریں۔ ہر اپ ڈیٹ کے لیے اعلیٰ سطح کی ضرورت ہوتی ہے۔ versionCode. جب ہم پیداوار تک رسائی کی درخواستوں کا جائزہ لیتے ہیں، تو ہم نہ صرف یہ دیکھتے ہیں کہ آیا وہ وقت کے ساتھ ساتھ ہیں، بلکہ یہ بھی جانچتے ہیں کہ آیا ایپ کو بہتر بنانے کے لیے جانچ کا استعمال کیا گیا ہے۔
14 دنوں کے بعد، آپ کا ڈیش بورڈ ظاہر ہوگا: پیداوار کی درخواست بٹن ایپلیکیشن پوچھتی ہے کہ آپ نے ٹیسٹرز کو کیسے بھرتی کیا، آپ کو کیا فیڈ بیک ملا، اس کے نتیجے میں آپ نے کیا تبدیلیاں کیں، اور کیا آپ کی ایپ ریلیز ہونے کے لیے تیار ہے۔ براہ کرم خصوصی طور پر جواب دیں۔ مبہم جوابات جیسے "امتحان دینے والوں نے اسے پسند کیا” اکثر مسترد ہونے کی وجوہات ہیں۔ منظوری میں عام طور پر ایک ہفتہ لگتا ہے۔
پروڈکشن میں شائع کرنے کا طریقہ
پیداوار تک رسائی حاصل کرنے کے بعد، اس پر جائیں: ٹیسٹ اور ریلیز > پیداوار اور کلک کریں ایک نئی ریلیز بنائیں. آپ ٹیسٹ ٹریک سے ایک نیا بنڈل اپ لوڈ کر سکتے ہیں یا بنڈل کو فروغ دے سکتے ہیں۔ ‘نیا کیا ہے’ فیلڈ میں ریلیز نوٹس شامل کریں۔ صارفین اسے اپنے اسٹور کی فہرست میں دیکھیں گے اور اطلاعات کو اپ ڈیٹ کریں گے۔
ان ممالک اور علاقوں کو منتخب کریں جہاں آپ کی ایپ لانچ ہونے سے پہلے دستیاب ہے۔ ملک/علاقہ پیداوار کے صفحے پر ٹیب. پھر رہائی کے خلاصے کا جائزہ لیں۔ Play Console ایسی خرابیاں دکھاتا ہے جو ریلیز کو روکتی ہیں (جیسے نامکمل اعلانات) اور انتباہات جو نہیں کرتی ہیں (جیسے غائب ترجمہ)۔ غلطی کو ٹھیک کرنے اور انتباہ کو پڑھنے کے بعد: جائزہ کے لیے بھیجیں۔.
ہر نئی ایپ اور ہر اپ ڈیٹ کا جائزہ لیا جاتا ہے۔ نئی ایپس کے لیے، پہلے جائزے میں 7 دن اور کچھ معاملات میں زیادہ وقت لگ سکتا ہے۔ بعد کے اپ ڈیٹس میں عام طور پر ایک یا دو دن لگتے ہیں۔ اگر آپ کی ایپ مسترد ہو جاتی ہے، تو آپ کو Play Console اور ڈویلپر کی ای میل میں پالیسی کی خلاف ورزیوں کی تفصیل موصول ہوگی۔ مسئلہ حل کریں versionCodeمنتخب کریں اور دوبارہ جمع کرائیں۔
پہلے سے طے شدہ طور پر، منظور شدہ ریلیز کا جائزہ پاس ہوتے ہی شائع کیا جاتا ہے۔ رہائی کی تاریخ کو کنٹرول کرنے کے لیے نظم پبلشنگ پر اشاعت کا جائزہ صفحہ آپ کی تبدیلیاں منظور ہونے کے بعد، ان کے کلک کرنے کا انتظار کریں۔ پوسٹبلاگ پوسٹس یا مارکیٹنگ کی تاریخوں کو مربوط کرتے وقت یہ مفید ہے۔
رہائی کے بعد اپ ڈیٹس کیسے بھیجیں۔
ایپ کو جاری کرنا کسی پروجیکٹ کا اختتام نہیں ہے، یہ دیکھ بھال کا آغاز ہے۔ تین عادتیں اپڈیٹس کو ہموار بناتی ہیں۔
پہلا مرحلہ وار رول آؤٹ ہے۔ جب آپ پروڈکشن اپ ڈیٹ جاری کرتے ہیں، تو Play Console آپ کو اپنے صارفین کا فیصد منتخب کرنے کی اجازت دیتا ہے (مثلاً 10%) جنہیں وہ اپ ڈیٹ موصول ہوگا۔ اینڈرائیڈ وائٹلز میں ایک یا دو دن کے لیے اپنے کریش اور ایپ ناٹ ریسپانسنگ (ANR) ریٹ دیکھیں، پھر ریٹ بڑھائیں۔ اگر کچھ غلط ہو جاتا ہے تو ہم ریلیز کو روک سکتے ہیں اور ہمارے باقی صارفین کو کرپٹ ورژن نہیں ملے گا۔
10% پر مسائل کو پکڑنا ضروری ہے کیونکہ Google یہ فیصلہ کرنے کے لیے Android Vitals کی حد کا استعمال کرتا ہے کہ آیا آپ کی فہرست میں وارننگ ڈسپلے کرنا ہے یا اس کی مرئیت کو کم کرنا ہے۔
دوسرا اپنے ہدف SDK کو تازہ ترین رکھنا ہے۔ Google Play پر کم از کم رقم میں اضافہ کریں۔ targetSdk ہر سال اپ ڈیٹس کے لیے۔ اگر آپ کی ایپ پیچھے رہ جاتی ہے، تو آپ مزید اپ ڈیٹس شائع نہیں کر سکتے، اور رعایتی مدت کے بعد، تازہ ترین Android ورژنز پر نئے صارفین کو آپ کی ایپ نہیں مل سکتی ہے۔
تاثر targetSdk یہ صرف نمبروں کو تبدیل کرنے کے بارے میں نہیں ہے۔ ہر نئے اینڈرائیڈ ورژن کے لیے رویے کی تبدیلیوں کا صفحہ پڑھیں اور اسی کے مطابق اپنی ایپ کی جانچ کریں۔ ایک اچھا معمول یہ ہے کہ ہر موسم بہار میں ٹیسٹ ڈیوائس یا ایمولیٹر پر اینڈرائیڈ بیٹا انسٹال کیا جائے تاکہ اگست کی آخری تاریخ کبھی بھی حیران کن نہ ہو۔
تیسرا ڈیٹا بیس کی تبدیلیوں کی منصوبہ بندی کر رہا ہے۔ اگر میں ایک کالم شامل کروں Note یا ایک نیا وجود بنائیں اور version کو @Database کمرے میں منتقلی فراہم کرتا ہے۔ سادہ تبدیلیوں کے لیے، جیسے کہ ڈیفالٹ ویلیو کے ساتھ ایک کالم شامل کرنا، آپ درج ذیل کا اعلان کر کے کمرے میں ہجرت کر سکتے ہیں۔ autoMigrations = [AutoMigration(from = 1, to = 2)] اپنی تشریحات میں برآمد شدہ اسکیما JSON فائل کا استعمال کریں۔ اگر آپ اس قدم کو چھوڑ دیتے ہیں، تو سبھی موجودہ صارفین کو اس وقت کریش کا سامنا کرنا پڑے گا جب وہ اپ ڈیٹ شدہ ایپ کھولیں گے، جو اپ ڈیٹ کے بعد 1-اسٹار جائزوں کی سب سے عام وجوہات میں سے ایک ہے۔
خلاصہ
اس ہینڈ بک میں، ہم نے گوگل کے ذریعہ تجویز کردہ ٹولز کا استعمال کرتے ہوئے ایک مکمل مقامی اینڈرائیڈ ایپ بنائی ہے۔
Jetpack Compose UI کو ریاست کے ایک فنکشن کے طور پر بیان کرتا ہے، اور اسٹیٹ لفٹنگ پیٹرن اس طرح کی اسکرین کو برقرار رکھتا ہے: NoteListContent یہ جانچنا آسان ہے۔ کمرہ ایک کمپائل ٹائم چیک ریپوزٹری فراہم کرتا ہے جو اپ ڈیٹس کو بذریعہ اسٹریم کرتا ہے: FlowViewModel اس ڈیٹا کو اسٹور کرتا ہے۔ StateFlow یہ اسپن سے بچنے کے بارے میں ہے۔ نیویگیشن 3 بیک اسٹیک کو صارف کی ملکیت والی ایک باقاعدہ فہرست کے طور پر دیکھتا ہے، اس لیے آگے پیچھے نیویگیٹ کرنا بس ہے add اور removeLastOrNull.
ریلیز کی طرف، ہم نے ایپ کو کم کرنے کے لیے R8 کو کنفیگر کیا، ایک اپ لوڈ کلید بنائی اور اسے پروجیکٹ کے باہر محفوظ طریقے سے اسٹور کیا، اور ایک دستخط شدہ Android ایپ بنڈل بنایا۔ Play ایپ سائن کرنے کا مطلب ہے کہ Google کے پاس حتمی دستخطی کلید ہے، لہذا آپ کی اپ لوڈ کلید کا کھو جانا مہلک نہیں ہے اور اسے بازیافت کیا جا سکتا ہے۔
Google Play پر شائع کرنے کے لیے صرف اپ لوڈ کرنے سے زیادہ کی ضرورت ہوتی ہے۔ ہم نے اپنی ڈیولپر کی شناخت کی توثیق کی ہے، ایمانداری کے ساتھ اپنی رازداری کی پالیسی، مواد کی درجہ بندی، اور ڈیٹا سیفٹی سٹیٹمنٹس (بشمول فریق ثالث لائبریریوں سے جمع کردہ ڈیٹا) بنائے ہیں، اپنی اسٹور لسٹنگ بنائی ہے، اور ذاتی اکاؤنٹس کے لیے، پیداوار تک رسائی کے لیے درخواست دینے سے پہلے کم از کم 12 ٹیسٹرز کے ساتھ 14 دن کا نجی ٹیسٹ چلایا ہے۔ آزمائشی مدت کی منصوبہ بندی شروع سے ہی سب سے بڑی چیز ہے جو آپ لانچ میں تاخیر سے بچنے کے لیے کر سکتے ہیں۔
یہاں ایک اچھا اگلا مرحلہ یہ ہوگا کہ آپ کی ایپ کے بڑھنے کے ساتھ ہی ہلٹ جیسے انحصار انجیکشن فریم ورک کو شامل کریں، نوٹ کو بیک اینڈ میں ہم آہنگ کریں، اور اس کے ذریعے دوسری زبان شامل کریں: strings.xmlایک CI پائپ لائن ترتیب دیں جو ٹیسٹ چلاتی ہے اور تمام ٹیگ شدہ ریلیز میں دستخط شدہ بنڈل بناتی ہے۔ آپ کی ایپ کے سائز سے قطع نظر اس ہینڈ بک میں پیروی کرنے والا مجموعی بہاؤ یکساں رہتا ہے۔