কম্পোজেবল টেনে আনা

ব্যবহারকারীদের স্ক্রিন জুড়ে কম্পোনেন্ট শারীরিকভাবে টেনে আনার সুবিধা দেওয়া হল আধুনিক Android অ্যাপের মৌলিক বিষয়। আপনি কাস্টম স্লাইডার, সোয়াইপ-টু-ডিসমিস কম্পোনেন্ট বা ইন্টার‍্যাক্টিভ ক্যানভাস যাই তৈরি করুন না কেন, Jetpack Compose, ড্র্যাগ ইভেন্ট সহজে ম্যানেজ করার জন্য API-এর একটি বিস্তৃত সেট প্রদান করে।

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

এই পৃষ্ঠায় প্রতিটি ড্র্যাগিং API কীভাবে এবং কখন ব্যবহার করতে হবে তা বর্ণনা করা হয়েছে।

সঠিক ড্র্যাগিং API বেছে নিন

ড্র্যাগ করা যায় এমন কন্টেন্ট প্রয়োগ করার জন্য আপনি একাধিক API ব্যবহার করতে পারেন।

  • স্ট্যান্ডার্ড মেটেরিয়াল কম্পোনেন্ট (যেমন SwipeToDismissBox বা নেভিগেশন ড্রয়ার) বেছে নিন যখন: আপনার ডিফল্ট মেটেরিয়াল ৩ সোয়াইপ বা ড্র্যাগ ব্যবহার (যেমন, ইমেল মুছে ফেলা, টাস্ক আর্কাইভ করা বা সাইড মেনু / স্ক্রিনের নিচে অ্যাটাচ করা শিট খোলা) প্রয়োজন। কাস্টম স্টেট বা লেআউট ম্যানেজমেন্টের প্রয়োজন ছাড়াই এগুলি বিল্ট-ইন আচরণ প্রদান করে।
  • কখন Modifier.draggable বেছে নেবেন: আপনার শুধু র' ড্র্যাগ ডেল্টা প্রয়োজন (যেমন, কাস্টম স্লাইডার ড্র্যাগ করা)। আরও তথ্যের জন্য, ড্র্যাগেবল সহ একটি অক্ষ বরাবর টেনে আনুন দেখুন।
  • কখন Modifier.anchoredDraggable বেছে নেবেন: আপনি যখন এমন কাস্টম ইন্টার‍্যাক্টিভ কম্পোনেন্ট তৈরি করছেন যা দুই বা তার বেশি আলাদা আলাদা পজিশনের মধ্যে স্ন্যাপ করতে হবে (যেমন, স্ক্রিনের নিচে অ্যাটাচ করা শিট, নেভিগেশন ড্রয়ার বা সোয়াইপ-টু-রিভিল অ্যাকশন)। আরও তথ্যের জন্য, -এর সাহায্যে অ্যাঙ্কর পয়েন্টের মধ্যে কম্পোনেন্ট ড্র্যাগ করাanchoredDraggable দেখুন।

draggable ব্যবহার করে একটি অক্ষ বরাবর ড্র্যাগ করুন

draggable মডিফায়ার হল একটি নির্দিষ্ট ওরিয়েন্টেশনে ড্র্যাগ জেসচার করার জন্য হাই-লেভেল এন্ট্রি পয়েন্ট এবং এটি পিক্সেলে ড্র্যাগ ডিস্ট্যান্স রিপোর্ট করে।

এটি মনে রাখা গুরুত্বপূর্ণ যে এই মডিফায়ারটি scrollable-এর মতো, কারণ এটি শুধুমাত্র জেসচার শনাক্ত করে। আপনাকে স্টেট ধরে রাখতে হবে এবং স্ক্রিনে তা দেখাতে হবে যেমন, offset মডিফায়ারের মাধ্যমে এলিমেন্ট সরিয়ে:

@Composable
private fun DraggableText() {
    var offsetX by remember { mutableFloatStateOf(0f) }
    Text(
        modifier = Modifier
            .offset { IntOffset(offsetX.roundToInt(), 0) }
            .draggable(
                orientation = Orientation.Horizontal,
                state = rememberDraggableState { delta ->
                    offsetX += delta
                }
            ),
        text = "Drag me!"
    )
}

আপনাকে যদি পুরো ড্র্যাগিং জেসচার নিয়ন্ত্রণ করতে হয়, তাহলে pointerInput মডিফায়ারের মাধ্যমে ড্র্যাগ জেসচার ডিটেক্টর ব্যবহার করার কথা বিবেচনা করুন।

@Composable
private fun DraggableTextLowLevel() {
    Box(modifier = Modifier.fillMaxSize()) {
        var offsetX by remember { mutableFloatStateOf(0f) }
        var offsetY by remember { mutableFloatStateOf(0f) }

        Box(
            Modifier
                .offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
                .background(Color.Blue)
                .size(50.dp)
                .pointerInput(Unit) {
                    detectDragGestures { change, dragAmount ->
                        change.consume()
                        offsetX += dragAmount.x
                        offsetY += dragAmount.y
                    }
                }
        )
    }
}

এখানে অলটারনেট টেক্সট যোগ করুন
ছবি ১. একটি আঙুল দিয়ে UI এলিমেন্ট টেনে আনা হচ্ছে

anchoredDraggable ব্যবহার করে অ্যাঙ্কর পয়েন্টের মধ্যে কম্পোনেন্ট টেনে আনুন

anchoredDraggable মডিফায়ার আপনাকে অ্যাঙ্কর পয়েন্ট নামক পূর্বনির্ধারিত পজিশনের সেটের মধ্যে কম্পোনেন্ট টেনে নিয়ে যেতে দেয়। ব্যবহারকারী টেনে আনার জেসচার ছেড়ে দিলে, কম্পোনেন্টটি সবচেয়ে কাছের অ্যাঙ্করে সেট হয়ে যায়। anchoredDraggable কাস্টম বটম শিট, জটিল স্লাইডার বা ড্রয়ার প্যানেলের মতো কম্পোনেন্ট তৈরি করার জন্য উপযোগী।

API সারফেস

AnchoredDraggable API-এর মধ্যে নিম্নলিখিত প্রধান অংশগুলি রয়েছে:

  • অ্যাঙ্কর (DraggableAnchors): এমন একটি বিল্ডার যা আপনার কাস্টম স্টেট (যেমন, Start, Center, End-এর জন্য একটি এনুম ক্লাস) এবং সেগুলির সংশ্লিষ্ট পিক্সেল অফসেট ভ্যালুর মধ্যে ম্যাপিংকে সংজ্ঞায়িত করে।
  • স্টেট (AnchoredDraggableState<T>): একটি স্টেট হোল্ডার যা কম্পোনেন্টের বর্তমান পজিশন (অফসেট), এটি যে অ্যাঙ্করে সেট করা আছে এবং এর ড্র্যাগ প্রগ্রেস ম্যানেজ করে। কনফিগারেশন পরিবর্তন জুড়ে এর অবস্থা বজায় রাখতে, এটি rememberSaveable এবং AnchoredDraggableState.Saver<T> ব্যবহার করে তৈরি করা উচিত।
  • জেসচার মডিফায়ার (Modifier.anchoredDraggable): একটি মডিফায়ার যা Orientation বরাবর টেনে আনার জেসচার শনাক্ত করে, টেনে আনার ডেল্টার সাথে স্ট্যাটাস আপডেট করে এবং রিলিজ করার পরে কম্পোনেন্টকে সবচেয়ে কাছের অ্যাঙ্করে অ্যানিমেট করার জন্য ফ্লিং জেসচার ম্যানেজ করে।

উদাহরণ: কাস্টম SwipeToDismiss কম্পোনেন্ট তৈরি করুন

এই উদাহরণে দেখানো হয়েছে কীভাবে কাস্টম SwipeToDismiss কম্পোনেন্ট (একটি সাদা ব্যানার) তৈরি করতে হয় যা ব্যবহারকারী বাঁদিকে সোয়াইপ করে বাতিল করতে পারেন অথবা ডানদিকে টেনে এনে ওভারস্ক্রল স্ট্রেচ এফেক্ট দেখতে পারেন।

  1. অ্যাঙ্কর ভ্যালু নির্ধারণ করুন। ড্র্যাগ করা যায় এমন কন্টেন্ট যে আলাদা আলাদা পজিশনে সেট হতে পারে, তার জন্য একটি এনুম সংজ্ঞায়িত করুন:

    private enum class AnchoredDraggableSwipeDismissValue { DismissedStart, Resting }

  2. ইনস্ট্যান্টিয়েট করে AnchoredDraggableState সেভ করুন। অ্যাক্টিভিটি রিক্রিয়েশন এবং কনফিগারেশন পরিবর্তন (যেমন, স্ক্রিন ঘোরানো বা থিম পরিবর্তন) জুড়ে কম্পোনেন্টের বর্তমান পজিশন অপরিবর্তিত রাখতে AnchoredDraggableState.Saver()-এর সাথে rememberSaveable ব্যবহার করুন:

    val state =
        rememberSaveable(saver = AnchoredDraggableState.Saver()) {
            AnchoredDraggableState(initialValue = AnchoredDraggableSwipeDismissValue.Resting)
        }

  3. layout, offset ও draggable মডিফায়ার প্রয়োগ করুন। কম্পোনেন্টকে ড্র্যাগ করা যায় এমন করে তুলতে এবং এর অ্যাঙ্কর পয়েন্ট যাতে এর সাইজের সাথে মানিয়ে নিতে পারে তা নিশ্চিত করতে, যে কন্টেন্টকে দৃশ্যত সরাতে হবে তার উপর নিচে উল্লেখ করা মডিফায়ার প্রয়োগ করুন:

    • Modifier.layout: অ্যাঙ্কর পজিশন প্রায়ই কম্পোনেন্টের সাইজের উপর নির্ভর করে, যা প্রাথমিক কম্পোজিশনের সময় জানা যায় না। কম্পোনেন্ট পরিমাপ করতে layout মডিফায়ার ব্যবহার করুন, এর প্রস্থের উপর ভিত্তি করে ডায়নামিক্যালি DraggableAnchors ম্যাপ হিসেব করুন এবং state.updateAnchors() ব্যবহার করে স্ট্যাটাস আপডেট করুন।

    • Modifier.anchoredDraggable: স্ট্যাটাসের সাথে কানেক্ট করে, নির্দিষ্ট ওরিয়েন্টেশনে ড্র্যাগ ডেল্টা ডিসপ্যাচ করে এবং রিলিজ করার সময় কম্পোনেন্টকে সবচেয়ে কাছের অ্যাঙ্করে অ্যানিমেট করার জন্য ফ্লিং জেসচার ম্যানেজ করে। যে কম্পোনেন্টকে ড্র্যাগ জেসচারের উত্তর দিতে হবে, সেটিতে এই মডিফায়ার যোগ করুন এবং state ও ড্র্যাগ orientation (এবং ঐচ্ছিকভাবে, overscrollEffect) পাস করুন।
    • Modifier.offset: অফসেট ল্যাম্বডার মধ্যে থেকে state.requireOffset() অফসেট রিড করে। এই ল্যাম্বডার মধ্যে অফসেট পড়লে এটি নিশ্চিত হয় যে ভ্যালুটি শুধুমাত্র প্লেসমেন্ট ফেজের সময় পড়া হয়, যা ড্র্যাগ করার সময় অপ্রয়োজনীয় রিকম্পোজিশন এড়ায়। যে কন্টেন্ট দৃশ্যত সরানোর প্রয়োজন, সেটির উপর এই মডিফায়ার প্রয়োগ করুন।

    Box(
        modifier = Modifier
            .fillMaxWidth()
            // Read state.requireOffset() inside the offset lambda to defer reading to the placement phase
            .offset { IntOffset(state.requireOffset().roundToInt(), 0) }
            .layout { measurable, constraints ->
                val placeable = measurable.measure(constraints)
                val anchors = DraggableAnchors {
                    AnchoredDraggableSwipeDismissValue.DismissedStart at -placeable.width.toFloat()
                    AnchoredDraggableSwipeDismissValue.Resting at 0f
                }
                state.updateAnchors(anchors)
                layout(placeable.width, placeable.height) { placeable.placeRelative(0, 0) }
            }
            // Pass overscrollEffect to anchoredDraggable so deltas beyond the bounds (min and max anchor) are dispatched to overscrollEffect, and render the visual effect with Modifier.overscroll.
            .overscroll(overscrollEffect)
            .anchoredDraggable(
                state,
                Orientation.Horizontal,
                overscrollEffect = overscrollEffect
            )
    ) {
        dismissContent()
    }

  4. [ঐচ্ছিক] ওভারস্ক্রল ইন্টিগ্রেট করুন। overscrollEffect API একটি ভিজ্যুয়াল এফেক্ট প্রয়োগ করে যা স্ক্রল বা ফ্লিংয়ের মাধ্যমে স্ক্রলিং কন্টেনারের প্রান্তে পৌঁছে গেলে দেখা যায়। ওভারস্ক্রল ইফেক্ট (সাধারণত স্ট্রেচ বা গ্লো) সহ কোনও ব্যবহারকারী যখন আগে থেকে নির্দিষ্ট করা অ্যাঙ্কর বাউন্ডের বাইরে ড্র্যাগ করেন, তখন আপনি ভিজ্যুয়াল ফিডব্যাক দিতে পারেন।

    1. ডিসপ্যাচ: anchoredDraggable মডিফায়ারে একটি overscrollEffect ইনস্ট্যান্স পাস করুন। এটি ড্র্যাগ ডেল্টাকে AnchoredDraggable স্টেট বা ওভারস্ক্রল এফেক্টে পাঠায়, যা নির্ভর করে জেসচার অ্যাঙ্কর বাউন্ডের ভিতরে নাকি বাইরে আছে তার উপর।
    2. Render: একই OverscrollEffect ইনস্ট্যান্স সহ Modifier.overscroll কম্পোনেন্টে প্রয়োগ করুন যা এফেক্টকে ভিজ্যুয়ালি রেন্ডার করবে।

নিম্নলিখিত স্নিপেট থেকে বোঝা যাবে যে কীভাবে স্ট্যান্ডার্ড স্ট্রেচ এফেক্ট যোগ করতে হয়:

val overscrollEffect = rememberOverscrollEffect()
Box(
    modifier.fillMaxWidth()
) {
    Box(Modifier.fillMaxWidth()) {
        background()
    }
    Box(
        modifier = Modifier
            .fillMaxWidth()
            // Read state.requireOffset() inside the offset lambda to defer reading to the placement phase
            .offset { IntOffset(state.requireOffset().roundToInt(), 0) }
            .layout { measurable, constraints ->
                val placeable = measurable.measure(constraints)
                val anchors = DraggableAnchors {
                    AnchoredDraggableSwipeDismissValue.DismissedStart at -placeable.width.toFloat()
                    AnchoredDraggableSwipeDismissValue.Resting at 0f
                }
                state.updateAnchors(anchors)
                layout(placeable.width, placeable.height) { placeable.placeRelative(0, 0) }
            }
            // Pass overscrollEffect to anchoredDraggable so deltas beyond the bounds (min and max anchor) are dispatched to overscrollEffect, and render the visual effect with Modifier.overscroll.
            .overscroll(overscrollEffect)
            .anchoredDraggable(
                state,
                Orientation.Horizontal,
                overscrollEffect = overscrollEffect
            )
    ) {
        dismissContent()
    }
}

ফলাফল

উপরের কোড স্নিপেটটি একটি সাদা ব্যানার তৈরি করে, যেখানে "বাতিল করতে আমাকে ড্র্যাগ করুন!" লেখা থাকে। ব্যবহারকারী এটি বাতিল করতে বাঁদিকে সোয়াইপ করতে পারেন (অথবা ওভারস্ক্রল স্ট্রেচ এফেক্ট দেখতে ডানদিকে ড্র্যাগ করতে পারেন):

ভিডিও ২. কাস্টম সোয়াইপ-টু-ডিসমিস কম্পোনেন্ট প্রয়োগ।

উদাহরণ: কাস্টম, জেসচার-চালিত নেভিগেশন ড্রয়ার তৈরি করুন

এই উদাহরণে দেখানো হয়েছে কীভাবে কাস্টম, জেসচার-চালিত নেভিগেশন ড্রয়ার তৈরি করতে হয়। ব্যবহারকারী অনুভূমিকভাবে টেনে আনলে, ফোরগ্রাউন্ড স্ক্রিন (ScreenContents) ডানদিকে স্লাইড করে, 1.0f থেকে 0.8f-এ স্কেল ডাউন করে এবং এর কোণগুলি গোল করে যাতে নিচে থাকা নেভিগেশন ড্রয়ার দেখা যায়।

  1. অ্যাঙ্কর স্টেট ও পজিশন নির্ধারণ করুন। এই উদাহরণে, ড্রয়ারের খোলা ও বন্ধ অবস্থাকে অ্যাঙ্কর হিসেবে ব্যবহার করা হয়েছে। Open এবং Closed অবস্থার জন্য একটি এনাম সংজ্ঞায়িত করুন এবং DraggableAnchors ব্যবহার করে পিক্সেল অফসেটে ম্যাপ করুন:

    enum class DrawerState {
        Open,
        Closed
    }
    
        val anchors = DraggableAnchors {
            DrawerState.Open at drawerWidth
            DrawerState.Closed at 0f
        }
        val state = remember {
            AnchoredDraggableState(
                initialValue = DrawerState.Closed,
                anchors = anchors,
            )
        }

  2. কোনও আইটেমে ক্লিক করলে anchoredDraggableState অ্যানিমেট করা। Box-এর নিচে HomeScreenDrawer এবং উপরে ScreenContents রাখুন। ড্রয়ারটি স্থির থাকে, যখন উপরের লেয়ারটি এর উপরে অনুবাদ করে এবং স্কেল করে।

    কোনও ব্যবহারকারী ড্রয়ার আইটেম বেছে নিলে, অ্যাক্টিভ স্ক্রিন স্টেট আপডেট করুন এবং state.animateTo(DrawerState.Closed) ব্যবহার করে অটোমেটিক ড্রয়ারটি বাতিল করুন:

    @Composable
    private fun AnchoredDraggableDrawerOnClickSample(
        modifier: Modifier = Modifier,
        state: AnchoredDraggableState<DrawerState>,
        coroutineScope: CoroutineScope
    ) {
        var screenState by remember { mutableStateOf(ScreenState.Home) }
    
        Box(modifier = modifier.fillMaxSize()) {
            HomeScreenDrawer(
                selectedScreen = screenState,
                onScreenSelected = { screen ->
                    screenState = screen
                    // Dismiss drawer when a destination is selected
                    coroutineScope.launch { state.animateTo(DrawerState.Closed) }
                }
            )
    
            // ScreenContents(...)
        }
    }

  3. একাধিক ভিজ্যুয়াল প্রপার্টি পরিবর্তন করতে state.requireOffset() ব্যবহার করুন। শুধুমাত্র কন্টেন্ট অফসেট করার পরিবর্তে, আপনি Modifier.graphicsLayer ব্যবহার করে একাধিক প্রপার্টি পরিবর্তন করতে পারেন ড্র্যাগ প্রগ্রেসের উপর ভিত্তি করে একই সাথে ট্রান্সলেশন, স্কেল এবং কর্নার রেডিয়াস অ্যানিমেট করতে পারেন:

    • বর্তমান ড্র্যাগ অফসেট পেতে state.requireOffset() ব্যবহার করুন।
    • অফসেট ও ড্রয়ারের প্রস্থের সাপেক্ষে আনুপাতিক স্কেল (1f থেকে 0.8f) ও কোণাকৃতির ব্যাসার্ধ (0.dp থেকে 32.dp) গণনা করতে lerp() (লিনিয়ার ইন্টারপোলেশন) ব্যবহার করুন।
    • Modifier.graphicsLayer ড্র ফেজে পরিবর্তনগুলি কার্যকর করে, যা ড্র্যাগ করার সময় রিকম্পোজিশন ট্রিগার না করেই উচ্চ-পারফরম্যান্স অ্যানিমেশন নিশ্চিত করে।

    @Composable
    private fun AnchoredDraggableDrawerGraphicsLayerSample(
        state: AnchoredDraggableState<DrawerState>,
        drawerWidth: Float,
        selectedScreen: ScreenState,
        onDrawerClicked: () -> Unit
    ) {
        ScreenContents(
            selectedScreen = selectedScreen,
            onDrawerClicked = onDrawerClicked,
            modifier = Modifier
                .graphicsLayer {
                    this.translationX = state.requireOffset()
                    val scale = lerp(1f, 0.8f, state.requireOffset() / drawerWidth)
                    this.scaleX = scale
                    this.scaleY = scale
                    val cornerRadius = lerp(0f, 32f, state.requireOffset() / drawerWidth)
                    this.shape = RoundedCornerShape(cornerRadius.dp)
                    this.clip = true
                }
                .anchoredDraggable(state, Orientation.Horizontal)
        )
    }

  4. animateTo-এর মাধ্যমে প্রোগ্রামাটিক অ্যানিমেশন ট্রিগার করুন। কোরুটিন স্কোপের মধ্যে state.animateTo() লঞ্চ করে প্রোগ্রামাটিক উপায়ে ড্রয়ার কন্ট্রোল করুন (যেমন, মেনু আইকনে ট্যাপ করলে বা ড্রয়ার আইটেম বেছে নিলে):

    fun toggleDrawerState(
        state: AnchoredDraggableState<DrawerState>,
        coroutineScope: CoroutineScope
    ) {
        coroutineScope.launch {
            if (state.currentValue == DrawerState.Open) {
                state.animateTo(DrawerState.Closed)
            } else {
                state.animateTo(DrawerState.Open)
            }
        }
    }

ফলাফল

ভিডিও ৩. ফোরগ্রাউন্ড স্ক্রিন (স্লিপ) ডানদিকে টেনে আনা হয়েছে, যা অন্তর্নিহিত নেভিগেশন ড্রয়ার দেখাচ্ছে।

Modifier.draggable2D-এর সাহায্যে দুই মাত্রায় টেনে আনুন

X ও Y অক্ষ বরাবর একই সাথে আলাদা আলাদা UI এলিমেন্ট সরানোর জন্য draggable2D মডিফায়ার ব্যবহার করুন।

কম্পোজ করা যায় এমন এলিমেন্ট টেনে আনুন

এই উদাহরণে draggable2D-এর সবচেয়ে সাধারণ ব্যবহার দেখানো হয়েছে — এর মাধ্যমে ব্যবহারকারী কোনও UI এলিমেন্ট বেছে নিয়ে পেরেন্ট কন্টেনারের মধ্যে যেকোনও জায়গায় সেটিকে আবার রাখতে পারেন।

@Composable
private fun DraggableComposableElement() {
    // 1. Track the position of the floating window
    var offset by remember { mutableStateOf(Offset.Zero) }

    Box(modifier = Modifier.fillMaxSize().background(Color(0xFFF5F5F5))) {
        Box(
            modifier = Modifier
                // 2. Apply the offset to the box's position
                .offset { IntOffset(offset.x.roundToInt(), offset.y.roundToInt()) }
                // ...
                // 3. Attach the 2D drag logic
                .draggable2D(
                    state = rememberDraggable2DState { delta ->
                        // 4. Update the position based on the movement delta
                        offset += delta
                    }
                ),
            contentAlignment = Alignment.Center
        ) {
            Text("Video Preview", color = Color.White, fontSize = 12.sp)
        }
    }
}

ভিডিও ৪. ধূসর ব্যাকগ্রাউন্ডে একটি ছোট বেগুনি রঙের বক্সের পজিশন পরিবর্তন করা হচ্ছে, এর মাধ্যমে ডাইরেক্ট টুডি ড্র্যাগিং দেখানো হচ্ছে যেখানে ব্যবহারকারীর আঙুল তোলার সাথে সাথেই এলিমেন্টটি সরানো বন্ধ হয়ে যায়।

পূর্ববর্তী কোড স্নিপেটে নিম্নলিখিত বিষয়গুলি অন্তর্ভুক্ত রয়েছে:

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

অভিভাবকের ড্র্যাগ এরিয়ার উপর ভিত্তি করে চাইল্ড কম্পোজেবল ড্র্যাগ করুন

এই উদাহরণে দেখানো হয়েছে যে কীভাবে draggable2D ব্যবহার করে 2D ইনপুট এরিয়া তৈরি করা যায় যেখানে একটি নির্দিষ্ট সারফেসে সিলেক্টর নব সীমাবদ্ধ থাকে। ড্র্যাগেবল এলিমেন্টের উদাহরণের মতো নয়, যা কম্পোনেন্টকে নিজেই সরায়, এই প্রয়োগ রঙ চয়নকারী জুড়ে চাইল্ড কম্পোজেবল "নির্বাচক" সরাতে 2D ডেল্টা ব্যবহার করে:

@Composable
private fun ExampleColorSelector(
    // ...
)  {
    // 1. Maintain the 2D position of the selector in state.
    var selectorOffset by remember { mutableStateOf(Offset.Zero) }

    // 2. Track the size of the background container.
    var containerSize by remember { mutableStateOf(IntSize.Zero) }

    Box(
        modifier = Modifier
            .size(300.dp, 200.dp)
            // Capture the actual pixel dimensions of the container when it's laid out.
            .onSizeChanged { containerSize = it }
            .clip(RoundedCornerShape(12.dp))
            .background(
                brush = remember(hue) {
                    // Create a simple gradient representing Saturation and Value for the given Hue.
                    Brush.linearGradient(listOf(Color.White, Color.hsv(hue, 1f, 1f)))
                }
            )
    ) {
        Box(
            modifier = Modifier
                .size(24.dp)
                .graphicsLayer {
                    // Center the selector on the finger by subtracting half its size.
                    translationX = selectorOffset.x - (24.dp.toPx() / 2)
                    translationY = selectorOffset.y - (24.dp.toPx() / 2)
                }
                // ...
                // 3. Configure 2D touch dragging.
                .draggable2D(
                    state = rememberDraggable2DState { delta ->
                        // 4. Calculate the new position and clamp it to the container bounds
                        val newX = (selectorOffset.x + delta.x)
                            .coerceIn(0f, containerSize.width.toFloat())
                        val newY = (selectorOffset.y + delta.y)
                            .coerceIn(0f, containerSize.height.toFloat())

                        selectorOffset = Offset(newX, newY)
                    }
                )
        )
    }
}

ভিডিও ৫. সাদা বৃত্তাকার সিলেক্টর নব সহ একটি কালার গ্রেডিয়েন্ট যা যেকোনও দিকে টেনে আনা যেতে পারে, এটি দেখায় যে কীভাবে 2D ডেল্টা কন্টেনারের সীমানায় ক্ল্যাম্প করা হয় যাতে বেছে নেওয়া রঙের মান আপডেট করা যায়।

আগের স্নিপেটে নিম্নলিখিতগুলি অন্তর্ভুক্ত আছে:

  • এটি onSizeChanged মডিফায়ার ব্যবহার করে গ্রেডিয়েন্ট কন্টেনারের আসল ডাইমেনশন ক্যাপচার করে। সিলেক্টর সঠিকভাবে জানে যে এজ কোথায় আছে।
  • graphicsLayer-এর মধ্যে, এটি translationX এবং translationY অ্যাডজাস্ট করে যাতে টেনে আনার সময় সিলেক্টরটি কেন্দ্রে থাকে।