פריסות זרימה ב'כתיבה'

‫FlowRow ו-FlowColumn הם רכיבים קומפוזביליים שדומים ל-Row ול-Column. ההבדל הוא שהפריטים עוברים לשורה הבאה כשנגמר המקום בקונטיינר. הפעולה הזו יוצרת כמה שורות או עמודות. אפשר גם לשלוט במספר הפריטים בשורה באמצעות ההגדרות maxItemsInEachRow או maxItemsInEachColumn. לרוב אפשר להשתמש ב-FlowRow וב-FlowColumn כדי ליצור פריסות רספונסיביות – התוכן לא ייחתך אם הפריטים גדולים מדי מבחינה מסוימת. בנוסף אפשר להשתמש בשילוב של maxItemsInEach* עם Modifier.weight(weight) כדי ליצור פריסות שמתרחבות וממלאות שורה או עמודה במקרה הצורך.

הדוגמה הטיפוסית היא לממשק משתמש של צ'יפ או סינון:

‫5 צ'יפים ב-FlowRow, שמציגים את הגלישה לשורה הבאה כשאין יותר מקום פנוי.
איור 1. דוגמה לFlowRow

שימוש בסיסי

כדי להשתמש ב-FlowRow או ב-FlowColumn, צריך ליצור את הרכיבים הקומפוזביליים האלה ולמקם בהם את הפריטים שצריכים לפעול לפי התהליך הרגיל:

@Composable
private fun FlowRowSimpleUsageExample() {
    FlowRow(modifier = Modifier.padding(8.dp)) {
        ChipItem("Price: High to Low")
        ChipItem("Avg rating: 4+")
        ChipItem("Free breakfast")
        ChipItem("Free cancellation")
        ChipItem("£50 pn")
    }
}

הקטע הזה יוצר את ממשק המשתמש שמוצג למעלה, והפריטים עוברים אוטומטית לשורה הבאה כשאין יותר מקום בשורה הראשונה.

תכונות של פריסת Flow

פריסות זרימה כוללות מספר תכונות ומאפיינים שאפשר להשתמש בהם כדי ליצור פריסות שונות באפליקציה.

סידור הציר הראשי: סידור אופקי או אנכי

הציר הראשי הוא הציר שבו הפריטים מסודרים (לדוגמה, ב-FlowRow הפריטים מסודרים בצורה אופקית). הפרמטר horizontalArrangement ב-FlowRow קובע איך השטח הפנוי יחולק בין הפריטים.

בטבלה הבאה מוצגות דוגמאות להגדרת horizontalArrangement בפריטים של FlowRow:

הוגדר סידור אופקי של FlowRow

תוצאה

Arrangement.Start (Default)

פריטים שמסודרים בהתחלה

Arrangement.SpaceBetween

סידור פריטים עם רווח ביניהם

Arrangement.Center

פריטים שמסודרים במרכז

Arrangement.End

פריטים שמסודרים בסוף

Arrangement.SpaceAround

פריטים מסודרים עם רווחים ביניהם

Arrangement.spacedBy(8.dp)

פריטים עם רווח של dp ביניהם

ב-FlowColumn, אפשרויות דומות זמינות עם verticalArrangement, וברירת המחדל היא Arrangement.Top.

פריסה של ציר ניצב

הציר המשני הוא הציר בכיוון ההפוך לציר הראשי. לדוגמה, בFlowRow, זהו הציר האנכי. כדי לשנות את האופן שבו כל התוכן בתוך הקונטיינר מסודר בציר המשני, משתמשים ב-verticalArrangement בשביל FlowRow וב-horizontalArrangement בשביל FlowColumn.

בטבלה הבאה מוצגות דוגמאות להגדרת ערכים שונים של verticalArrangement בפריטים של FlowRow:

הגדרת סידור אנכי ב-FlowRow

תוצאה

Arrangement.Top (Default)

הסידור העליון של הקונטיינר

Arrangement.Bottom

הסידור התחתון של הקונטיינר

Arrangement.Center

הסידור המרכזי של הקונטיינר

ב-FlowColumn, אפשרויות דומות זמינות עם horizontalArrangement. פריסת ברירת המחדל של הציר הניצב היא Arrangement.Start.

יישור פריטים בודדים

יכול להיות שלגבי פריטים מסוימים יהיה נכון יותר לבחור יישור שונה. ההגדרה הזו שונה מ-verticalArrangement ומ-horizontalArrangement כי היא מיישרת פריטים בתוך השורה הנוכחית. אפשר להשתמש ב-Modifier.align() כדי להחיל את ההגדרה הזו.

לדוגמה, כשפריטים ב-FlowRow הם בגבהים שונים, השורה מקבלת את הגובה של הפריט הכי גדול ומחילה את Modifier.align(alignmentOption) על הפריטים:

הגדרת היישור האנכי היא FlowRow

תוצאה

Alignment.Top (Default)

הפריטים מיושרים למעלה

Alignment.Bottom

הפריטים מיושרים לתחתית

Alignment.CenterVertically

פריטים מיושרים למרכז

ב-FlowColumn יש אפשרויות דומות. ברירת המחדל היא Alignment.Start.

מספר הפריטים המקסימלי בשורה או בעמודה

הפרמטרים maxItemsInEachRow או maxItemsInEachColumn מגדירים את מספר הפריטים המקסימלי בציר הראשי שמותר להציג בשורה אחת לפני שהם עוברים לשורה הבאה. ערך ברירת המחדל הוא Int.MAX_INT, שמאפשר להציג כמה שיותר פריטים, כל עוד הגודל שלהם מאפשר להם להתאים לשורה.

לדוגמה, אם מגדירים maxItemsInEachRow, הפריסה הראשונית תכלול רק 3 פריטים:

לא הוגדר ערך מקסימלי

maxItemsInEachRow = 3

לא הוגדר ערך מקסימלי בשורת הזרימה מספר הפריטים המקסימלי שמוגדר בשורת הזרימה

משקלי הפריטים

המשקל מגדיל פריט על סמך המקדם שלו והשטח הזמין בשורה שבה הוא הוצב. חשוב להבין שיש הבדל בין FlowRow לבין Row באופן שבו המשקלים משמשים לחישוב הרוחב של פריט. במקרה של Rows, המשקל מבוסס על כל הפריטים ב-Row. ב-FlowRow, המשקל מבוסס על הפריטים בשורה שבה הפריט ממוקם, ולא על כל הפריטים בקונטיינר FlowRow.

לדוגמה, אם 4 פריטים נמצאים באותו קו, ולכל אחד מהם משקלים שונים של 1f, 2f, 1f ו-3f, המשקל הכולל הוא 7f. הרווח שנותר בשורה או בעמודה יחולק ב-7f. לאחר מכן, רוחב כל פריט יחושב באמצעות: weight * (remainingSpace / totalWeight).

אפשר להשתמש בשילוב של Modifier.weight ומספר הפריטים המקסימלי עם FlowRow או FlowColumn כדי ליצור פריסה דמוית רשת. הגישה הזו שימושית ליצירת פריסות רספונסיביות שמותאמות לגודל המכשיר.

הנה כמה דוגמאות למה שאפשר לעשות באמצעות משקלים. דוגמה אחת היא רשת שבה הפריטים בגודל שווה, כמו שמוצג כאן:

רשת שנוצרה עם שורת זרימה
איור 2. שימוש ב-FlowRow ליצירת רשת

כדי ליצור רשת של פריטים בגדלים שווים, אפשר לבצע את הפעולות הבאות:

val rows = 3
val columns = 3
FlowRow(
    modifier = Modifier.padding(4.dp),
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    maxItemsInEachRow = rows
) {
    val itemModifier = Modifier
        .padding(4.dp)
        .height(80.dp)
        .weight(1f)
        .clip(RoundedCornerShape(8.dp))
        .background(MaterialColors.Blue200)
    repeat(rows * columns) {
        Spacer(modifier = itemModifier)
    }
}

חשוב לציין שאם מוסיפים עוד פריט וחוזרים עליו 10 פעמים במקום 9, הפריט האחרון תופס את כל העמודה האחרונה, כי המשקל הכולל של כל השורה הוא 1f:

הפריט האחרון בגודל מלא ברשת
איור 3. שימוש ב-FlowRow כדי ליצור רשת שבה הפריט האחרון תופס את כל הרוחב

אפשר לשלב משקלים עם Modifiers אחרים, כמו Modifier.width(exactDpAmount), Modifier.aspectRatio(aspectRatio) או Modifier.fillMaxWidth(fraction). כל העיבודים האלה פועלים יחד כדי לאפשר שינוי גודל רספונסיבי של פריטים בתוך FlowRow (או FlowColumn).

אפשר גם ליצור רשת מתחלפת של פריטים בגדלים שונים, שבה שני פריטים תופסים חצי מהרוחב כל אחד, ופריט אחד תופס את כל הרוחב של העמודה הבאה:

רשת לסירוגין עם שורת זרימה
איור 4. ‫FlowRow עם גדלים מתחלפים של שורות

כדי לעשות את זה, אפשר להשתמש בקוד הבא:

FlowRow(
    modifier = Modifier.padding(4.dp),
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    maxItemsInEachRow = 2
) {
    val itemModifier = Modifier
        .padding(4.dp)
        .height(80.dp)
        .clip(RoundedCornerShape(8.dp))
        .background(Color.Blue)
    repeat(6) { item ->
        // if the item is the third item, don't use weight modifier, but rather fillMaxWidth
        if ((item + 1) % 3 == 0) {
            Spacer(modifier = itemModifier.fillMaxWidth())
        } else {
            Spacer(modifier = itemModifier.weight(0.5f))
        }
    }
}

מידות חלקיות

באמצעות המאפיין Modifier.fillMaxWidth(fraction), אפשר לציין את גודל הקונטיינר שהפריט צריך למלא. ההתנהגות של המאפיין Modifier.fillMaxWidth(fraction) שונה כשמחילים אותו על Row או על Column, כי הפריטים ב-Row/Column תופסים אחוז מסוים מהרוחב שנותר, ולא את הרוחב של כל המאגר.

לדוגמה, הקוד הבא מניב תוצאות שונות כשמשתמשים ב-FlowRow לעומת Row:

FlowRow(
    modifier = Modifier.padding(4.dp),
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    maxItemsInEachRow = 3
) {
    val itemModifier = Modifier
        .clip(RoundedCornerShape(8.dp))
    Box(
        modifier = itemModifier
            .height(200.dp)
            .width(60.dp)
            .background(Color.Red)
    )
    Box(
        modifier = itemModifier
            .height(200.dp)
            .fillMaxWidth(0.7f)
            .background(Color.Blue)
    )
    Box(
        modifier = itemModifier
            .height(200.dp)
            .weight(1f)
            .background(Color.Magenta)
    )
}

‫FlowRow: הפריט האמצעי עם חלק של 0.7 מרוחב הקונטיינר כולו.

רוחב חלקי עם שורת זרימה

‫Row: הפריט האמצעי תופס 0.7 אחוזים מהרוחב הנותר של Row.

רוחב חלקי עם שורה

‫fillMaxColumnWidth() וגם fillMaxRowHeight()

אם מחילים את Modifier.fillMaxColumnWidth() או את Modifier.fillMaxRowHeight() על פריט בתוך FlowColumn או FlowRow, הפריטים באותה עמודה או שורה יתפסו את אותו רוחב או גובה כמו הפריט הכי גדול בעמודה או בשורה.

לדוגמה, כאן נעשה שימוש ב-FlowColumn כדי להציג את רשימת קינוחי Android. אפשר לראות את ההבדל ברוחב של כל פריט כשמחילים את Modifier.fillMaxColumnWidth() על הפריטים לעומת המצב שבו לא מחילים אותו והפריטים עוברים לשורה הבאה.

FlowColumn(
    Modifier
        .padding(20.dp)
        .fillMaxHeight()
        .fillMaxWidth(),
    horizontalArrangement = Arrangement.spacedBy(8.dp),
    verticalArrangement = Arrangement.spacedBy(8.dp),
    maxItemsInEachColumn = 5,
) {
    repeat(listDesserts.size) {
        Box(
            Modifier
                .fillMaxColumnWidth()
                .border(1.dp, Color.DarkGray, RoundedCornerShape(8.dp))
                .padding(8.dp)
        ) {

            Text(
                text = listDesserts[it],
                fontSize = 18.sp,
                modifier = Modifier.padding(3.dp)
            )
        }
    }
}

‫Modifier.fillMaxColumnWidth() חל על כל פריט

fillMaxColumnWidth

לא הוגדרו שינויים ברוחב (פריטים עוטפים)

לא הוגדר רוחב עמודה מקסימלי למילוי