Back Press Handling Android میں سسٹم کے پیچھے والے بٹن کو روکنے اور اس پر کارروائی کرنے کا ایک طریقہ کار ہے، جو یہ طے کرتا ہے کہ اسے دبانے پر کون سا عمل انجام دیا جاتا ہے۔ Android Developers (2024) کے مطابق، Android 11 سے روایتی onBackPressed() طریقہ کو OnBackPressedDispatcher سے تبدیل کر دیا گیا ہے۔ نیا API درجہ بندی کی کسی بھی سطح پر اجزاء کو دبانے کو روکنے کی اجازت دیتا ہے، نہ کہ صرف Activity کو۔ اہم فرق — ترجیحات کے ساتھ زنجیر میں متعدد callback کی حمایت۔
اہم نکات
Back Press Handling Android میں ایک نظامی طریقہ کار ہے جو طے کرتا ہے کہ جب صارف ہارڈویئر یا سافٹ ویئر کے پیچھے والے بٹن کو دباتا ہے تو کیا ہوتا ہے۔ سیاق و سباق پر منحصر ہے، دبانے سے: موجودہ اسکرین بند ہو سکتی ہے اور پچھلی پر واپس آ سکتی ہے، کی بورڈ چھپ سکتا ہے، Drawer یا Bottom Sheet بند ہو سکتا ہے، اگر صارف روٹ اسکرین پر ہے تو ایپ سے باہر نکل سکتا ہے۔
پیچھے والے بٹن کا رویہ Android کے ہر ورژن کے ساتھ ترقی پذیر رہا ہے۔ Android 10 نے اشاروں کی نیویگیشن متعارف کرائی، Android 11 نے OnBackPressedDispatcher کو معیاری API کے طور پر متعارف کرایا، اور Android 13 نے بہتر predictive back gesture سپورٹ لایا، جہاں نظام عمل کے اصل میں انجام پانے سے پہلے ایک منتقلی اینیمیشن دکھاتا ہے۔ Google تمام آلات پر متوقع اور مستقل Back Press رویے کی طرف مسلسل بڑھ رہا ہے۔
پیچھے والے بٹن کی مناسب ہینڈلنگ Android ایپ میں UX کا ایک اہم عنصر ہے۔ صارف توقع کرتا ہے کہ پیچھے دبانے سے وہ نیویگیشن اسٹیک میں پچھلی اسکرین پر واپس آئے گا، نہ کہ ایپ غیر متوقع طور پر بند ہو جائے۔ اس توقع کی خلاف ورزی Google Play پر منفی جائزوں اور کم درجہ بندیوں کی ایک اہم وجہ ہے۔
Android میں Back Press API کی تاریخ پلیٹ فارم کے مجموعی ارتقا کی عکاسی کرتی ہے: Activity میں ایک سادہ طریقہ سے لے کر لائف سائیکل سپورٹ اور Compose کے ساتھ ایک لچکدار callback نظام تک۔ ترقی کے تین مراحل دیکھتے ہیں۔
پہلے Android API سے ہی، پیچھے والے بٹن کو Activity کلاس کے onBackPressed طریقہ میں ہینڈل کیا جاتا تھا۔ ڈویلپر اس طریقہ کو اوور رائیڈ کرتا تھا اور اپنی منطق لکھتا تھا۔ مسئلہ یہ تھا کہ Fragment اور View دبانے کو نہیں روک سکتے تھے — تمام کنٹرول Activity کے ذریعے جاتا تھا۔ اس سے پھولی ہوئی Activities اور پیچیدہ if-else زنجیریں پیدا ہوتی تھیں کہ دبانے کو کسے ہینڈل کرنا چاہیے۔
Activity 1.0.0 (AndroidX) کے ساتھ، Google نے OnBackPressedDispatcher متعارف کرایا۔ یہ ایک مرکزی ڈسپیچر ہے جو کسی بھی جزو — Activity، Fragment، Dialog، کسٹم View — سے callback قبول کرتا ہے۔ Callback ایک ترتیب (priority کے ذریعے) کے ساتھ رجسٹر ہوتے ہیں اور متحرک طور پر شامل یا ہٹائے جا سکتے ہیں۔ OnBackPressedDispatcher پرانے onBackPressed سے پہلے طلب کیا جاتا ہے، جو Activity کے ہینڈل کرنے سے پہلے دبانے کو روکنے کی اجازت دیتا ہے۔
Android 13 نے predictive back gesture متعارف کرایا — ایک نظامی اینیمیشن جو دکھاتا ہے کہ صارف کے اشارہ مکمل کرنے سے پہلے پیچھے دبانا کہاں لے جائے گا۔ اس اینیمیشن کو سپورٹ کرنے کے لیے، ڈویلپرز کو OnBackPressedDispatcher استعمال کرنا ہوگا اور isEnabled پراپرٹی کے ذریعے بتانا ہوگا کہ آیا callback سسٹم اینیمیشن کو سپورٹ کرتا ہے۔ اگر callback predictive animation کو سپورٹ نہیں کرتا، تو نظام ایک ڈیفالٹ اینیمیشن دکھاتا ہے جو ایپ کے سیاق و سباق سے میل نہیں کھا سکتی۔
| API | کم از کم SDK | Fragment سپورٹ | Predictive Back |
|---|---|---|---|
| onBackPressed | API Level 1 | Activity کے ذریعے | نہیں |
| OnBackPressedDispatcher | Activity 1.0.0 | براہ راست | جزوی |
| OnBackPressedDispatcher + lifecycle | Activity 1.3.0 | Lifecycle-aware | مکمل |
OnBackPressedDispatcher نئے Back Press API کا مرکز ہے۔ یہ callback کی ایک زنجیر کا انتظام کرتا ہے، انہیں ترتیب سے طلب کرتا ہے جب تک کہ پہلا واقعہ کو ہینڈل نہ کر لے۔ اگر کوئی callback دبانے کو ہینڈل نہیں کرتا، تو ڈسپیچر ڈیفالٹ عمل انجام دیتا ہے — Activity کے لیے finish() یا Navigation Component کے لیے popBackStack() کال کرنا۔
ایک callback LifecycleOwner اور OnBackPressedCallback آبجیکٹ کے ساتھ addCallback کے ذریعے رجسٹر ہوتا ہے۔ Callback میں ایک isEnabled پراپرٹی ہوتی ہے — اگر false پر سیٹ کی جائے تو callback چھوڑ دیا جاتا ہے۔ ترجیح کے لیے، آپ 0 (سب سے کم) سے Integer.MAX_VALUE تک قدر بھیج سکتے ہیں۔ Fragment Activity Result API لائف سائیکل سے منسلک callback کی خودکار رجسٹریشن کے لیے اس میکانزم کا استعمال کرتا ہے۔
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val callback = object : OnBackPressedCallback(enabled = true) {
override fun handleOnBackPressed() {
if (isDrawerOpen) { closeDrawer() }
else { isEnabled = false; onBackPressed() }
}
}
onBackPressedDispatcher.addCallback(this, callback)
}
Callback خود بخود ہٹا دیے جاتے ہیں جب LifecycleOwner DESTROYED حالت میں چلا جاتا ہے۔ یہ اسکرین گھومنے کے دوران callback لیک کے پرانے مسئلے کو حل کرتا ہے۔ اگر Fragment میں callback شامل کیا گیا ہے، تو Fragment کے تباہ ہونے پر اسے ہٹانے کی ضمانت ہے۔ عارضی طور پر callback کو غیر فعال کرنے کے لیے، isEnabled پراپرٹی استعمال کریں — آپ اسے ہٹانے اور دوبارہ رجسٹریشن کے بغیر ٹوگل کر سکتے ہیں۔
کال کی ترتیب شامل کرنے کی ترتیب کے الٹ ہے: سب سے آخر میں شامل کردہ callback پہلے کنٹرول حاصل کرتا ہے۔ یہ منطقی ہے کیونکہ سب سے زیادہ اندرونی UI عنصر (مثال کے طور پر Fragment کے اندر Bottom Sheet) کو اپنے پیرنٹ Fragment سے پہلے دبانے کو ہینڈل کرنا چاہیے۔ اگر سب سے گہرا callback دبانے کو ہینڈل نہیں کرتا (isEnabled = false)، تو کنٹرول زنجیر میں اگلے کو منتقل ہو جاتا ہے۔
Fragment API requireActivity().onBackPressedDispatcher طریقہ کے ذریعے OnBackPressedDispatcher کے ساتھ اپنا انضمام فراہم کرتا ہے۔ Fragment 1.2.0 سے شروع کرتے ہوئے، ہر Fragment اپنا callback رجسٹر کر سکتا ہے، جو خود بخود Fragment کے لائف سائیکل سے منسلک ہو جاتا ہے اور تباہ ہونے پر ہٹا دیا جاتا ہے۔
Fragment میں callback رجسٹریشن onCreate، onViewCreated یا خود View میں بھی کی جاتی ہے — اہم بات یہ ہے کہ LifecycleOwner (Fragment) فعال ہے۔ جب Fragment STARTED حالت میں جاتا ہے، callback فعال ہو جاتا ہے؛ STOPPED پر، یہ غیر فعال ہو جاتا ہے۔ یہ یقینی بناتا ہے کہ پوشیدہ Fragment (ViewPager میں) پیچھے دبانے کو ہینڈل نہیں کرے گا۔
class EditorFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
showDiscardDialog()
}
}
requireActivity().onBackPressedDispatcher.addCallback(this, callback)
}
}
ڈائیلاگ اور BottomSheets بند ہونے کے لیے خود بخود پیچھے دبانے کو روک لیتے ہیں۔ اگر آپ کو بند کرنے سے پہلے اضافی کارروائی کرنے کی ضرورت ہے — تو زیادہ ترجیح کے ساتھ callback رجسٹر کریں۔ اہم: اگر آپ Dialog پر setCancelable(false) سیٹ کرتے ہیں، تو callback فعال نہیں ہوگا — یہ نظامی رویہ ہے۔
Jetpack Compose BackHandler composable فنکشن کے ذریعے پیچھے والے بٹن کو ہینڈل کرنے کے لیے ایک اعلانیہ API فراہم کرتا ہے۔ BackHandler enabled (بولین حالت) اور onBack — دبانے پر طلب کردہ callback قبول کرتا ہے۔ اگر enabled = false ہے، تو دبانا زنجیر میں آگے منتقل ہو جاتا ہے۔
BackHandler خود بخود پیرنٹ Activity کے OnBackPressedDispatcher میں OnBackPressedCallback رجسٹر کرتا ہے۔ یہ composable کے لائف سائیکل کا احترام کرتا ہے: کمپوزیشن چھوڑنے پر، callback ہٹا دیا جاتا ہے۔ enabled کو حالت سے منسلک کیا جا سکتا ہے — مثال کے طور پر، تصدیقی ڈائیلاگ صرف اس وقت دکھائیں جب فارم میں غیر محفوظ تبدیلیاں ہوں۔
@Composable
fun EditScreen(hasUnsavedChanges: Boolean) {
BackHandler(enabled = hasUnsavedChanges) {
// Show confirmation dialog
}
Column {
TextField(value = ..., onValueChange = ...)
}
}
Compose میں Predictive back gesture Compose 1.5.0 سے تعاون یافتہ ہے۔ BackHandler خود بخود سسٹم ٹرانزیشن اینیمیشن کو ہینڈل کرتا ہے اگر یہ ڈیوائس پر فعال ہو۔ کسٹم predictive اینیمیشن کے لیے، predictiveBackHandler موڈیفائر استعمال کریں، جو 0 سے 1 تک اشارہ کی پیش رفت واپس کرتا ہے۔
Back Press Handling سادہ لگتا ہے، لیکن عملی طور پر ڈویلپرز کئی نظامی غلطیاں کرتے ہیں۔ Google کی سفارشات اور کمیونٹی کے تجربے کی بنیاد پر سب سے عام مسائل اور ان کے حل دیکھتے ہیں۔
handleOnBackPressed میں براہ راست finish() کال کرنا غیر متوقع ایپ باہر نکلاو کا سبب بن سکتا ہے اگر نیویگیشن اسٹیک میں پس منظر کی اسکرینیں ہوں۔ Activity بند کرنے سے پہلے ہمیشہ Navigation Component یا Coordinator کے ذریعے NavController.backStack چیک کریں۔
اگر آپ LifecycleOwner کے بغیر callback رجسٹر کرتے ہیں (پیرامیٹر کے بغیر پرانا addCallback استعمال کرتے ہوئے)، callback ہمیشہ زندہ رہے گا اور اگر Activity پہلے ہی تباہ ہو چکی ہے تو NullPointerException کا سبب بن سکتا ہے۔ ہمیشہ LifecycleOwner کے ساتھ addCallback(this, callback) استعمال کریں۔
موڈل ونڈوز (Bottom Sheet، Dialog) کے لیے، isEnabled = true صرف اس وقت سیٹ کریں جب ونڈو نظر آ رہی ہو۔ ونڈو کی حالت چیک کرنے والے لیمبڈا کے ساتھ addCallback استعمال کریں۔ Navigation Component NavHost کے لیے خود بخود اس کا انتظام کرتا ہے۔
تیز ڈبل پیچھے دبانے سے finish() کی ڈبل کال ہو سکتی ہے۔ 500 ms کے اندر بار بار کالوں سے بچانے کے لیے فلیگ یا throttleLast استعمال کریں۔ Navigation Component اس صورت حال کو مقامی طور پر ہینڈل کرتا ہے، لیکن کسٹم منظرناموں میں آپ کو دستی طور پر تحفظ لاگو کرنے کی ضرورت ہے۔
private var lastBackPressTime = 0L
override fun handleOnBackPressed() {
val currentTime = System.currentTimeMillis()
if (currentTime - lastBackPressTime > 500) {
lastBackPressTime = currentTime
navigateBack()
}
}
اکثر پوچھے گئے سوالات
onBackPressed کو Android 11 میں متروک کر دیا گیا کیونکہ یہ صرف Activity کی سطح پر کام کرتا ہے۔ OnBackPressedDispatcher کسی بھی جزو (Fragment، Dialog، View) کو لائف سائیکل سپورٹ کے ساتھ ایک واحد میکانزم کے ذریعے دبانے کو روکنے کی اجازت دیتا ہے۔
Android نظام ایپلیکیشن کے موصول ہونے سے پہلے کنارے سے سوائپ اشارہ کو سسٹم کے پیچھے دبانے میں تبدیل کرتا ہے۔ OnBackPressedDispatcher کی سطح پر، آپ ان دو واقعات میں فرق نہیں کر سکتے — دونوں handleOnBackPressed کے طور پر آتے ہیں۔
ہاں، OnBackPressedDispatcher کی ہینڈلنگ تین بٹن والے آلات اور اشاروں والی نیویگیشن کے لیے یکساں ہے۔ روکنے کا کوڈ نیویگیشن کی قسم پر منحصر نہیں ہے — نظام خود اشارہ کو ڈسپیچر کال میں تبدیل کرتا ہے۔
Android 13+ ایمیولیٹر کے Developer Options میں predictive back کو فعال کریں۔ ADB استعمال کریں: `adb shell settings put global enable_back_animation 1`۔ فعال کرنے کے بعد، نظام اینیمیشن پیچھے دبانے پر منتقلی کا پیش منظر دکھائے گا۔
دو شرائط چیک کریں: LifecycleOwner کو STARTED یا RESUMED حالت میں ہونا چاہیے، اور callback کا isEnabled true ہونا چاہیے۔ اگر دونوں شرائط پوری ہوتی ہیں، تو یقینی بنائیں کہ callback صحیح OnBackPressedDispatcher میں شامل کیا گیا ہے — Fragment میں requireActivity().onBackPressedDispatcher استعمال کریں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں