রচনা এবং অন্যান্য লাইব্রেরি

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

কার্যকলাপ

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

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

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

Accompanist Permissions লাইব্রেরিটি ঐ 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.10.0'
}

কোটলিন

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

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

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

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

viewModel() একটি বিদ্যমান 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 এবং Navigation Compose লাইব্রেরি, অ্যাক্টিভিটি ও ফ্র্যাগমেন্টের সাথে এর ইনস্ট্যান্সগুলো কীভাবে ব্যবহৃত হয়, সে সম্পর্কে আরও তথ্যের জন্য ইন্টারঅপারেবিলিটি ডক্স দেখুন।

ডেটার প্রবাহ

কম্পোজ অ্যান্ড্রয়েডের সবচেয়ে জনপ্রিয় স্ট্রিম-ভিত্তিক সমাধানগুলোর জন্য এক্সটেনশন সহ আসে। এই এক্সটেনশনগুলোর প্রত্যেকটি একটি ভিন্ন আর্টিফ্যাক্ট দ্বারা সরবরাহ করা হয়:

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

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

// 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)
    }
}

কম্পোজে অ্যাসিঙ্ক্রোনাস অপারেশন

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

আরও তথ্যের জন্য সাইড এফেক্টস ডকুমেন্টেশনে LaunchedEffect , produceState এবং rememberCoroutineScope এপিআইগুলো দেখুন।

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

হিল্ট

অ্যান্ড্রয়েড অ্যাপে ডিপেন্ডেন্সি ইনজেকশনের জন্য হিল্ট হলো প্রস্তাবিত সমাধান, এবং এটি কম্পোজের সাথে নির্বিঘ্নে কাজ করে।

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 নেভিগেশন কম্পোজ লাইব্রেরির সাথেও ইন্টিগ্রেট করে। আপনার Gradle ফাইলে নিম্নলিখিত অতিরিক্ত ডিপেন্ডেন্সিগুলো যোগ করুন:

গ্রুভি

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

কোটলিন

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

নেভিগেশন কম্পোজ ব্যবহার করার সময়, আপনার @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)
            }
        }
    }
}

পৃষ্ঠা সংখ্যা

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

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

@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-এ Paging ব্যবহার সম্পর্কে আরও তথ্যের জন্য Lists and grids ডকুমেন্টেশন দেখুন।

মানচিত্র

আপনার অ্যাপে গুগল ম্যাপস যুক্ত করতে আপনি ম্যাপস কম্পোজ লাইব্রেরি ব্যবহার করতে পারেন। এখানে এর ব্যবহারের একটি উদাহরণ দেওয়া হলো:

@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"
        )
    }
}

{% হুবহু %} {% endverbatim %} {% হুবহু %} {% endverbatim %}