SideEffect Jetpack Compose میں ایک composable فنکشن ہے جو ہر کامیاب دوبارہ ترکیب (recomposition) پر منتقل کردہ کوڈ بلاک کو چلاتا ہے۔ LaunchedEffect اور DisposableEffect کے برعکس، SideEffect کلیدوں سے منسلک نہیں ہے اور اس میں صفائی کا بلاک نہیں ہے — یہ ہر رینڈرنگ کے بعد Compose حالت کو بیرونی نظاموں کے ساتھ صرف مطابقت پذیر کرتا ہے۔ یہ اسے کال بیک فنکشنز کو اپ ڈیٹ کرنے، ViewPager کے ساتھ مطابقت پذیری اور Analytics SDK کو ڈیٹا منتقل کرنے کے لیے مثالی بناتا ہے۔ Android Developers Documentation (2025) کے مطابق، SideEffect اس وقت چلتا ہے جب Compose کامیاب دوبارہ ترکیب کی تصدیق کرتا ہے، اور اگر دوبارہ ترکیب چھوڑ دی جائے تو نہیں چلتا۔
اہم نکات
SideEffect Jetpack Compose میں سب سے آسان سائیڈ ایفیکٹ API ہے۔ یہ ایک composable جزو کی ہر کامیاب دوبارہ ترکیب پر ایک کوڈ بلاک چلاتا ہے۔ یہاں "کامیاب" لفظ کلیدی ہے: اگر Compose فیصلہ کرتا ہے کہ دوبارہ ترکیب ضروری نہیں ہے (مثال کے طور پر، تمام ان پٹ پیرامیٹرز میں تبدیلی نہیں آئی اور نتیجہ وہی ہوگا)، تو SideEffect نہیں چلتا۔ یہ یقینی بناتا ہے کہ مطابقت پذیری بلاک صرف اس وقت بلایا جائے جب یوزر انٹرفیس واقعی تبدیل ہوا ہو۔
SideEffect کا بنیادی استعمال Compose حالت کو ان اشیاء کے ساتھ مطابقت پذیر کرنا ہے جو Compose درخت کا حصہ نہیں ہیں۔ عام مثالوں میں شامل ہیں: Legacy View سسٹم میں کال بیک فنکشن کو اپ ڈیٹ کرنا، ViewPager کو موجودہ حالت منتقل کرنا، ڈسپلے کردہ ڈیٹا تبدیل ہونے پر Analytics SDK کو ایک واقعہ بھیجنا، اور میپنگ SDK کے ساتھ مطابقت پذیری کرنا جو بیرونی فارمیٹ میں اپ ڈیٹس کی توقع رکھتے ہیں۔
Android Developer Blog (2025) کے مطابق، SideEffect اکثر remember کے ساتھ استعمال ہوتا ہے: remember ایک شے (مثلاً ایک کال بیک) کو محفوظ رکھتا ہے، اور SideEffect جب بھی کوئی انحصار تبدیل ہوتا ہے اسے اپ ڈیٹ کرتا ہے۔ یہ پیٹرن خاص طور پر ان لائبریریوں کے لیے اہم ہے جو لسنر اشیاء قبول کرتی ہیں اور اپ ڈیٹ پر انہیں دوبارہ نہیں بناتیں — SideEffect کے بغیر، لسنر موجودہ حالت کا ایک پرانا حوالہ رکھے گا۔
@Composable
fun MapScreen(zoomLevel: Int, markers: List<Marker>) {
val mapView = remember { MapView(LocalContext.current) }
SideEffect {
mapView.setZoom(zoomLevel)
mapView.updateMarkers(markers)
}
AndroidView(factory = { mapView })
}
SideEffect کو سمجھنے کے لیے، Jetpack Compose کے عملدرآمد کے مراحل کو سمجھنا ضروری ہے۔ Compose ہر فریم کے لیے تین مراحل سے گزرتا ہے: Composition (کیا دکھانا ہے)، Layout (کہاں دکھانا ہے)، Drawing (کیسے دکھانا ہے)۔ SideEffect Composition مرحلے کے آخر میں چلتا ہے — تمام composable فنکشنز چلنے کے بعد، لیکن Layout مرحلے سے پہلے۔ یہ یقینی بناتا ہے کہ SideEffect دوبارہ ترکیب کے بعد تمام متغیرات کی آخری حالت دیکھتا ہے۔
زندگی کے چکر میں یہ مقام ایک اہم فائدہ دیتا ہے: SideEffect لامحدود دوبارہ ترکیب کا سبب نہیں بن سکتا، چاہے اس کے اندر حالت تبدیل کی جائے۔ چونکہ یہ کمپوزیشن کے بعد چلتا ہے، SideEffect کے اندر کی گئی تبدیلیاں صرف اگلے فریم میں لاگو ہوں گی — یہ ایک composable فنکشن کے جسم کے اندر حالت کی تبدیلیوں کے عام چکروں کو روکتا ہے (جب کمپوزیشن کے اندر setState موجودہ کمپوزیشن ختم ہونے سے پہلے ایک نئی کمپوزیشن کو متحرک کرتا ہے)۔
ایک اور خصوصیت یہ ہے کہ SideEffect کلیدوں کے ذریعے بہتر نہیں کیا جاتا۔ یہ ہر دوبارہ ترکیب پر چلتا ہے چاہے کسی بھی مخصوص حالت میں تبدیلی آئی ہو۔ اگر آپ کو زیادہ درست کنٹرول کی ضرورت ہے (صرف اس وقت چلائیں جب کوئی مخصوص پیرامیٹر تبدیل ہو)، تو کلیدوں کے ساتھ LaunchedEffect استعمال کریں یا SideEffect کو remember کے ذریعے تبدیلی کی جانچ میں لپیٹ دیں۔
// SideEffect optimized with remember
var currentZoom by remember { mutableIntStateOf(zoomLevel) }
SideEffect {
if (currentZoom != zoomLevel) {
map.animateToZoom(zoomLevel)
currentZoom = zoomLevel
}
}
// Without this check SideEffect would call animateToZoom
// on every recomposition, even if zoomLevel didn't change
SideEffect کے لیے سب سے عام عملی منظرنامہ موجودہ حالت کو کیپچر کرنے والے کال بیک فنکشنز کو اپ ڈیٹ کرنا ہے۔ Jetpack Compose میں اسے "کال بیک لائف سائیکل مینجمنٹ" کہا جاتا ہے۔ مسئلہ یہ ہے کہ Kotlin میں lambda اظہارات متغیرات کو حوالہ سے کیپچر کرتے ہیں، اور اگر ایک کال بیک ایک قدر کے ساتھ بنایا گیا اور بعد میں متغیر تبدیل ہوگیا — تو کال بیک پرانی قدر استعمال کرتا رہتا ہے۔
ایک مثال پر غور کریں: Android کے لیے Google Maps SDK setOnCameraMoveListener() کے ذریعے ایک OnCameraMoveListener شے قبول کرتا ہے۔ اگر آپ ایک lambda منتقل کرتے ہیں جو isTrackingEnabled کو کیپچر کرتا ہے، تو جب isTrackingEnabled تبدیل ہوگا تو lambda اپ ڈیٹ نہیں ہوگا — Maps SDK پرانے ڈیٹا کے ساتھ پرانے کال بیک کو کال کرتا رہے گا۔ SideEffect اس مسئلے کو حل کرتا ہے: یہ ہر دوبارہ ترکیب پر لسنر کو دوبارہ سیٹ کرتا ہے، اس بات کو یقینی بناتا ہے کہ SDK ہمیشہ تازہ ترین حالت کے ساتھ موجودہ lambda استعمال کرے۔
Android کے لیے Maps SDK دستاویز (2025) کے مطابق، Google Maps کو Jetpack Compose کے ساتھ مربوط کرتے وقت بالکل اسی پیٹرن کی سفارش کرتا ہے۔ اسی طرح کا طریقہ WebView، VideoView، TextureView اور کسی بھی دوسرے View پر مبنی اجزاء کے لیے استعمال ہوتا ہے جو set طریقوں کے ذریعے کال بیک قبول کرتے ہیں۔ SideEffect یقینی بناتا ہے کہ ہر حالت کی تبدیلی کے ساتھ کال بیک اپ ڈیٹ رہیں۔
@Composable
fun MapComposable(isTrackingEnabled: Boolean, onMarkerClick: (Marker) -> Unit) {
val mapView = remember { MapView(LocalContext.current) }
SideEffect {
mapView.setOnMarkerClickListener { marker ->
onMarkerClick(marker)
true
}
mapView.isTrafficEnabled = isTrackingEnabled
}
AndroidView(factory = { mapView })
}
SideEffect کے لیے ایک اور اہم منظرنامہ یوزر انٹرفیس کی حالت تبدیل ہونے پر تجزیہ کے نظاموں کو واقعات بھیجنا ہے۔ مثال کے طور پر، جب کوئی صارف Compose اسکرین کے اندر TabLayout میں ٹیب تبدیل کرتا ہے، SideEffect موجودہ منتخب ٹیب کی حالت Firebase Analytics یا AppsFlyer کو منتقل کر سکتا ہے۔ جب بھی منتخب ٹیب تبدیل ہوتا ہے (اور دوبارہ ترکیب ہوتی ہے)، SideEffect متعلقہ واقعہ بھیجتا ہے۔
واقعات کو براہ راست onClick یا onTabSelected میں بھیجنے سے فرق یہ ہے کہ SideEffect کسی بھی ذریعہ سے حالت کی تبدیلیوں پر متحرک ہوتا ہے — نہ صرف صارف کے اعمال، بلکہ پروگرامی تبدیلیاں، اسکرین گھومنے کے بعد حالت کی بحالی یا ڈیپ لنکس بھی۔ یہ SideEffect کو تبدیلی کے ذریعہ سے آزاد ایک عالمگیر مطابقت پذیری طریقہ کار بناتا ہے۔
Firebase Best Practices (Google, 2025) کے مطابق، SideEffect کے ذریعے تجزیہ کے واقعات بھیجنا صارف کے سفر کی ایک زیادہ مکمل تصویر فراہم کرتا ہے، کیونکہ یہ حالت کی تمام تبدیلیوں کو کیپچر کرتا ہے، بشمول وہ جو براہ راست صارف کے عمل کے بغیر ہوتی ہیں۔ تاہم، اسے زیادہ نہ کرنا ضروری ہے: ہر تجزیہ کا واقعہ ایک نیٹ ورک کی درخواست ہے، لہذا بار بار تبدیل ہونے والی حالتوں (اسکرول پوزیشن، انگلی کے نقاط) کے لیے SideEffect موزوں نہیں ہے — ڈیباؤنس استعمال کریں یا صرف اہم تبدیلیوں پر واقعات بھیجیں۔
@Composable
fun ProductScreen(selectedTab: ProductTab, productId: String) {
val firebaseAnalytics = remember { FirebaseAnalytics.getInstance(LocalContext.current) }
SideEffect {
val params = Bundle().apply {
putString(FirebaseAnalytics.Param.CONTENT_TYPE, selectedTab.name)
putString(FirebaseAnalytics.Param.ITEM_ID, productId)
}
firebaseAnalytics.logEvent( FirebaseAnalytics.Event.VIEW_ITEM, params)
}
// UI with TabRow and selected tab
}
SideEffect اور LaunchedEffect کے درمیان انتخاب دو عوامل پر منحصر ہے: کیا غیر مطابقت پذیر عملدرآمد ضروری ہے اور کیا کلید پر مبنی کنٹرول ضروری ہے۔ SideEffect مطابقت پذیر ہے اور ہر دوبارہ ترکیب پر چلتا ہے۔ LaunchedEffect غیر مطابقت پذیر (کوروٹین) ہے اور صرف اس وقت چلتا ہے جب کوئی کلید تبدیل ہوتی ہے، ہر دوبارہ ترکیب پر نہیں۔
| خصوصیت | SideEffect | LaunchedEffect |
|---|---|---|
| عملدرآمد | ہر دوبارہ ترکیب پر | کلید کی تبدیلی پر |
| غیر مطابقت پذیری | مطابقت پذیر | کوروٹین |
| کلیدیں | نہیں | ہاں (vararg) |
| صفائی | نہیں | خودکار کوروٹین منسوخی |
| عام استعمال | کال بیک، تجزیہ، منظر مطابقت پذیری | لوڈنگ، Flow سبسکرپشن، ٹائمر |
عملی طور پر، سائیڈ ایفیکٹ کے 70% استعمالات LaunchedEffect (غیر مطابقت پذیر کارروائیاں، ڈیٹا لوڈنگ)، 20% DisposableEffect (صفائی کے ساتھ وسائل) اور صرف 10% SideEffect (کال بیک مطابقت پذیری) کے ذریعے پورے ہوتے ہیں۔ SideEffect کاموں کی ایک محدود رینج کے لیے ایک خصوصی آلہ ہے، لیکن ان کاموں میں یہ ناگزیر ہے۔
بنیادی غلطی SideEffect کے اندر Compose حالت کو تبدیل کرنا ہے۔ اگرچہ SideEffect براہ راست لامحدود لوپ کا سبب نہیں بنتا (کیونکہ یہ کمپوزیشن مرحلے کے بعد چلتا ہے)، یہ ضرورت سے زیادہ دوبارہ ترکیب کو متحرک کر سکتا ہے۔ اگر SideEffect کے اندر حالت تبدیل کی جائے (mutableStateOf)، تو یہ اگلے فریم میں ایک نئی دوبارہ ترکیب کو متحرک کرتا ہے، جو پھر SideEffect چلاتا ہے — اور یہ استحکام تک جاری رہتا ہے۔ یہ لامحدود لوپ نہیں ہے، لیکن فریم ورک کے لیے غیر ضروری کام ہے۔
دوسری غلطی SideEffect کے اندر بھاری حسابات کرنا ہے۔ چونکہ SideEffect ہر دوبارہ ترکیب پر بلایا جاتا ہے، اور دوبارہ ترکیبیں سیکنڈ میں درجنوں بار ہو سکتی ہیں (اینیمیشن، اسکرولنگ کے دوران)، SideEffect کے اندر کوئی بھی بھاری کوڈ فریم ڈراپ کا سبب بنے گا۔ بھاری کارروائیوں کو کمپوزیشن سے باہر منتقل کریں — کوروٹین (LaunchedEffect) میں یا derivedStateOf / remember کے ذریعے حساب کریں۔
تیسری غلطی SideEffect کو غیر مطابقت پذیر کوڈ کے لیے استعمال کرنے کی کوشش کرنا ہے۔ SideEffect ایک suspend فنکشن نہیں ہے، لہذا delay()، await()، collect() اور دیگر کوروٹین کارروائیاں اس کے اندر مرتب نہیں ہوں گی۔ اگر آپ کو دوبارہ ترکیب کے بعد ایک غیر مطابقت پذیر کارروائی کرنے کی ضرورت ہے، تو LaunchedEffect کے ساتھ snapshotFlow { ... } استعمال کریں، یا rememberCoroutineScope کے ذریعے کوروٹین شروع کریں۔
اکثر پوچھے گئے سوالات
ہاں، SideEffect ہر کامیاب کمپوزیشن پر چلتا ہے، پہلی بھی شامل ہے — جب جزو پہلی بار اسکرین پر ظاہر ہوتا ہے۔ یہ LaunchedEffect(Unit) سے مختلف ہے، جو پہلی کمپوزیشن پر ایک بار چلتا ہے لیکن بعد کی دوبارہ ترکیبوں پر نہیں چلتا (اگر کلید تبدیل نہیں ہوئی)۔
نہیں، SideEffect کمپوزیشن مرحلے کے بعد چلتا ہے — اس کے اندر کی گئی تبدیلیاں صرف اگلے فریم میں لاگو ہوں گی، جو لوپ کو روکتی ہے۔ تاہم، SideEffect کے اندر بار بار حالت تبدیل کرنا دوبارہ ترکیب کا ایک سلسلہ شروع کر سکتا ہے، جس سے کارکردگی کم ہو سکتی ہے۔ SideEffect کے اندر حالت صرف اس وقت تبدیل کریں جب واقعی ضروری ہو۔
SideEffect ہر دوبارہ ترکیب پر مطابقت پذیر طریقے سے چلتا ہے۔ snapshotFlow Compose حالت سے ایک Flow بناتا ہے اور ردعمل کی تبدیلی کی کارروائی کے لیے LaunchedEffect میں collectLatest کے ساتھ استعمال کیا جا سکتا ہے۔ snapshotFlow ان صورتوں کے لیے موزوں ہے جہاں آپ کو debounce، filter یا distinctUntilChanged کے ساتھ تبدیلیوں کا جواب دینے کی ضرورت ہے — جو مطابقت پذیر SideEffect میں ناممکن ہے۔
Android Studio Compose Modifier Debugger استعمال کریں یا جزو کے نام اور کال فریکوئنسی کے ساتھ لاگنگ شامل کریں۔ اگر SideEffect توقع سے زیادہ بار چلتا ہے تو چیک کریں کہ آیا والد جزو کی حالت غیر ضروری طور پر تبدیل ہو رہی ہے۔ اصلاح: یوزر انٹرفیس کے مستحکم حصوں کو unstable تشریحات کے ساتھ علیحدہ composable فنکشنز میں نکالیں تاکہ دوبارہ ترکیب کی تعداد کم ہو۔
ہاں، انہیں مختلف مقاصد کے لیے ایک ہی جزو میں استعمال کیا جا سکتا ہے۔ DisposableEffect ایک وسیلہ کے سیٹ اپ اور صفائی (ایک بار) کے لیے ذمہ دار ہے، جبکہ SideEffect ہر دوبارہ ترکیب پر اس وسیلہ کے ساتھ موجودہ حالت کی مطابقت پذیری کا انتظام کرتا ہے۔ ایک عام مثال: DisposableEffect ایک API کے ذریعے کال بیک رجسٹر کرتا ہے، اور SideEffect ہر تبدیلی پر اس کال بیک میں کیپچر کردہ ڈیٹا کو اپ ڈیٹ کرتا ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں