كيفية تصميم واجهة تطبيق دردشة باستخدام Flutter وDart
مقدمة: لماذا تُعد واجهة تطبيقات الدردشة مهمة؟
أصبحت تطبيقات الدردشة اليوم من أكثر الأدوات استخداماً للتواصل بين الأصدقاء والعائلة وفرق العمل عبر الهواتف الذكية. ومع هذا الانتشار، لم يعد كافياً أن يعمل التطبيق بشكل صحيح فقط، بل يجب أن يقدّم تجربة استخدام مريحة وواجهة واضحة وسريعة وسهلة التفاعل.
في هذا الدليل العملي، سنتعلم كيفية بناء واجهة تطبيق دردشة حديثة باستخدام Flutter ولغة Dart. سنركّز على تصميم الواجهة فقط، بدءاً من إنشاء المشروع، مروراً بالشاشة الرئيسية وقائمة المحادثات، وصولاً إلى شاشة تفاصيل الرسائل.

إنشاء مشروع جديد باستخدام Flutter
قبل البدء، تأكد من تثبيت Flutter SDK وتجهيز بيئة التطوير بشكل صحيح. بعد ذلك، يمكنك إنشاء مشروع جديد عبر تنفيذ الأمر التالي داخل الطرفية:
flutter create ChatApp
بعد إنشاء المشروع، انتقل إلى مجلد المشروع وشغّل التطبيق باستخدام الأمر التالي:
flutter run
عند نجاح التشغيل، سيظهر التطبيق الافتراضي على المحاكي أو على الجهاز الحقيقي.

إعداد الملف الرئيسي للتطبيق
سنبدأ بتعديل الملف main.dart لإزالة الشيفرة الافتراضية واستبدالها بهيكل بسيط يعتمد على MaterialApp:
import 'package:flutter/material.dart' ;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo' ,
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false ,
home: Container(),
);
}
}
في هذه المرحلة، نستخدم Container مؤقتاً كواجهة رئيسية إلى أن ننشئ شاشة الصفحة الرئيسية الخاصة بالتطبيق.
بناء الشاشة الرئيسية للتطبيق
داخل المجلد ./lib، أنشئ مجلداً جديداً باسم ./screens. هذا المجلد سيحتوي على ملفات الشاشات المختلفة.
ثم أنشئ ملفاً باسم homePage.dart داخل ./lib/screens، وأضف الشيفرة التالية:
import 'package:flutter/material.dart' ;
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(child: Text("Chat")),
),
);
}
}
بعد ذلك، عد إلى ملف main.dart واجعل الصفحة الرئيسية هي HomePage() بدلاً من Container():
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo' ,
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false ,
home: HomePage(),
);
}
}

إضافة شريط التنقل السفلي
لإنشاء تجربة استخدام قريبة من تطبيقات الدردشة الحديثة، سنضيف شريط تنقل سفلي باستخدام العنصر BottomNavigationBar داخل الخاصية bottomNavigationBar في Scaffold:
return Scaffold(
body: Container(
child: Center(child: Text("Chat")),
),
bottomNavigationBar: BottomNavigationBar(
selectedItemColor: Colors.red,
unselectedItemColor: Colors.grey.shade600,
selectedLabelStyle: TextStyle(fontWeight: FontWeight.w600),
unselectedLabelStyle: TextStyle(fontWeight: FontWeight.w600),
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.message),
title: Text("Chats"),
),
BottomNavigationBarItem(
icon: Icon(Icons.group_work),
title: Text("Channels"),
),
BottomNavigationBarItem(
icon: Icon(Icons.account_box),
title: Text("Profile"),
),
],
),
);
بهذا أصبح لدينا شريط تنقل يضم الأقسام الأساسية:
- المحادثات
- القنوات
- الملف الشخصي

إنشاء شاشة قائمة المحادثات
الخطوة التالية هي بناء القسم الرئيسي الذي يعرض المحادثات. سننشئ ملفاً جديداً باسم chatPage.dart داخل مجلد ./lib/screens، ثم نضيف هيكل شاشة قابل للتطوير:
import 'package:flutter/material.dart' ;
class ChatPage extends StatefulWidget {
@override
_ChatPageState createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Center(child: Text("Chat")),
),
);
}
}
ثم استبدل محتوى body في homePage.dart من Container إلى ChatPage():
return Scaffold(
body: ChatPage(),
bottomNavigationBar: BottomNavigationBar(

إضافة ترويسة جذابة لقائمة المحادثات
من المهم أن تبدأ شاشة المحادثات بعنوان واضح وزر للإجراءات السريعة. أضف القسم التالي داخل Column في شاشة chatPage.dart:
return Scaffold(
body: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SafeArea(
child: Padding(
padding: EdgeInsets.only(left: 16 ,right: 16 ,top: 10 ),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"Conversations" ,
style: TextStyle(fontSize: 32 ,fontWeight: FontWeight.bold),
),
Container(
padding: EdgeInsets.only(left: 8 ,right: 8 ,top: 2 ,bottom: 2 ),
height: 30 ,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Colors.pink[50],
),
child: Row(
children: <Widget>[
Icon(Icons.add,color: Colors.pink,size: 20),
SizedBox(width: 2),
Text(
"Add New" ,
style: TextStyle(fontSize: 14 ,fontWeight: FontWeight.bold),
),
],
),
)
],
),
),
),
],
),
),
);
استخدام SingleChildScrollView مع BouncingScrollPhysics() يمنح الشاشة سلاسة أكبر أثناء التمرير، خصوصاً على الأجهزة التي تدعم تأثير الارتداد.

إضافة شريط بحث داخل واجهة الدردشة
وجود شريط بحث في الأعلى يجعل الوصول إلى المحادثات أسرع، خاصة مع ازدياد عدد المستخدمين. أضف العنصر التالي أسفل الترويسة مباشرة:
Padding(
padding: EdgeInsets.only(top: 16 ,left: 16 ,right: 16 ),
child: TextField(
decoration: InputDecoration(
hintText: "Search..." ,
hintStyle: TextStyle(color: Colors.grey.shade600),
prefixIcon: Icon(Icons.search,color: Colors.grey.shade600, size: 20),
filled: true,
fillColor: Colors.grey.shade100,
contentPadding: EdgeInsets.all(8),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
borderSide: BorderSide(color: Colors.grey.shade100),
),
),
),
),

بناء نموذج بيانات المحادثات
حتى نعرض قائمة محادثات منظمة، من الأفضل إنشاء نموذج بيانات مستقل. أنشئ مجلداً جديداً باسم ./models ثم أضف ملفاً باسم chatUsersModel.dart:
import 'package:flutter/cupertino.dart' ;
class ChatUsers {
String name;
String messageText;
String imageURL;
String time;
ChatUsers({
@required this.name,
@required this.messageText,
@required this.imageURL,
@required this.time
});
}
هذا النموذج يحفظ المعلومات الأساسية لكل محادثة:
- اسم المستخدم
- آخر رسالة
- رابط الصورة الشخصية
- وقت الرسالة
إعداد بيانات تجريبية لقائمة المحادثات
داخل ملف chatPage.dart، أضف قائمة تجريبية من المستخدمين:
class _ChatPageState extends State<ChatPage> {
List<ChatUsers> chatUsers = [
ChatUsers(name: "Jane Russel", messageText: "Awesome Setup", imageURL: "images/userImage1.jpeg", time: "Now"),
ChatUsers(name: "Glady's Murphy", messageText: "That's Great", imageURL: "images/userImage2.jpeg", time: "Yesterday"),
ChatUsers(name: "Jorge Henry", messageText: "Hey where are you?", imageURL: "images/userImage3.jpeg", time: "31 Mar"),
ChatUsers(name: "Philip Fox", messageText: "Busy! Call me in 20 mins", imageURL: "images/userImage4.jpeg", time: "28 Mar"),
ChatUsers(name: "Debra Hawkins", messageText: "Thankyou, It's awesome", imageURL: "images/userImage5.jpeg", time: "23 Mar"),
ChatUsers(name: "Jacob Pena", messageText: "will update you in evening", imageURL: "images/userImage6.jpeg", time: "17 Mar"),
ChatUsers(name: "Andrey Jones", messageText: "Can you please share the file?", imageURL: "images/userImage7.jpeg", time: "24 Feb"),
ChatUsers(name: "John Wick", messageText: "How are you?", imageURL: "images/userImage8.jpeg", time: "18 Feb"),
];
هذه البيانات التجريبية مفيدة لتصميم الواجهة قبل ربطها لاحقاً بواجهة خلفية أو قاعدة بيانات.
إنشاء مكوّن مستقل لكل عنصر محادثة
لفصل الواجهة بطريقة احترافية، من الأفضل إنشاء عنصر مستقل يمثل كل محادثة. أنشئ مجلداً باسم ./widgets ثم أضف ملفاً باسم conversationList.dart:
import 'package:flutter/material.dart' ;
class ConversationList extends StatefulWidget {
String name;
String messageText;
String imageUrl;
String time;
bool isMessageRead;
ConversationList({
@required this.name,
@required this.messageText,
@required this.imageUrl,
@required this.time,
@required this.isMessageRead
});
@override
_ConversationListState createState() => _ConversationListState();
}
class _ConversationListState extends State<ConversationList> {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: (){},
child: Container(
padding: EdgeInsets.only(left: 16 ,right: 16 ,top: 10 ,bottom: 10 ),
child: Row(
children: <Widget>[
Expanded(
child: Row(
children: <Widget>[
CircleAvatar(
backgroundImage: NetworkImage(widget.imageUrl),
maxRadius: 30,
),
SizedBox(width: 16),
Expanded(
child: Container(
color: Colors.transparent,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(widget.name, style: TextStyle(fontSize: 16)),
SizedBox(height: 6),
Text(
widget.messageText,
style: TextStyle(
fontSize: 13,
color: Colors.grey.shade600,
fontWeight: widget.isMessageRead ? FontWeight.bold : FontWeight.normal
),
),
],
),
),
),
],
),
),
Text(
widget.time,
style: TextStyle(
fontSize: 12,
fontWeight: widget.isMessageRead ? FontWeight.bold : FontWeight.normal
),
),
],
),
),
);
}
}
هذا المكوّن يستقبل القيم التالية:
- اسم المستخدم
- نص الرسالة الأخيرة
- رابط الصورة
- وقت الرسالة
- حالة قراءة الرسالة
عرض قائمة المحادثات باستخدام ListView
الآن يمكننا استخدام ListView.builder لعرض العناصر داخل chatPage.dart:
ListView.builder(
itemCount: chatUsers.length,
shrinkWrap: true,
padding: EdgeInsets.only(top: 16),
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return ConversationList(
name: chatUsers[index].name,
messageText: chatUsers[index].messageText,
imageUrl: chatUsers[index].imageURL,
time: chatUsers[index].time,
isMessageRead: (index == 0 || index == 3) ? true : false,
);
},
),
احرص على وضع هذا المكوّن داخل Column ضمن شاشة chatPage حتى تظهر القائمة أسفل شريط البحث مباشرة.

إنشاء شاشة تفاصيل المحادثة
بعد الانتهاء من شاشة القائمة، ننتقل إلى شاشة تفاصيل الرسائل. أنشئ ملفاً جديداً باسم chatDetailPage.dart داخل ./lib/screens:
import 'package:flutter/material.dart' ;
class ChatDetailPage extends StatefulWidget {
@override
_ChatDetailPageState createState() => _ChatDetailPageState();
}
class _ChatDetailPageState extends State<ChatDetailPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Chat Detail"),
),
body: Container()
);
}
}
هذه الشاشة ستكون الأساس الذي سنبني عليه واجهة الرسائل لاحقاً.
إضافة التنقل إلى شاشة تفاصيل المحادثة
في ملف conversationList.dart، أضف عملية التنقل داخل الحدث onTap في GestureDetector:
GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ChatDetailPage();
}));
},
بهذا، عند الضغط على أي محادثة، سينتقل المستخدم إلى شاشة التفاصيل.

تصميم شريط علوي مخصص لشاشة المحادثة
بدلاً من استخدام شريط افتراضي بسيط، يمكننا إنشاء AppBar مخصص يعرض صورة المستخدم وحالته:
return Scaffold(
appBar: AppBar(
elevation: 0,
automaticallyImplyLeading: false,
backgroundColor: Colors.white,
flexibleSpace: SafeArea(
child: Container(
padding: EdgeInsets.only(right: 16),
child: Row(
children: <Widget>[
IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Icon(Icons.arrow_back, color: Colors.black),
),
SizedBox(width: 2),
CircleAvatar(
backgroundImage: NetworkImage("https://randomuser.me/api/portraits/men/5.jpg"),
maxRadius: 20,
),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Kriss Benwat",
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
SizedBox(height: 6),
Text(
"Online",
style: TextStyle(color: Colors.grey.shade600, fontSize: 13),
),
],
),
),
Icon(Icons.settings, color: Colors.black54),
],
),
),
),
),
body: Container()
);

إضافة صندوق كتابة الرسائل في الأسفل
أي تطبيق دردشة يحتاج إلى منطقة كتابة واضحة وثابتة أسفل الشاشة. يمكن تنفيذ ذلك باستخدام Stack مع Align:
body: Stack(
children: <Widget>[
Align(
alignment: Alignment.bottomLeft,
child: Container(
padding: EdgeInsets.only(left: 10 ,bottom: 10 ,top: 10 ),
height: 60,
width: double.infinity,
color: Colors.white,
child: Row(
children: <Widget>[
GestureDetector(
onTap: () {},
child: Container(
height: 30,
width: 30,
decoration: BoxDecoration(
color: Colors.lightBlue,
borderRadius: BorderRadius.circular(30),
),
child: Icon(Icons.add, color: Colors.white, size: 20),
),
),
SizedBox(width: 15),
Expanded(
child: TextField(
decoration: InputDecoration(
hintText: "Write message...",
hintStyle: TextStyle(color: Colors.black54),
border: InputBorder.none
),
),
),
SizedBox(width: 15),
FloatingActionButton(
onPressed: () {},
child: Icon(Icons.send, color: Colors.white, size: 18),
backgroundColor: Colors.blue,
elevation: 0,
),
],
),
),
),
],
),

إنشاء نموذج بيانات الرسائل
لعرض الرسائل بشكل صحيح، نحتاج إلى نموذج مستقل يمثل الرسالة. أنشئ ملفاً باسم chatMessageModel.dart داخل ./models:
import 'package:flutter/cupertino.dart' ;
class ChatMessage {
String messageContent;
String messageType;
ChatMessage({
@required this.messageContent,
@required this.messageType
});
}
هذا النموذج يحدد:
- محتوى الرسالة
- نوعها:
senderأوreceiver
إعداد قائمة الرسائل داخل شاشة التفاصيل
داخل ملف chatDetailPage.dart، أضف قائمة تجريبية للرسائل:
List<ChatMessage> messages = [
ChatMessage(messageContent: "Hello, Will", messageType: "receiver"),
ChatMessage(messageContent: "How have you been?", messageType: "receiver"),
ChatMessage(messageContent: "Hey Kriss, I am doing fine dude. wbu?", messageType: "sender"),
ChatMessage(messageContent: "ehhhh, doing OK.", messageType: "receiver"),
ChatMessage(messageContent: "Is there any thing wrong?", messageType: "sender"),
];
ثم استخدم ListView.builder لعرض الرسائل أعلى صندوق الكتابة:
body: Stack(
children: <Widget>[
ListView.builder(
itemCount: messages.length,
shrinkWrap: true,
padding: EdgeInsets.only(top: 10 ,bottom: 10 ),
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.only(left: 16 ,right: 16 ,top: 10 ,bottom: 10 ),
child: Text(messages[index].messageContent),
);
},
),
Align(

تنسيق فقاعات الرسائل حسب المرسل والمستقبل
الآن نمنح الرسائل مظهراً احترافياً يشبه تطبيقات الدردشة الحقيقية، مع تغيير المحاذاة واللون حسب نوع الرسالة:
ListView.builder(
itemCount: messages.length,
shrinkWrap: true,
padding: EdgeInsets.only(top: 10 ,bottom: 10 ),
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.only(left: 14 ,right: 14 ,top: 10 ,bottom: 10 ),
child: Align(
alignment: (messages[index].messageType == "receiver" ? Alignment.topLeft : Alignment.topRight),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: (messages[index].messageType == "receiver" ? Colors.grey.shade200 : Colors.blue[200]),
),
padding: EdgeInsets.all(16),
child: Text(
messages[index].messageContent,
style: TextStyle(fontSize: 15),
),
),
),
);
},
),
بهذا الشكل:
- تظهر الرسائل المستلمة على اليسار
- تظهر الرسائل المرسلة على اليمين
- يتغير لون الفقاعة لتسهيل التمييز البصري

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

أفضل ممارسات لتحسين واجهة تطبيق الدردشة
1. افصل المكوّنات عن الشاشات
إنشاء ملفات مستقلة مثل conversationList.dart يساعدك على تنظيم المشروع وتسهيل الصيانة.
2. استخدم النماذج البرمجية
وجود نماذج مثل ChatUsers وChatMessage يجعل ربط البيانات لاحقاً مع API أو قاعدة البيانات أكثر وضوحاً.
3. راعِ الأداء في القوائم
استخدام ListView.builder أفضل من إنشاء العناصر يدوياً، لأنه يحمّل العناصر عند الحاجة فقط.
4. اجعل الواجهة قابلة للتطوير
هذه الواجهة تمثل أساساً ممتازاً لإضافة مزايا لاحقاً مثل الإشعارات، رفع الملفات، المكالمات الصوتية، أو الرسائل الصوتية.
الخلاصة التقنية
بناء واجهة تطبيق دردشة باستخدام Flutter وDart يمنحك مرونة كبيرة في تصميم تجربة استخدام حديثة تعمل بكفاءة على أكثر من منصة. هذا النوع من المشاريع مناسب جداً لفهم بنية الواجهات، وإدارة المكوّنات، وتنظيم الملفات، قبل الانتقال إلى المراحل الأعمق مثل إدارة الحالة وربط الخدمات الخلفية. تقنياً، يُظهر هذا المشروع قوة Flutter في إنتاج واجهات دقيقة وسلسة بزمن تطوير قصير نسبياً، وهو ما يجعله خياراً ممتازاً لتطبيقات التراسل الحديثة.