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

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

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

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

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

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

@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()-এর প্রথম আর্গুমেন্ট হিসেবে ব্যবহার করা যেতে পারে। এর অর্থ হল, আপনি এই কৌশল ব্যবহার করে ফ্রেমওয়ার্ক এবং অন্যান্য সাধারণ প্যাটার্ন থেকে কন্টেন্টের অনুরোধ করতে পারবেন। এছাড়াও, আপনি নিজের কাস্টম কন্ট্র্যাক্ট তৈরি করতে এবং এই টেকনিকের সাথে ব্যবহার করতে পারবেন।

রানটাইম অনুমতির অনুরোধ করা

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

এছাড়াও, সঙ্গী সংক্রান্ত অনুমতি লাইব্রেরি সেইসব API-এর উপরে একটি লেয়ার হিসেবে ব্যবহার করা যেতে পারে, যাতে বর্তমান মঞ্জুর করা স্টেটকে এমন স্টেটে ম্যাপ করা যায় যা আপনার Compose UI ব্যবহার করতে পারে।

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

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

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

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

ViewModel

আপনি Architecture Components 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 ইনস্ট্যান্স স্ক্রিন-লেভেলে কম্পোজ করার উপযুক্ত আইটেম অ্যাক্সেস করেন, অর্থাৎ, অ্যাক্টিভিটি থেকে কল করা রুট কম্পোজ করার উপযুক্ত আইটেমের কাছাকাছি, ফ্র্যাগমেন্ট বা নেভিগেশন গ্রাফের ডেস্টিনেশন। এর কারণ হল, ViewModels ডিফল্ট হিসেবে সেইসব স্ক্রিন লেভেল অবজেক্টের মধ্যে স্কোপ করা থাকে। a ViewModel's লাইফসাইকেল ও স্কোপ সম্পর্কে এখানে আরও জানুন।

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

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

আপনার ViewModel-এর উপর অন্য আইটেম নির্ভরশীল হলে, viewModel() একটি ঐচ্ছিক ViewModelProvider.Factory প্যারামিটার হিসেবে নেয়।

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

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

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() ফাংশনটি Hilt-এর @HiltViewModel অ্যানোটেশন দিয়ে তৈরি করা ViewModel অটোমেটিক ব্যবহার করে। 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 composable ফাংশন ব্যবহার করুন। এটি @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 ডিপেন্ডেন্সি সম্পর্কে তথ্য থাকে যা প্রোজেক্ট ও এর ভার্সনে যোগ করতে হয়।

Paging লাইব্রেরির 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"
        )
    }
}