كيفية تصميم فقاعة دردشة شبيهة بـ iMessage في React Native

دقائق القراءة: 6

مقدمة: لماذا تبدو فقاعة iMessage مميزة؟

سواء كنت من محبي منتجات Apple أم لا، فمن الصعب تجاهل جودة الواجهة البصرية التي تشتهر بها الشركة. ومن أبرز التفاصيل التصميمية في تطبيق iMessage شكل فقاعة الرسائل، وخصوصاً الذيل المنحني الصغير المتصل بحاوية النص. هذه اللمسة البسيطة تمنح واجهة المحادثة مظهراً أنيقاً واحترافياً.

في هذا المقال ستتعرف على طريقة بناء فقاعة دردشة شبيهة بتصميم iMessage باستخدام React Native، مع شرح الفكرة التصميمية أولاً، ثم تحويلها إلى تطبيق عملي قابل للاستخدام داخل قوائم المحادثات.

تصميم فقاعة دردشة شبيهة بتطبيق iMessage في React Native

المتطلبات الأساسية قبل البدء

لفهم الخطوات بسلاسة، يُفضّل أن تكون لديك معرفة أساسية بالمفاهيم التالية:

  • JSX
  • React Native
  • HTML وCSS

ما المقصود بفقاعة الدردشة؟

فقاعة الدردشة هي حاوية مرئية تُستخدم لعرض نص الرسالة داخل تطبيقات المحادثة. غالباً ما تظهر الرسائل المرسلة في جهة، والرسائل المستلمة في الجهة المقابلة، مع اختلاف اللون لتسهيل التمييز بينها.

في معظم التطبيقات، تكون الفقاعة مجرد مستطيل بحواف دائرية. لكن ما يجعل تصميم iMessage مختلفاً هو وجود ذيل صغير منحني في أسفل الفقاعة، فيعطي الرسالة مظهراً أقرب إلى الفقاعات التقليدية للمحادثة.

مثال على فقاعة دردشة مع ذيل منحني يشبه iMessage

فهم فكرة تصميم الذيل المنحني

إذا دققت النظر في هذا التصميم، فستجد أن فقاعة iMessage ليست عنصراً واحداً فقط، بل هي مزيج من:

  • حاوية نص رئيسية بحواف دائرية.
  • عنصر إضافي يشكّل الذيل المنحني.
  • عنصر متداخل آخر لإخفاء جزء من الذيل وصنع الشكل النهائي.

التحدي الحقيقي ليس في رسم الحاوية نفسها، بل في ربط الذيل بها بصرياً بطريقة تبدو طبيعية ومتناسقة.

كيف تُبنى الفكرة في HTML وCSS؟

قبل الانتقال إلى React Native، من المفيد فهم النسخة الأساسية في HTML وCSS، لأن منطق التصميم نفسه سيُستخدم لاحقاً.

p {
  max-width: 255px;
  word-wrap: break-word;
  margin-bottom: 12px;
  line-height: 24px;
  position: relative;
  padding: 10px 20px;
  border-radius: 25px;

  &:before,
  &:after {
    content: "";
    position: absolute;
    bottom: 0;
    height: 25px;
  }
}

.from-me {
  color: white;
  background: #0B93F6;
  align-self: flex-end;

  &:before {
    right: -7px;
    width: 20px;
    background-color: #0B93F6;
    border-bottom-left-radius: 16px 14px;
  }

  &:after {
    right: -26px;
    width: 26px;
    background-color: white;
    border-bottom-left-radius: 10px;
  }
}

.from-them {
  background: #E5E5EA;
  color: black;
  align-self: flex-start;

  &:before {
    left: -7px;
    width: 20px;
    background-color: #E5E5EA;
    border-bottom-right-radius: 16px;
  }

  &:after {
    left: -26px;
    width: 26px;
    background-color: white;
    border-bottom-right-radius: 10px;
  }
}

شرح البنية خطوة بخطوة

العنصر <p> هنا يمثل الحاوية الأساسية للرسالة، ويحتوي على خصائص مثل padding وposition: relative وborder-radius. أما العنصران &:before و&:after فيُستخدمان لرسم الذيل المنحني.

الفكرة ببساطة أن:

  1. العنصر &:before يرسم الامتداد الأساسي للذيل.
  2. العنصر &:after يتداخل معه جزئياً.
  3. يتم ضبط لون كل عنصر بحيث ينسجم أحدهما مع لون الفقاعة، بينما ينسجم الآخر مع خلفية شاشة المحادثة.

هذه الحيلة البصرية تجعل الذيل يبدو مندمجاً مع الفقاعة بدلاً من كونه شكلاً منفصلاً.

المرحلة الأولى من رسم ذيل فقاعة الدردشة باستخدام beforeتداخل العنصر after مع before لتشكيل انحناءة ذيل الرسالةتغيير لون الخلفية لإخفاء جزء من الذيل داخل شاشة المحادثةالشكل النهائي لذيل فقاعة الدردشة بعد مطابقة الألوان

تحويل الفكرة إلى React Native

عند نقل هذا الأسلوب إلى React Native، لن نستخدم &:before أو &:after كما في CSS، بل سنعوضهما بعناصر <View> إضافية تُوضَع داخل فقاعة الرسالة.

ويُفضّل هنا استخدام المكوّن FlatList عند عرض الرسائل، لأنه أكثر استقراراً وكفاءة من استخدام map في واجهات المحادثة الطويلة أو المتغيرة.

الخطوات الأساسية

  1. إنشاء فقاعة الرسالة.
  2. إضافة عنصرين فرعيين لتكوين الذيل.
  3. تطبيق الأنماط المناسبة على الفقاعة والذيل.
  4. عرض الرسائل داخل FlatList.

إنشاء فقاعة الدردشة مع الذيل

في المثال التالي، ستلاحظ أن العنصر الخارجي <View> يمثل جسم الفقاعة، بينما العنصران الداخليان يمثلان جزأي الذيل.

<View
  style={{
    backgroundColor: "#0078fe",
    padding: 10,
    marginLeft: '45%',
    borderRadius: 5,
    marginTop: 5,
    marginRight: "5%",
    maxWidth: '50%',
    alignSelf: 'flex-end',
    borderRadius: 20,
  }}
  key={index}
>
  <Text
    style={{
      fontSize: 16,
      color: "#fff",
    }}
    key={index}
  >
    {item.text}
  </Text>
  <View style={styles.rightArrow}></View>
  <View style={styles.rightArrowOverlap}></View>
</View>

// Received Message
<View
  style={{
    backgroundColor: "#dedede",
    padding: 10,
    borderRadius: 5,
    marginTop: 5,
    marginLeft: "5%",
    maxWidth: '50%',
    alignSelf: 'flex-start',
    borderRadius: 20,
  }}
  key={index}
>
  <Text
    style={{
      fontSize: 16,
      color: "#000",
      justifyContent: "center"
    }}
    key={index}
  >
    {item.text}
  </Text>
  <View style={styles.leftArrow}></View>
  <View style={styles.leftArrowOverlap}></View>
</View>

في هذا التركيب:

  • الرسائل المرسلة تُحاذى إلى اليمين باستخدام alignSelf: 'flex-end'.
  • الرسائل المستلمة تُحاذى إلى اليسار باستخدام alignSelf: 'flex-start'.
  • العنصران rightArrow وrightArrowOverlap يصنعان ذيل الرسالة المرسلة.
  • العنصران leftArrow وleftArrowOverlap يصنعان ذيل الرسالة المستلمة.

إضافة الأنماط الخاصة بالفقاعة والذيل

الآن نحتاج إلى تعريف الأنماط داخل StyleSheet.create() حتى يظهر الذيل بالشكل الصحيح.

const styles = StyleSheet.create({
  rightArrow: {
    position: "absolute",
    backgroundColor: "#0078fe",
    width: 20,
    height: 25,
    bottom: 0,
    borderBottomLeftRadius: 25,
    right: -10
  },
  rightArrowOverlap: {
    position: "absolute",
    backgroundColor: "#eeeeee",
    width: 20,
    height: 35,
    bottom: -6,
    borderBottomLeftRadius: 18,
    right: -20
  },
  leftArrow: {
    position: "absolute",
    backgroundColor: "#dedede",
    width: 20,
    height: 25,
    bottom: 0,
    borderBottomRightRadius: 25,
    left: -10
  },
  leftArrowOverlap: {
    position: "absolute",
    backgroundColor: "#eeeeee",
    width: 20,
    height: 35,
    bottom: -6,
    borderBottomRightRadius: 18,
    left: -20
  },
})

لماذا تنجح هذه الطريقة؟

تعتمد الفكرة على التداخل البصري بين عنصرين:

  • العنصر الأول يحمل لون الفقاعة نفسها.
  • العنصر الثاني يحمل لون خلفية شاشة المحادثة، فيغطي جزءاً من العنصر الأول.

والنتيجة النهائية هي ذيل منحني يبدو كأنه امتداد طبيعي للفقاعة. هذه التقنية عملية جداً، ولا تحتاج إلى رسوم معقدة أو مكتبات إضافية.

دمج الفقاعات داخل FlatList

بعد تجهيز شكل الفقاعة والذيل، يأتي دور عرض الرسائل داخل قائمة ديناميكية. هنا يبرز دور FlatList كخيار مناسب لتطبيقات المحادثة بسبب كفاءته في التعامل مع العناصر المتكررة.

<FlatList
  style={{ backgroundColor: "#eeeeee" }}
  data={this.state.chat_log}
  ref={ref => (this.FlatListRef = ref)}
  renderItem={({ item, index }) => {
    rowId = {index}
    if (SENT_MESSAGE) {
      return (
        <View
          style={{
            backgroundColor: "#0078fe",
            padding: 10,
            marginLeft: '45%',
            borderRadius: 5,
            marginTop: 5,
            marginRight: "5%",
            maxWidth: '50%',
            alignSelf: 'flex-end',
            borderRadius: 20,
          }}
          key={index}
        >
          <Text
            style={{
              fontSize: 16,
              color: "#fff",
            }}
            key={index}
          >
            {item.text}
          </Text>
          <View style={styles.rightArrow}></View>
          <View style={styles.rightArrowOverlap}></View>
        </View>
      )
    } else {
      return (
        <View
          style={{
            backgroundColor: "#dedede",
            padding: 10,
            borderRadius: 5,
            marginTop: 5,
            marginLeft: "5%",
            maxWidth: '50%',
            alignSelf: 'flex-start',
            borderRadius: 20,
          }}
          key={index}
        >
          <Text
            style={{
              fontSize: 16,
              color: "#000",
              justifyContent: "center"
            }}
            key={index}
          >
            {item.text}
          </Text>
          <View style={styles.leftArrow}></View>
          <View style={styles.leftArrowOverlap}></View>
        </View>
      )
    }
  }}
  keyExtractor={(item, index) => index.toString()}
/>

ملاحظات مهمة على القيم المستخدمة

القيم مثل borderRadius وpadding وmargin وbackgroundColor ليست ثابتة إلزامياً، بل يمكنك تعديلها بما يناسب هوية تطبيقك. مثلاً:

  • زيادة borderRadius تمنح الفقاعة مظهراً أكثر نعومة.
  • تعديل maxWidth يؤثر في عرض الرسالة على الشاشات المختلفة.
  • اختيار ألوان متناسقة يعزز تجربة الاستخدام ويجعل الواجهة أكثر احترافية.

ومن الأفضل دائماً اختبار التصميم على أكثر من جهاز وحجم شاشة للتأكد من ثبات المظهر وسهولة القراءة.

أفضل الممارسات لتحسين واجهة المحادثة

1. اجعل العرض متجاوباً

استخدام قيم نسبية مثل maxWidth: '50%' أو الهوامش النسبية يساعد في الحفاظ على شكل مناسب للفقاعات عبر الأجهزة المختلفة، بدلاً من الاعتماد على قيم جامدة قد تنكسر على الشاشات الصغيرة.

2. حافظ على التباين اللوني

احرص على أن يكون لون النص واضحاً فوق لون الخلفية. فالرسائل المرسلة باللون الأزرق تحتاج عادةً إلى نص أبيض، بينما الرسائل المستلمة ذات الخلفية الفاتحة يناسبها نص داكن.

3. اختبر الذيل مع الخلفية الحقيقية

بما أن عنصر التداخل يعتمد على لون خلفية الشاشة، يجب أن يكون لون overlap مطابقاً تماماً لخلفية واجهة المحادثة. أي اختلاف بسيط قد يجعل الذيل يبدو مشوهاً أو منفصلاً بصرياً.

4. استخدم FlatList للأداء الأفضل

عند التعامل مع سجل محادثات طويل، يوفر FlatList أداءً أعلى من الحلول البسيطة، لأنه لا يرسم جميع العناصر دفعة واحدة، بل يديرها بذكاء حسب مساحة العرض.

النتيجة النهائية

بعد تطبيق الخطوات السابقة، ستحصل على فقاعة دردشة أنيقة تشبه إلى حد كبير تصميم iMessage، ويمكن دمجها بسهولة في أي تطبيق مراسلة أو شاشة محادثات داخل مشروعك.

النتيجة النهائية لفقاعة دردشة شبيهة بـ iMessage على React Native

الخلاصة التقنية

فكرة فقاعة iMessage لا تعتمد على تعقيد برمجي بقدر ما تعتمد على فهم جيد للتراكب البصري بين العناصر. باستخدام عدة عناصر <View> مع تموضع مطلق وألوان مدروسة، يمكنك تنفيذ تصميم احترافي وخفيف داخل React Native دون الحاجة إلى مكتبات رسومية إضافية. وإذا كنت تطور تطبيق دردشة، فإن هذا الأسلوب يمنح واجهتك طابعاً أكثر جودة وتميزاً مع الحفاظ على بساطة التنفيذ وقابلية التخصيص.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *