تخطَّ إلى المحتوى

تزيين الصناديق في Flutter: BoxDecoration والظلال والحدود

الدرس 10 من 33 · Flutter

انظر إلى أي تطبيق جميل: بطاقات بزوايا ناعمة، وظلال خفيفة، وأزرار دائرية، وخلفيات متدرجة. كل هذا في Flutter يخرج من مكان واحد: الخاصية decoration في Container. في هذا الدرس ستتعلم كيف تحوّل مستطيلاً عادياً إلى عنصر أنيق.

مراجعة سريعة: طبقات Container

Section titled “مراجعة سريعة: طبقات Container”

Container يجمع عدة وظائف في Widget واحد. تخيّله مثل إطار صورة على الحائط:

  • margin: المسافة بين الإطار وما حوله على الحائط.
  • decoration: شكل الإطار نفسه: لونه وحدوده وظله.
  • padding: المسافة بين حافة الإطار والصورة التي بداخله.
  • child: الصورة.
Container(
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
decoration: BoxDecoration(
color: Colors.indigo.shade50,
borderRadius: BorderRadius.circular(12),
),
child: const Text('صندوق بزوايا دائرية'),
)

الزوايا الدائرية: borderRadius

Section titled “الزوايا الدائرية: borderRadius”
BoxDecoration(
color: Colors.amber,
borderRadius: BorderRadius.circular(16),
)
  • BorderRadius.circular(16): كل الزوايا بنفس المقدار.
  • BorderRadius.vertical(top: Radius.circular(24)): الزاويتان العلويتان فقط، وهو شكل مناسب للوحات التي تصعد من أسفل الشاشة.
  • BorderRadius.only(topLeft: Radius.circular(20)): زاوية واحدة تحددها أنت.
BoxDecoration(
border: Border.all(color: Colors.indigo, width: 2),
borderRadius: BorderRadius.circular(12),
)

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

const BoxDecoration(
border: Border(
bottom: BorderSide(color: Colors.teal, width: 4),
),
)

الظل يعطي إحساساً بأن الصندوق مرتفع عن الخلفية:

BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.12),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
)
  • color: لون الظل. نستخدم الأسود مع شفافية عالية، والدالة withValues(alpha: 0.12) تعني أن اللون ظاهر بنسبة 12% فقط.
  • blurRadius: مقدار التمويه. كلما زاد أصبح الظل أنعم وأوسع.
  • offset: إزاحة الظل. Offset(0, 4) تعني: بدون إزاحة أفقية، و 4 بكسلات للأسفل، كأن الضوء قادم من الأعلى.
  • spreadRadius: خاصية اختيارية توسّع الظل قبل تمويهه.

بدلاً من لون واحد، يمكنك تدريج لونين أو أكثر:

BoxDecoration(
borderRadius: BorderRadius.circular(16),
gradient: const LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [Colors.indigo, Colors.purple],
),
)

begin و end يحددان اتجاه التدرج، و colors قائمة الألوان بالترتيب. ويوجد أيضاً RadialGradient لتدرج دائري يبدأ من المركز.

لصنع دائرة كاملة، مثل صورة شخصية أو مؤشر حالة:

Container(
width: 56,
height: 56,
decoration: const BoxDecoration(
color: Colors.teal,
shape: BoxShape.circle,
),
child: const Icon(Icons.check, color: Colors.white),
)

مع BoxShape.circle لا تستخدم borderRadius، فالدائرة لا زوايا لها. واجعل العرض والارتفاع متساويين.

width و height يحددان حجماً ثابتاً. لكن أحياناً تريد أن تقول: «خذ الحجم الذي يناسب محتواك، لكن لا تزد على 300». هذه وظيفة constraints:

Container(
constraints: const BoxConstraints(maxWidth: 300, minHeight: 80),
color: Colors.amber.shade100,
child: const Text('لن يزيد عرضي على 300'),
)

هذا مفيد جداً في الشاشات الكبيرة: بطاقة تمتد بعرض هاتف صغير، لكنها لا تتمدد بشكل قبيح على تابلت.

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

ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image.asset('assets/images/cover.png'),
)

ClipRRect تعني «قص على شكل مستطيل دائري الزوايا». وأخواتها: ClipOval للقص الدائري. ويمكنك أيضاً إضافة clipBehavior: Clip.antiAlias إلى Container نفسه ليقص محتواه بحسب شكل زخرفته.

لنبنِ بطاقة تشبه بطاقات المحافظ الإلكترونية: خلفية متدرجة، وزوايا دائرية، وظل، ودائرة صغيرة للأيقونة.

lib/balance_card.dart
class BalanceCard extends StatelessWidget {
const BalanceCard({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('محفظتي')),
body: Center(
child: Container(
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(20),
constraints: const BoxConstraints(maxWidth: 360),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: const LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [Colors.indigo, Colors.deepPurple],
),
boxShadow: [
BoxShadow(
color: Colors.indigo.withValues(alpha: 0.3),
blurRadius: 16,
offset: const Offset(0, 8),
),
],
),
child: Row(
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.2),
shape: BoxShape.circle,
),
child: const Icon(Icons.wallet, color: Colors.white),
),
const SizedBox(width: 16),
const Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text('الرصيد الحالي', style: TextStyle(color: Colors.white70)),
SizedBox(height: 4),
Text(
'1,250 جنيهاً',
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
),
),
);
}
}

لاحظ ثلاثة أشياء:

  1. ظل البطاقة أخذ لون البطاقة نفسه بشفافية 30% بدلاً من الأسود، وهذه حيلة تجعل الظل يبدو كأنه توهج ملون.
  2. الدائرة البيضاء شبه الشفافة (alpha: 0.2) تظهر فوق التدرج بلون أفتح منه، فلا نحتاج إلى اختيار لون جديد.
  3. العمود ملفوف بـ Expanded ليأخذ العرض المتبقي بجانب الدائرة، فإذا كان الرقم طويلاً ينزل لسطر جديد بدلاً من أن يخرج عن حدود البطاقة.
  1. شغّل بطاقة الرصيد، ثم غيّر ألوان التدرج واتجاهه.
  2. أضف حداً أبيض رفيعاً شبه شفاف حول البطاقة بـ Border.all.
  3. اصنع ثلاث دوائر صغيرة ملونة في Row كمؤشرات حالة: أخضر وأصفر وأحمر.
  4. تحدٍّ: اصنع بطاقة لها خط ملون عريض على حافتها اليمنى فقط، وبقية الحواف بلا حدود.
  • وضع color و decoration معاً في Container: خطأ عند التشغيل. انقل اللون إلى داخل BoxDecoration.
  • استخدام borderRadius مع BoxShape.circle: يسبب خطأ، فالدائرة لا تقبل زوايا.
  • توقع أن borderRadius ستقص الصورة: الزخرفة لا تقص المحتوى. استخدم ClipRRect أو clipBehavior.

سؤال

أين تضع لون الخلفية عندما تستخدم decoration في Container؟

سؤال

وضعت صورة داخل Container له borderRadius، لكن زوايا الصورة بقيت حادة. ما الحل؟

  • BoxDecoration تجمع اللون والزوايا والحدود والظلال والتدرج والشكل.
  • BorderRadius.circular للزوايا، و Border.all للحدود، و BoxShadow للظلال الهادئة.
  • LinearGradient للخلفيات المتدرجة، و BoxShape.circle للدوائر.
  • constraints تضع حداً أدنى وأقصى للحجم بدلاً من حجم ثابت.
  • ClipRRect و ClipOval لقص المحتوى نفسه، وليس الخلفية فقط.