Compose ও অন্যান্য লাইব্রেরি

Compose-এ আপনার পছন্দের লাইব্রেরি ব্যবহার করতে পারবেন। এই বিভাগে বর্ণনা করা হয়েছে যে কীভাবে সবচেয়ে উপযোগী কয়েকটি লাইব্রেরি অন্তর্ভুক্ত করতে হয়।

অ্যাক্টিভিটি

কোনও অ্যাক্টিভিটিতে Compose ব্যবহার করতে হলে, আপনাকে অবশ্যই ComponentActivity ব্যবহার করতে হবে, যা Activity-এর একটি সাবক্লাস এবং Compose-এর জন্য উপযুক্ত LifecycleOwner ও কম্পোনেন্ট প্রদান করে। এছাড়াও, এটি অতিরিক্ত API প্রদান করে যা আপনার অ্যাক্টিভিটি ক্লাসে ওভাররাইডিং পদ্ধতি থেকে আপনার কোডকে ডিকাপল করে। Activity Compose এইসব API-কে কম্পোজেবলের কাছে এক্সপোজ করে যাতে আপনার কম্পোজেবলের বাইরে ওভাররাইডিং পদ্ধতি বা স্পষ্ট Activity ইনস্ট্যান্স আর প্রয়োজন না হয়। এছাড়াও, এই APIগুলি নিশ্চিত করে যে সেগুলি শুধুমাত্র একবার শুরু করা হয়েছে, রিকম্পোজিশন থেকে বেঁচে গেছে এবং কম্পোজিশন থেকে কম্পোজেবল সরানো হলে সঠিকভাবে পরিষ্কার করা হয়েছে।

অ্যাক্টিভিটির ফলাফল

rememberLauncherForActivityResult() API আপনাকে কম্পোজেবলে অ্যাক্টিভিটি থেকে ফলাফল পেতে অনুমতি দেয়:

@Composable
fun GetContentExample() {
    var imageUri by remember { mutableStateOf<Uri?>(null) }
    val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
        imageUri = uri
    }
    Column {
        Button(onClick = { launcher.launch("image/*") }) {
            Text(text = "Load Image")
        }
        Image(
            painter = rememberAsyncImagePainter(imageUri),
            contentDescription = "My Image"
        )
    }
}

এই উদাহরণে একটি সাধারণ GetContent() চুক্তি দেখানো হয়েছে। বোতামে ট্যাপ করলে অনুরোধ চালু হয়ে যাবে। ব্যবহারকারী কোনও ছবি বেছে নিয়ে লঞ্চিং অ্যাক্টিভিটিতে ফিরে এলে rememberLauncherForActivityResult() -এর জন্য ট্রেলিং ল্যাম্বডা ইনভোক করা হয়। এটি Coil-এর rememberImagePainter() ফাংশন ব্যবহার করে বেছে নেওয়া ছবি লোড করে।

ActivityResultContract-এর যেকোনও সাবক্লাস rememberLauncherForActivityResult()-এর প্রথম আর্গুমেন্ট হিসেবে ব্যবহার করা যেতে পারে। এর অর্থ হল, আপনি এই টেকনিক ব্যবহার করে ফ্রেমওয়ার্ক থেকে কন্টেন্ট অনুরোধ করতে পারবেন এবং অন্যান্য সাধারণ প্যাটার্নেও। এছাড়াও, আপনি নিজের কাস্টম কন্ট্র্যাক্ট তৈরি করতে এবং এই টেকনিকের সাথে সেগুলি ব্যবহার করতে পারবেন।

রানটাইম অনুমতির জন্য অনুরোধ করা হচ্ছে

একই অ্যাক্টিভিটি ফলাফল API এবং rememberLauncherForActivityResult() উপরে ব্যাখ্যা করা হয়েছে যা রানটাইম অনুমতির অনুরোধ করতে ব্যবহার করা যেতে পারে RequestPermission একটি অনুমতির জন্য চুক্তি বা RequestMultiplePermissions একাধিক অনুমতির জন্য চুক্তি ব্যবহার করে।

এছাড়াও, Accompanist Permissions লাইব্রেরি ওইসব API-এর উপরে একটি লেয়ার হিসেবে ব্যবহার করা যেতে পারে যাতে অনুমতির জন্য বর্তমানে অনুমোদিত স্টেটকে এমন স্টেটে ম্যাপ করা যায় যা আপনার Compose UI ব্যবহার করতে পারে।

সিস্টেমের 'ফিরে যান' বোতাম ম্যানেজ করা

কাস্টম ব্যাক নেভিগেশন প্রদান করতে এবং আপনার কম্পোজেবল থেকে সিস্টেম ব্যাক বোতামের ডিফল্ট আচরণ ওভাররাইড করতে, আপনার কম্পোজেবল সেই ইভেন্টটি ইন্টারসেপ্ট করতে একটি BackHandler ব্যবহার করতে পারে:

var backHandlingEnabled by remember { mutableStateOf(true) }
BackHandler(backHandlingEnabled) {
    // Handle back press
}

প্রথম আর্গুমেন্ট নিয়ন্ত্রণ করে যে BackHandler বর্তমানে চালু আছে কিনা; আপনার কম্পোনেন্টের অবস্থার উপর ভিত্তি করে আপনার হ্যান্ডলার সাময়িকভাবে বন্ধ করতে আপনি এই আর্গুমেন্ট ব্যবহার করতে পারেন। ব্যবহারকারী সিস্টেম ব্যাক ইভেন্ট ট্রিগার করলে এবং BackHandler বর্তমানে চালু থাকলে ট্রেলিং ল্যাম্বডা ইনভোক করা হবে।

ViewModel

আপনি আর্কিটেকচার কম্পোনেন্ট ViewModel লাইব্রেরি ব্যবহার করলে, viewModel() ফাংশন কল করে যেকোনও কম্পোজেবল থেকে ViewModel অ্যাক্সেস করতে পারবেন। আপনার Gradle ফাইলে নিম্নলিখিত ডিপেন্ডেন্সি যোগ করুন:

গ্ৰুভি

dependencies {
    implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.11.0'
}

Kotlin

dependencies {
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.11.0")
}

তারপর আপনি নিজের কোডে viewModel() ফাংশন ব্যবহার করতে পারবেন।

class MyViewModel : ViewModel() { /*...*/ }

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) {
    // use viewModel here
}

viewModel() আগে থেকে থাকা ViewModel ফিরিয়ে আনে বা নতুন একটি তৈরি করে। ডিফল্ট হিসেবে, ফেরত আসা ViewModel-এর স্কোপ এনক্লোজিং অ্যাক্টিভিটি, ফ্র্যাগমেন্ট বা নেভিগেশন ডেস্টিনেশনে সীমাবদ্ধ থাকে এবং স্কোপ যতক্ষণ থাকে ততক্ষণ এটি রেখে দেওয়া হয়।

যেমন, কম্পোজেবল কোনও অ্যাক্টিভিটিতে ব্যবহার করা হলে, অ্যাক্টিভিটি শেষ না হওয়া বা প্রসেস বন্ধ না হওয়া পর্যন্ত viewModel() একই ইনস্ট্যান্স ফেরত দেয়।

class MyViewModel : ViewModel() { /*...*/ }
// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    // Returns the same instance as long as the activity is alive,
    // just as if you grabbed the instance from an Activity or Fragment
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

@Composable
fun MyScreen2(
    viewModel: MyViewModel = viewModel() // Same instance as in MyScreen
) { /* ... */ }

ব্যবহার করার নির্দেশিকা

আপনি সাধারণত স্ক্রিন-লেভেল কম্পোজেবলে ViewModel ইনস্ট্যান্স অ্যাক্সেস করেন, অর্থাৎ, অ্যাক্টিভিটি, ফ্র্যাগমেন্ট বা নেভিগেশন গ্রাফের ডেস্টিনেশন থেকে কল করা রুট কম্পোজেবলের কাছাকাছি। এর কারণ হল, ডিফল্ট হিসেবে ViewModel স্ক্রিন লেভেল অবজেক্টের জন্য স্কোপ করা থাকে। ViewModel-এর লাইফসাইকেল ও স্কোপ সম্পর্কে এখানে আরও পড়ুন।

অন্য কম্পোজেবলে ViewModel ইনস্ট্যান্স পাস করা এড়িয়ে চলুন, কারণ এর ফলে সেই কম্পোজেবল টেস্ট করা আরও কঠিন হয়ে যেতে পারে এবং প্রিভিউ ভেঙে যেতে পারে। পরিবর্তে, শুধুমাত্র ডেটা পাস করুন এবং তাদের প্যারামিটার হিসাবে প্রয়োজনীয় ফাংশন।

সাব স্ক্রিন-লেভেল কম্পোজেবলের জন্য স্টেট ম্যানেজ করতে আপনি ViewModel ইনস্ট্যান্স ব্যবহার করতে পারেন, তবে ViewModel-এর লাইফসাইকেল ও স্কোপ সম্পর্কে সচেতন থাকুন। যদি কম্পোজেবল স্বয়ংসম্পূর্ণ হয়, তাহলে পেরেন্ট কম্পোজেবল থেকে ডিপেন্ডেন্সি পাস করা এড়াতে ViewModel ইনজেক্ট করতে আপনি Hilt ব্যবহার করার কথা বিবেচনা করতে পারেন।

আপনার ViewModel-এর ডিপেন্ডেন্সি থাকলে, viewModel() একটি ঐচ্ছিক ViewModelProvider.Factory প্যারামিটার হিসেবে নেয়।

Compose-এ ViewModel সম্পর্কে এবং কীভাবে ইনস্ট্যান্স ব্যবহার করা হয় নেভিগেশন কম্পোজ লাইব্রেরি, বা অ্যাক্টিভিটি এবং ফ্র্যাগমেন্টের সাথে, ইন্টারঅপারেবিলিটি ডকুমেন্ট দেখুন।

ডেটার স্ট্রিম

Android-এর সবচেয়ে জনপ্রিয় স্ট্রিম-ভিত্তিক সমাধানের জন্য Compose-এ এক্সটেনশন আছে। এইসব এক্সটেনশনের প্রতিটি আলাদা আর্টিফ্যাক্ট দ্বারা প্রদান করা হয়:

  • androidx.compose.runtime:runtime-livedata:$composeVersion আর্টিফ্যাক্টে LiveData.observeAsState() অন্তর্ভুক্ত আছে।
  • Flow.collectAsState()-এর জন্য অতিরিক্ত ডিপেন্ডেন্সি প্রয়োজন হয় না।
  • androidx.compose.runtime:runtime-rxjava2:$composeVersion বা androidx.compose.runtime:runtime-rxjava3:$composeVersion আর্টিফ্যাক্টে Observable.subscribeAsState() অন্তর্ভুক্ত।

এইসব আর্টিফ্যাক্ট লিসেনার হিসেবে রেজিস্টার করে এবং ভ্যালুকে State হিসেবে দেখায়। যখনই কোনও নতুন ভ্যালু এমিট করা হয়, Compose, UI-এর সেইসব অংশ রিকম্পোজ করে যেখানে state.value ব্যবহার করা হয়। যেমন, এই কোডে, ShowData প্রতিবার রিকম্পোজ করে exampleLiveData একটি নতুন ভ্যালু এমিট করে।

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) {
    val dataExample = viewModel.exampleLiveData.observeAsState()

    // Because the state is read here,
    // MyScreen recomposes whenever dataExample changes.
    dataExample.value?.let {
        ShowData(dataExample)
    }
}

Compose-এ অ্যাসিঙ্ক্রোনাস অপারেশন

Jetpack Compose আপনাকে কম্পোজেবলের মধ্যে থেকে কোরুটিন ব্যবহার করে অ্যাসিঙ্ক্রোনাস অপারেশন চালাতে দেয়।

আরও তথ্যের জন্য সাইড এফেক্ট সংক্রান্ত ডকুমেন্টেশন-এ LaunchedEffect, produceState ও rememberCoroutineScope API দেখুন।

নেভিগেশন কম্পোনেন্ট Jetpack Compose অ্যাপ্লিকেশনের জন্য সহায়তা প্রদান করে। আরও তথ্যের জন্য Compose-এর মাধ্যমে নেভিগেট করা এবং Jetpack Navigation-কে Navigation Compose-এ মাইগ্রেট করা দেখুন।

Hilt

Android অ্যাপে ডিপেন্ডেন্সি ইনজেকশনের জন্য Hilt হল সাজেস্ট করা সমাধান এবং Compose-এর সাথে নির্বিঘ্নে কাজ করে।

ViewModel বিভাগে উল্লেখ করা viewModel() ফাংশন অটোমেটিক ViewModel ব্যবহার করে যা Hilt, @HiltViewModel অ্যানোটেশনের মাধ্যমে তৈরি করে। আমরা Hilt-এর ViewModel ইন্টিগ্রেশন সম্পর্কে তথ্য সহ ডকুমেন্টেশন প্রদান করেছি।

@HiltViewModel
class MyViewModel @Inject constructor(
    private val savedStateHandle: SavedStateHandle,
    private val repository: ExampleRepository
) : ViewModel() { /* ... */ }

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

Hilt এবং নেভিগেশন

Hilt, Navigation Compose লাইব্রেরির সাথেও ইন্টিগ্রেট করে। আপনার Gradle ফাইলে নিম্নলিখিত অতিরিক্ত ডিপেন্ডেন্সি যোগ করুন:

গ্ৰুভি

dependencies {
    implementation 'androidx.hilt:hilt-navigation-compose:1.4.0'
}

Kotlin

dependencies {
    implementation("androidx.hilt:hilt-navigation-compose:1.4.0")
}

Navigation Compose ব্যবহার করার সময়, আপনার @HiltViewModel অ্যানোটেট করা ViewModel-এর ইনস্ট্যান্স পেতে, সবসময় hiltViewModel কম্পোজেবল ফাংশন ব্যবহার করুন। এটি ফ্র্যাগমেন্ট বা অ্যাক্টিভিটির সাথে কাজ করে যা @AndroidEntryPoint-এর সাথে অ্যানোটেট করা হয়।

যেমন, ExampleScreen যদি নেভিগেশন গ্রাফে কোনও গন্তব্য হয়, তাহলে নিচে দেখানো কোড স্নিপেটের মতো গন্তব্যের জন্য স্কোপ করা ExampleViewModel-এর ইনস্ট্যান্স পেতে hiltViewModel() কল করুন:

// import androidx.hilt.navigation.compose.hiltViewModel

@Composable
fun MyApp() {
    val navController = rememberNavController()
    val startRoute = "example"
    NavHost(navController, startDestination = startRoute) {
        composable("example") { backStackEntry ->
            // Creates a ViewModel from the current BackStackEntry
            // Available in the androidx.hilt:hilt-navigation-compose artifact
            val viewModel = hiltViewModel<MyViewModel>()
            MyScreen(viewModel)
        }
        /* ... */
    }
}

এর পরিবর্তে, নেভিগেশন রুট বা নেভিগেশন গ্রাফ-এ স্কোপ করা ViewModel-এর ইনস্ট্যান্স আপনাকে ফিরিয়ে আনতে হলে, hiltViewModel কম্পোজেবল ফাংশন ব্যবহার করুন এবং প্যারামিটার হিসেবে সংশ্লিষ্ট backStackEntry পাস করুন:

// import androidx.hilt.navigation.compose.hiltViewModel
// import androidx.navigation.compose.getBackStackEntry

@Composable
fun MyApp() {
    val navController = rememberNavController()
    val startRoute = "example"
    val innerStartRoute = "exampleWithRoute"
    NavHost(navController, startDestination = startRoute) {
        navigation(startDestination = innerStartRoute, route = "Parent") {
            // ...
            composable("exampleWithRoute") { backStackEntry ->
                val parentEntry = remember(backStackEntry) {
                    navController.getBackStackEntry("Parent")
                }
                val parentViewModel = hiltViewModel<ParentViewModel>(parentEntry)
                ExampleWithRouteScreen(parentViewModel)
            }
        }
    }
}

পেজিং

পেজিং লাইব্রেরি আপনার জন্য ধীরে ধীরে ডেটা লোড করা সহজ করে তোলে এবং এটি Compose-এ কাজ করে। পেজিং রিলিজ পৃষ্ঠাতে অতিরিক্ত paging-compose ডিপেন্ডেন্সি সম্পর্কে তথ্য রয়েছে যা প্রজেক্ট এবং এর ভার্সনে যোগ করতে হবে।

পেজিং লাইব্রেরির Compose API-এর একটি উদাহরণ এখানে দেওয়া হল:

@Composable
fun MyScreen(flow: Flow<PagingData<String>>) {
    val lazyPagingItems = flow.collectAsLazyPagingItems()
    LazyColumn {
        items(
            lazyPagingItems.itemCount,
            key = lazyPagingItems.itemKey { it }
        ) { index ->
            val item = lazyPagingItems[index]
            Text("Item is $item")
        }
    }
}

Compose-এ পেজিং ব্যবহার করার ব্যাপারে আরও তথ্যের জন্য তালিকা ও গ্রিড ডকুমেন্টেশন চেক করে দেখুন।

Maps

আপনার অ্যাপে Google Maps প্রদান করতে আপনি Maps Compose লাইব্রেরি ব্যবহার করতে পারেন। এখানে একটি ব্যবহারের উদাহরণ দেওয়া হল:

@Composable
fun MapsExample() {
    val singapore = LatLng(1.35, 103.87)
    val cameraPositionState = rememberCameraPositionState {
        position = CameraPosition.fromLatLngZoom(singapore, 10f)
    }
    GoogleMap(
        modifier = Modifier.fillMaxSize(),
        cameraPositionState = cameraPositionState
    ) {
        Marker(
            state = remember { MarkerState(position = singapore) },
            title = "Singapore",
            snippet = "Marker in Singapore"
        )
    }
}