التخطيط باستخدام Flexbox في React Native
يمكن للمكوّن تحديد تخطيط layout أبنائه باستخدام خوارزمية Flexbox التي صُمِّمَت لتوفير تخطيط متناسق على أحجام شاشات مختلفة. سنعتمد على كلّ من الخاصيّات flexDirection
، و alignItems
و justifyContent
للحصول على التصميم الصحيح.
يعمل Flexbox في React Native كما يعمل في CSS على الويب مع اختلافات طفيفة، إذ تختلف الإعدادات الافتراضية، فالقيمة الافتراضيّة للخاصيّة flexDirection
هي column
عوضًا عن row
، والقيمة الافتراضيّة للخاصيّة alignContent
هي flex-start
عوضًا عن stretch
، والقيمة الافتراضيّة للخاصيّة flexShrink
هي 0
عوضًا عن 1
، ولا تدعم خاصيّة flex
إلّا عددًا واحدًا كقيمة.
الخاصية flex
ستحدّد الخاصية flex
كيف "تملأ" عناصرك المساحةَ المتاحة على طول المحور الرئيسي main axis، إذ تُقسَم المساحة وفقًا للخاصية flex لكل عنصر.
تكون الواجهات (العروض) views ذات اللون الأحمر والأصفر والأخضر في المثال التالي كلها عناصرًا أبناء في حاوية الواجهة التي لها الخاصية flex: 1
. تستخدم الواجهة ذات اللون الأحمر الخاصية flex: 1
، وتستخدم الواجهة ذات اللون الأصفر الخاصية flex: 2
، وتستخدم الواجهة ذات اللون الأخضر الخاصية flex: 3
، أي 1 + 2 + 3 = 6، مما يعني أن الواجهة الحمرات ستحصل على 1/6
من المساحة، وتحصل الواجهة الصفراء على 2/6
من المساحة، والخضراء على 3/6
من المساحة.
إليك المثال التالي (تجربة حية):
import React from "react";
import { StyleSheet, Text, View } from "react-native";
const Flex = () => {
return (
<View style={[styles.container, {
// جرّب ضبط الخاصية flexDirection على القيمة row
flexDirection: "column"
}]}>
<View style={{ flex: 1, backgroundColor: "red" }} />
<View style={{ flex: 2, backgroundColor: "darkorange" }} />
<View style={{ flex: 3, backgroundColor: "green" }} />
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
});
export default Flex;
الخاصية flexDirection
تتحكّم الخاصية flexDirection
في اتجاه ترتيب أبناء عقدةٍ ما، حيث يُشار إلى هذا الاتجاه أيضًا بالمحور الرئيسي main axis. المحور cross axis هو المحور المتعامد مع المحور الرئيسي، أو المحور الذي توضَع فيه الأسطر التي يمكنها الالتفاف على أكثر من سطر.
column
(القيمة الافتراضية): تحاذي هذه القيمة العناصر الأبناء من الأعلى إلى الأسفل. إذا كان الالتفاف wrapping مفعَّلًا، فسيبدأ السطر التالي على يمين العنصر الأول أعلى الحاوية.row
: تحاذي هذه القيمة العناصر الأبناء من اليسار إلى اليمين. إذا كان الالتفاف wrapping مفعَّلًا، فسيبدأ السطر التالي أسفل العنصر الأول على يسار الحاوية.column-reverse
: تحاذي هذه القيمة العناصر الأبناء من الأسفل إلى الأعلى. إذا كان الالتفاف wrapping مفعَّلًا، فسيبدأ السطر التالي على يمين العنصر الأول في أسفل الحاوية.row-reverse
: تحاذي هذه القيمة العناصر الأبناء من اليمين إلى اليسار. إذا كان الالتفاف wrapping مفعَّلًا، فسيبدأ السطر التالي أسفل العنصر الأول على يمين الحاوية.
اطّلع على مزيد من المعلومات حول الخاصية flexDirection
من توثيق CSS.
إليك المثال التالي (تجربة حية):
import React, { useState } from "react";
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
const FlexDirectionBasics = () => {
const [flexDirection, setflexDirection] = useState("column");
return (
<PreviewLayout
label="flexDirection"
values={["column", "row", "row-reverse", "column-reverse"]}
selectedValue={flexDirection}
setSelectedValue={setflexDirection}
>
<View
style={[styles.box, { backgroundColor: "powderblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "skyblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "steelblue" }]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<View style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[
styles.button,
selectedValue === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value && styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View style={[styles.container, { [label]: selectedValue }]}>
{children}
</View>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 8,
backgroundColor: "aliceblue",
},
box: {
width: 50,
height: 50,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default FlexDirectionBasics;
اتجاه التخطيط Layout Direction
تحدّد خاصية اتجاه التخطيط اتجاهَ توزيع الأبناء والنص ضمن تسلسلٍ هرمي، كما يؤثّر اتجاه التخطيط أيضًا على ما تشير إليه بداية start
ونهاية end
الأطراف. يحدّد React Native اتجاه التخطيط على القيمة LTR افتراضيًا، حيث تشير البداية start
إلى اليسار وتشير النهاية end
إلى اليمين في هذه الحالة.
LTR
(القيمة الافتراضية): يوضَع النص والأبناء من اليسار إلى اليمين باستخدام هذه القيمة، حيث تُطبَّق الهوامش والحاشية المُطبَّقة على بداية العنصر من الجانب الأيسر.RTL
: يُوضَع النص والأبناء من اليمين إلى اليسار باستخدام هذه القيمة، حيث تُطبَّق الهوامش والحاشية المُطبَّقة على بداية العنصر من الجانب الأيمن.
إليك المثال التالي (تجربة حية):
import React, { useState } from "react";
import { View, TouchableOpacity, Text, StyleSheet } from "react-native";
const DirectionLayout = () => {
const [direction, setDirection] = useState("ltr");
return (
<PreviewLayout
label="direction"
selectedValue={direction}
values={["ltr", "rtl"]}
setSelectedValue={setDirection}>
<View
style={[styles.box, { backgroundColor: "powderblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "skyblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "steelblue" }]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<View style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[
styles.button,
selectedValue === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value && styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View style={[styles.container, { [label]: selectedValue }]}>
{children}
</View>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 8,
backgroundColor: "aliceblue",
},
box: {
width: 50,
height: 50,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default DirectionLayout;
الخاصية justifyContent
تحدّد الخاصية justifyContent
كيفية محاذاة المكونات الأبناء وفق محور الحاوية الرئيسي، حيث يمكنك استخدام هذه الخاصية لتوسيط ابنٍ أفقيًا ضمن حاوية مع ضبط الخاصية flexDirection
على القيمة row
أو عموديًا ضمن حاوية مع ضبط الخاصية flexDirection
على القيمة column
.
flex-start
(القيمة الافتراضية): تحاذي أبناء الحاوية إلى بداية محور الحاوية الرئيسي.flex-end
: تحاذي هذه القيمة الحاوية إلى نهاية محور الحاوية الرئيسي.center
: تحاذي أبناء الحاوية إلى مركز محور الحاوية الرئيسي.space-between
: تكون المسافة بين الأبناء متساوية على طول محور الحاوية الرئيسي، وتُوزَّع المسافة المتبقية بين الأبناء.space-around
: تكون المسافة متساوية بين الأبناء على طول محور الحاوية الرئيسي، وتُوزَّع المسافة المتبقية حول الأبناء، فإن استخدام القيمةspace-around
سيؤدي إلى توزيع المسافة المتبقية على بداية الابن الأول ونهاية الابن الأخير.space-evenly
: يُوزَّع الأبناء بمسافة متساوية ضمن حاوية المحاذاة على طول المحور الرئيسي. التباعد بين كل زوج من العناصر المتجاورة، وطرف البداية الرئيسية والعنصر الأول، وطرف النهاية الرئيسية والعنصر الأخير، كلها متماثلة تمامًا.
اطّلع على مزيد من المعلومات حول الخاصية justifyContent
من توثيق CSS.
إليك المثال التالي (تجربة حية):
import React, { useState } from "react";
import { View, TouchableOpacity, Text, StyleSheet } from "react-native";
const JustifyContentBasics = () => {
const [justifyContent, setJustifyContent] = useState("flex-start");
return (
<PreviewLayout
label="justifyContent"
selectedValue={justifyContent}
values={[
"flex-start",
"flex-end",
"center",
"space-between",
"space-around",
"space-evenly",
]}
setSelectedValue={setJustifyContent}
>
<View
style={[styles.box, { backgroundColor: "powderblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "skyblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "steelblue" }]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<View style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[styles.button, selectedValue === value && styles.selected]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value && styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View style={[styles.container, { [label]: selectedValue }]}>
{children}
</View>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 8,
backgroundColor: "aliceblue",
},
box: {
width: 50,
height: 50,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default JustifyContentBasics;
الخاصية alignItems
تحدّد الخاصية alignItems
كيفية محاذاة الأبناء وفق محور الحاوية المتعامد مع المحور الرئيسي. تتشابه محاذاة العناصر إلى حد كبير مع الخاصية justifyContent
ولكن تُطبَّق الخاصية alignItems
على المحور المتعامد مع المحور الرئيسي cross axis بدلًا من تطبيقها على المحور الرئيسي main axis.
stretch
(القيمة الافتراضية): يمتد أبناء الحاوية للتطابق مع ارتفاعheight
محور الحاوية المتعامد مع المحور الرئيسي.flex-start
: تحاذي هذه القيمة أبناء الحاوية مع بداية محور الحاوية المتعامد مع المحور الرئيسي.flex-end
: تحاذي هذه القيمة أبناء الحاوية إلى نهاية محور الحاوية المتعامد مع المحور الرئيسي.center
: تحاذي هذه القيمة أبناء الحاوية إلى مركز محور الحاوية المتعامد مع المحور الرئيسي.
baseline
: تحاذي هذه القيمة أبناء الحاوية على طول خط الأساس baseline المشترك، إذ يمكن ضبط الأبناء ليكونوا خط الأساس المرجعي لآبائهم.
ملاحظة: يجب ألا يكون للأبناء بُعدٌ ثابت على طول المحور الثانوي ليكون للخاصيّة
stretch
(التي تتحكّم بالتمدّد) تأثير، فضبط القيمة alignItems: stretch
في المثال التالي لا تأثير له حتى تُزالَ القيمة width: 50
من الأبناء.
اطّلع على مزيد من المعلومات حول الخاصية alignItems
من توثيق CSS.
إليك المثال التالي (تجربة حية):
import React, { useState } from "react";
import {
View,
TouchableOpacity,
Text,
StyleSheet,
} from "react-native";
const AlignItemsLayout = () => {
const [alignItems, setAlignItems] = useState("stretch");
return (
<PreviewLayout
label="alignItems"
selectedValue={alignItems}
values={[
"stretch",
"flex-start",
"flex-end",
"center",
"baseline",
]}
setSelectedValue={setAlignItems}
>
<View
style={[styles.box, { backgroundColor: "powderblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "skyblue" }]}
/>
<View
style={[
styles.box,
{
backgroundColor: "steelblue",
width: "auto",
minWidth: 50,
},
]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<View style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[
styles.button,
selectedValue === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value &&
styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View
style={[
styles.container,
{ [label]: selectedValue },
]}
>
{children}
</View>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 8,
backgroundColor: "aliceblue",
minHeight: 200,
},
box: {
width: 50,
height: 50,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default AlignItemsLayout;
الخاصية alignSelf
تتمتع الخاصية alignSelf
بنفس خيارات وتأثير الخاصية alignItems
، ولكن يمكنك تطبيق الخاصية alignSelf
على ابن واحد لتغيير محاذاته ضمن المكوّن الأب، بدلًا من التأثير على الأبناء ضمن الحاوية. تعدّل الخاصية alignSelf
أيّ خيار ضبطه الأب مستخدمًا الخاصية alignItems
.
إليك المثال التالي (تجربة حية):
import React, { useState } from "react";
import { View, TouchableOpacity, Text, StyleSheet } from "react-native";
const AlignSelfLayout = () => {
const [alignSelf, setAlignSelf] = useState("stretch");
return (
<PreviewLayout
label="alignSelf"
selectedValue={alignSelf}
values={["stretch", "flex-start", "flex-end", "center", "baseline"]}
setSelectedValue={setAlignSelf}>
<View
style={[styles.box, {
alignSelf,
width: "auto",
minWidth: 50,
backgroundColor: "powderblue",
}]}
/>
<View
style={[styles.box, { backgroundColor: "skyblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "steelblue" }]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<View style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[
styles.button,
selectedValue === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value &&
styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View style={styles.container}>
{children}
</View>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 8,
backgroundColor: "aliceblue",
minHeight: 200,
},
box: {
width: 50,
height: 50,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default AlignSelfLayout;
الخاصية alignContent
تحدد الخاصية alignContent
توزيع السطور على طول المحور المتعامد مع المحور الرئيسي، وهذا له تأثير فقط عندما تستطيع العناصر الالتفاف إلى عدة أسطر باستخدام الخاصية flexWrap
.
flex-start
(القيمة الافتراضية): تحاذي هذه القيمة السطور الملتفّة مع بداية محور الحاوية المتعامد مع المحور الرئيسي.flex-end
: تحاذي السطور الملتفة مع نهاية محور الحاوية المتعامد مع المحور الرئيسي.stretch
(القيمة الافتراضية عند استخدام Yoga على الويب): تمدّد السطور الملتفة لتتناسب مع ارتفاع محور الحاوية المتعامد مع المحور الرئيسي.center
: تحاذي السطور الملتفة مع مركز محور الحاوية المتعامد مع المحور الرئيسي.
space-between
: تكون المسافة بين السطور الملتفة متساوية على طول محور الحاوية المتعامد مع المحور الرئيسي، وتُوزَّع المسافة المتبقية بين السطور.space-around
: تكون المسافة متساوية بين السطور الملتفة على طول محور الحاوية المتعامد مع المحور الرئيسي، وتُوزَّع المسافة المتبقية حول السطور، فإن استخدام القيمةspace-around
سيؤدي إلى توزيع المسافة المتبقية على بداية السطر الأول ونهاية السطر الأخير.
اطّلع على مزيد من المعلومات حول الخاصية alignContent
من توثيق CSS.
إليك المثال التالي (تجربة حية):
import React, { useState } from "react";
import { View, TouchableOpacity, Text, StyleSheet } from "react-native";
const AlignContentLayout = () => {
const [alignContent, setAlignContent] = useState("flex-start");
return (
<PreviewLayout
label="alignContent"
selectedValue={alignContent}
values={[
"flex-start",
"flex-end",
"stretch",
"center",
"space-between",
"space-around",
]}
setSelectedValue={setAlignContent}>
<View
style={[styles.box, { backgroundColor: "orangered" }]}
/>
<View
style={[styles.box, { backgroundColor: "orange" }]}
/>
<View
style={[styles.box, { backgroundColor: "mediumseagreen" }]}
/>
<View
style={[styles.box, { backgroundColor: "deepskyblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "mediumturquoise" }]}
/>
<View
style={[styles.box, { backgroundColor: "mediumslateblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "purple" }]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<View style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[
styles.button,
selectedValue === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value &&
styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View
style={[
styles.container,
{ [label]: selectedValue },
]}
>
{children}
</View>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
flexWrap: "wrap",
marginTop: 8,
backgroundColor: "aliceblue",
maxHeight: 400,
},
box: {
width: 50,
height: 80,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default AlignContentLayout;
الخاصية flexWrap
تُضبَط الخاصية flexWrap
للحاويات وتتحكّم بما يحدث عندما يتجاوز الأبناء حجم الحاوية على طول المحور الرئيسي. يُجبَر الأبناء افتراضيًا على الالتزام بسطر واحد فقط (والذي يمكن أن يؤدي إلى تصغير shrink العناصر). إذا كان الالتفاف wrapping مسموحًا، فستلتف العناصر إلى عدة أسطر على طول المحور الرئيسي إن لزم الأمر.
يمكن استخدام الخاصية alignContent
عند التفاف الأسطر لتحديد كيفية وضع الأسطر في الحاوية.
اطّلع على مزيد من المعلومات حول الخاصية flexWrap
من توثيق CSS.
إليك المثال التالي (تجربة حية):
import React, { useState } from "react";
import { View, TouchableOpacity, Text, StyleSheet } from "react-native";
const FlexWrapLayout = () => {
const [flexWrap, setFlexWrap] = useState("wrap");
return (
<PreviewLayout
label="flexWrap"
selectedValue={flexWrap}
values={["wrap", "no-wrap"]}
setSelectedValue={setFlexWrap}>
<View
style={[styles.box, { backgroundColor: "orangered" }]}
/>
<View
style={[styles.box, { backgroundColor: "orange" }]}
/>
<View
style={[styles.box, { backgroundColor: "mediumseagreen" }]}
/>
<View
style={[styles.box, { backgroundColor: "deepskyblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "mediumturquoise" }]}
/>
<View
style={[styles.box, { backgroundColor: "mediumslateblue" }]}
/>
<View
style={[styles.box, { backgroundColor: "purple" }]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<View style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[
styles.button,
selectedValue === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value &&
styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View
style={[
styles.container,
{ [label]: selectedValue },
]}
>
{children}
</View>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 8,
backgroundColor: "aliceblue",
maxHeight: 400,
},
box: {
width: 50,
height: 80,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default FlexWrapLayout;
الخاصيات flexBasis
و flexGrow
و flexShrink
- الخاصية
flexBasis
هي طريقة مستقلة عن المحاور لإعطاء العنصر حجمًا افتراضيًّا على طول المحور الرئيسي، ويتشابه إعدادflexBasis
للمكوّن الابن مع إعداد العرضwidth
للمكوّن الابن إذا كان الأب حاويةً لها الخاصيةflexDirection: row
، أو مع إعداد الارتفاعheight
للمكوّن الابن إذا كان الأب عبارة عن حاويةً لها الخاصيةflexDirection: column
. وتمثّل الخاصيةflexBasis
حجم العنصر الافتراضي قبل أن تُطبَّق عليه الخاصيتانflexShrink
وflexGrow
. - تحدّد الخاصية
flexGrow
كيفيّة توزيع المسافات بين المكوّنات الأبناء ضمن الحاوية على طول المحور الرئيسي، حيث توزّع الحاوية -بعد وضع المكونات الأبناء- المسافة المتبقية بينها، بناءً على قيم الخاصيةflexGrow
الخاصّة بكلّ مكوّن ابن، وتقبلflexGrow
أيّ قيمةٍ عشرية أكبر من الصفر أو تساويه، حيث يكون الصفر هو قيمتها الافتراضيّة. توزّع الحاوية المسافةَ المتبقية بين المكونات الأبناء توزيعًا متناسبًا مع قيمflexGrow
الخاصة بالأبناء.
- تحدّد الخاصية
flexShrink
كيفيّة تصغير المكونات الأبناء على طول المحور الرئيسي عندما يتجاوز حجم المكونات الأبناء الكلي حجمَ الحاوية على طول المحور الرئيسيّ، وهذه الخاصيّة شبيهةٌ جدًا بالخاصيّةflexGrow
عند عَدّ الحجم الزائد بمثابة مسافة متبقيةٍ سالبة القيمة، وتتعاون هاتان الخاصيتان فيما بينهما للسماح للمكونات الأبناء بالتمدد والتقلص حسب الحاجة. تقبل الخاصيةflexShrink
أيّ قيمةٍ عشريّة أكبر من الصفر أو تساويه، حيث يكون الصفر هو قيمتها الافتراضيّة (الواحد هو قيمتها الافتراضيّة على الويب)، وتقلّص الحاوية المكونات الأبناء تقليصًا متناسبًا مع قيمflexShrink
الخاصة بالأبناء.
اطّلع على مزيد من المعلومات حول الخاصيات flexBasis
و flexGrow
و flexShrink
من توثيق CSS.
إليك المثال التالي (تجربة حية):
import React, { useState } from "react";
import {
View,
Text,
TextInput,
StyleSheet,
} from "react-native";
const App = () => {
const [powderblue, setPowderblue] = useState({
flexGrow: 0,
flexShrink: 1,
flexBasis: "auto",
});
const [skyblue, setSkyblue] = useState({
flexGrow: 1,
flexShrink: 0,
flexBasis: 100,
});
const [steelblue, setSteelblue] = useState({
flexGrow: 0,
flexShrink: 1,
flexBasis: 200,
});
return (
<View style={styles.container}>
<View
style={[
styles.container,
{
flexDirection: "row",
alignContent: "space-between",
},
]}
>
<BoxInfo
color="powderblue"
{...powderblue}
setStyle={setPowderblue}
/>
<BoxInfo
color="skyblue"
{...skyblue}
setStyle={setSkyblue}
/>
<BoxInfo
color="steelblue"
{...steelblue}
setStyle={setSteelblue}
/>
</View>
<View style={styles.previewContainer}>
<View
style={[
styles.box,
{
flexBasis: powderblue.flexBasis,
flexGrow: powderblue.flexGrow,
flexShrink: powderblue.flexShrink,
backgroundColor: "powderblue",
},
]}
/>
<View
style={[
styles.box,
{
flexBasis: skyblue.flexBasis,
flexGrow: skyblue.flexGrow,
flexShrink: skyblue.flexShrink,
backgroundColor: "skyblue",
},
]}
/>
<View
style={[
styles.box,
{
flexBasis: steelblue.flexBasis,
flexGrow: steelblue.flexGrow,
flexShrink: steelblue.flexShrink,
backgroundColor: "steelblue",
},
]}
/>
</View>
</View>
);
};
const BoxInfo = ({
color,
flexBasis,
flexShrink,
setStyle,
flexGrow,
}) => (
<View style={[styles.row, { flexDirection: "column" }]}>
<View
style={[
styles.boxLabel,
{
backgroundColor: color,
},
]}
>
<Text
style={{
color: "#fff",
fontWeight: 500,
textAlign: "center",
}}
>
Box
</Text>
</View>
<Text style={styles.label}>flexBasis</Text>
<TextInput
value={flexBasis}
style={styles.input}
onChangeText={(fB) =>
setStyle((value) => ({
...value,
flexBasis: isNaN(parseInt(fB))
? "auto"
: parseInt(fB),
}))
}
/>
<Text style={styles.label}>flexShrink</Text>
<TextInput
value={flexShrink}
style={styles.input}
onChangeText={(fS) =>
setStyle((value) => ({
...value,
flexShrink: isNaN(parseInt(fS))
? ""
: parseInt(fS),
}))
}
/>
<Text style={styles.label}>flexGrow</Text>
<TextInput
value={flexGrow}
style={styles.input}
onChangeText={(fG) =>
setStyle((value) => ({
...value,
flexGrow: isNaN(parseInt(fG))
? ""
: parseInt(fG),
}))
}
/>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
paddingHorizontal: 10,
},
box: {
flex: 1,
height: 50,
width: 50,
},
boxLabel: {
minWidth: 80,
padding: 8,
borderRadius: 4,
marginTop: 8,
},
label: {
marginTop: 6,
fontSize: 16,
fontWeight: "100",
},
previewContainer: {
flex: 1,
flexDirection: "row",
backgroundColor: "aliceblue",
},
row: {
flex: 1,
flexDirection: "row",
flexWrap: "wrap",
alignItems: "center",
marginBottom: 10,
},
input: {
borderBottomWidth: 1,
paddingVertical: 3,
width: 50,
textAlign: "center",
},
});
export default App;
العرض والارتفاع
تحدد خاصية العرض width
عرض منطقة محتوى العنصر، وتحدّد خاصية الارتفاع height
ارتفاع منطقة محتوى العنصر.
يمكن أن تأخذ كلٌّ من الخاصيتين width
و height
القيم التالية:
auto
(القيمة الافتراضية): يحسب إطار عمل React Native عرض أو ارتفاع العنصر بناءً على محتواه، سواء كان ذلك المحتوى ابنًا آخر أو نصًا أو صورة.pixels
: تحدّد هذه القيمة العرض أو الارتفاع بالبكسلات المطلقة، حيث قد يكون هذا هو بُعد العقدة النهائي وقد لا يكون كذلك، اعتمادًا على الأنماط الأخرى التي تُضبَط للمكوّن.percentage
: تحدّد هذه القيمة العرض أو الارتفاع على أنها نسبة مئوية من عرض المكوّن الأب أو ارتفاعه.
إليك المثال التالي (تجربة حية):
import React, { useState } from "react";
import {
View,
SafeAreaView,
TouchableOpacity,
Text,
StyleSheet,
} from "react-native";
const WidthHeightBasics = () => {
const [widthType, setWidthType] = useState("auto");
const [heightType, setHeightType] = useState("auto");
return (
<PreviewLayout
widthType={widthType}
heightType={heightType}
widthValues={["auto", 300, "80%"]}
heightValues={["auto", 200, "60%"]}
setWidthType={setWidthType}
setHeightType={setHeightType}
>
<View
style={{
alignSelf: "flex-start",
backgroundColor: "aliceblue",
height: heightType,
width: widthType,
padding: 15,
}}
>
<View
style={[
styles.box,
{ backgroundColor: "powderblue" },
]}
/>
<View
style={[
styles.box,
{ backgroundColor: "skyblue" },
]}
/>
<View
style={[
styles.box,
{ backgroundColor: "steelblue" },
]}
/>
</View>
</PreviewLayout>
);
};
const PreviewLayout = ({
children,
widthType,
heightType,
widthValues,
heightValues,
setWidthType,
setHeightType,
}) => (
<View style={{ flex: 1, padding: 10 }}>
<View style={styles.row}>
<Text style={styles.label}>width </Text>
{widthValues.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setWidthType(value)}
style={[
styles.button,
widthType === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
widthType === value && styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View style={styles.row}>
<Text style={styles.label}>height </Text>
{heightValues.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setHeightType(value)}
style={[
styles.button,
heightType === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
heightType === value && styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
{children}
</View>
);
const styles = StyleSheet.create({
box: {
width: 50,
height: 50,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
padding: 8,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginRight: 10,
marginBottom: 10,
},
selected: {
backgroundColor: "coral",
shadowOpacity: 0,
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default WidthHeightBasics;
التخطيط المطلق Absolute والنسبي Relative
يحدّد نوع خاصية الموضع position
لعنصرٍ ما كيفية وضعه ضمن المكوّن الأب.
relative
(القيمة الافتراضية): يوضَع العنصر افتراضيًا موضعًا نسبيًا، وهذا يعني أن العنصر يُوضَع وفقًا لتدفق التخطيط الطبيعي، ثم يُزاح بالنسبة إلى هذا الموضع بناءً على القيمtop
وright
وbottom
وleft
، إذ لا تؤثر الإزاحة على موضع أي عنصر شقيق أو أب.absolute
: لا يشترك العنصر في تدفق التخطيط الطبيعي عند وضعه بموضع مطلق، وإنما يوضَع بصورةٍ مستقلة عن أشقائه، حيث يُحدَّد الموضع بناءً على القيمtop
وright
وbottom
وleft
.
إليك المثال التالي (تجربة حية):
import React, { useState } from "react";
import {
View,
SafeAreaView,
TouchableOpacity,
Text,
StyleSheet,
} from "react-native";
const PositionLayout = () => {
const [position, setPosition] = useState("relative");
return (
<PreviewLayout
label="position"
selectedValue={position}
values={["relative", "absolute"]}
setSelectedValue={setPosition}
>
<View
style={[
styles.box,
{
top: 25,
left: 25,
position,
backgroundColor: "powderblue",
},
]}
/>
<View
style={[
styles.box,
{
top: 50,
left: 50,
position,
backgroundColor: "skyblue",
},
]}
/>
<View
style={[
styles.box,
{
top: 75,
left: 75,
position,
backgroundColor: "steelblue",
},
]}
/>
</PreviewLayout>
);
};
const PreviewLayout = ({
label,
children,
values,
selectedValue,
setSelectedValue,
}) => (
<View style={{ padding: 10, flex: 1 }}>
<Text style={styles.label}>{label}</Text>
<View style={styles.row}>
{values.map((value) => (
<TouchableOpacity
key={value}
onPress={() => setSelectedValue(value)}
style={[
styles.button,
selectedValue === value && styles.selected,
]}
>
<Text
style={[
styles.buttonLabel,
selectedValue === value &&
styles.selectedLabel,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
<View style={styles.container}>{children}</View>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 8,
backgroundColor: "aliceblue",
minHeight: 200,
},
box: {
width: 50,
height: 50,
},
row: {
flexDirection: "row",
flexWrap: "wrap",
},
button: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: "oldlace",
alignSelf: "flex-start",
marginHorizontal: "1%",
marginBottom: 6,
minWidth: "48%",
textAlign: "center",
},
selected: {
backgroundColor: "coral",
borderWidth: 0,
},
buttonLabel: {
fontSize: 12,
fontWeight: "500",
color: "coral",
},
selectedLabel: {
color: "white",
},
label: {
textAlign: "center",
marginBottom: 10,
fontSize: 24,
},
});
export default PositionLayout;
مزيد من المعلومات
اطّلع على ملعب yoga التفاعلي الذي يمكنك استخدامه لفهم flexbox فهمًا أفضل، واطلع أيضًا على توثيق CSS العربي، ويمكنك أيضًا الاطّلاع على بعض الأمثلة من Wix Engineers.
هذه هي الأساسيات، لكن هناك عدّة أنماط أخرى قد تحتاج إليها لتخطيط تطبيقاتك. اطّلع على قائمة خاصيّات التخطيط الكاملة.