عناصر الرسوم الضمنية: AnimatedOpacity و AnimatedAlign و AnimatedSwitcher
عناصر الرسوم الضمنية: AnimatedOpacity و AnimatedAlign و AnimatedSwitcher
يأتي Flutter مزوّداً بمجموعة من عناصر الرسوم المتحركة الضمنية التي تتولى كل منطق الرسوم المتحركة داخلياً. تضع قيمةً جديدة، وتحدد duration ومنحنى اختياري curve، فيتكفّل Flutter بالانتقال السلس بين القيمة القديمة والجديدة. في هذا الدرس نركّز على ثلاثة أعضاء بارزين من تلك المجموعة: AnimatedOpacity وAnimatedAlign وAnimatedSwitcher.
AnimatedOpacity
يُغلّف AnimatedOpacity أيّ عنصر ويُحرّك شفافيته بين 0.0 (غير مرئي كلياً) و1.0 (معتم كلياً). وهو الأسلوب الأمثل لـتلاشي العناصر ظهوراً واختفاءً دون كتابة سطر واحد من كود متحكّم الرسوم المتحركة.
- opacity — قيمة الشفافية المستهدفة؛ يبدأ الرسم المتحرك كلما تغيّرت.
- duration — مدة الانتقال (مثل
Duration(milliseconds: 500)). - curve — منحنى التسهيل؛ الافتراضي
Curves.linear. - onEnd — رد نداء اختياري يُطلَق عند اكتمال الرسم المتحرك.
opacity: 0.0) لا يزال يشغل مساحة في التخطيط. استخدم Visibility أو التصيير الشرطي إذا كنت تريد إزالة العنصر من التخطيط أيضاً.AnimatedOpacity — تلاشٍ ظهوراً واختفاءً عند الضغط على زر
class FadeDemo extends StatefulWidget {
const FadeDemo({super.key});
@override
State<FadeDemo> createState() => _FadeDemoState();
}
class _FadeDemoState extends State<FadeDemo> {
double _opacity = 1.0;
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedOpacity(
opacity: _opacity,
duration: const Duration(milliseconds: 600),
curve: Curves.easeInOut,
child: Container(
width: 200,
height: 200,
color: Colors.deepPurple,
child: const Center(
child: Text(
'Flutter',
style: TextStyle(color: Colors.white, fontSize: 28),
),
),
),
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () {
setState(() {
_opacity = _opacity == 1.0 ? 0.0 : 1.0;
});
},
child: const Text('تبديل الظهور'),
),
],
);
}
}
AnimatedAlign
يُحرّك AnimatedAlign موضع عنصره الفرعي داخل العنصر الأب عبر إيجاد قيمة وسيطة لـAlignment. تشمل حالات الاستخدام الشائعة: إبراز لوحة من الحافة، وإبراز مؤشر التبويب النشط، وتحريك زر الإجراء العائم إلى زاوية جديدة.
- alignment — قيمة
Alignmentالمستهدفة؛ يبدأ الرسم المتحرك عند تغييرها. - duration وcurve — نفس دلالاتهما في جميع العناصر الضمنية.
- widthFactor / heightFactor — عوامل اختيارية لتحجيم العنصر نسبةً إلى عنصره الفرعي.
AnimatedOpacity وAnimatedAlign حول نفس العنصر الفرعي لإنشاء تأثير دخول "انزلاق وتلاشٍ" دون أي كود إضافي.AnimatedAlign — الانزلاق بين الزوايا
class AlignDemo extends StatefulWidget {
const AlignDemo({super.key});
@override
State<AlignDemo> createState() => _AlignDemoState();
}
class _AlignDemoState extends State<AlignDemo> {
Alignment _alignment = Alignment.topLeft;
static const List<Alignment> _positions = [
Alignment.topLeft,
Alignment.topRight,
Alignment.bottomRight,
Alignment.bottomLeft,
];
int _index = 0;
void _next() {
setState(() {
_index = (_index + 1) % _positions.length;
_alignment = _positions[_index];
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(
width: 300,
height: 300,
child: AnimatedAlign(
alignment: _alignment,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOutCubic,
child: Container(
width: 60,
height: 60,
decoration: const BoxDecoration(
color: Colors.teal,
shape: BoxShape.circle,
),
),
),
),
ElevatedButton(
onPressed: _next,
child: const Text('تحريك'),
),
],
);
}
}
AnimatedSwitcher
يُجري AnimatedSwitcher تلاشياً متقاطعاً (أو أي انتقال مخصص) بين عنصرَين فرعيَّين مختلفَين. كلما عُيِّن للخاصية child عنصرٌ ذو مفتاح مختلف، يؤدي العنصر القديم رسم خروج بينما يؤدي الجديد رسم دخول في الوقت ذاته.
- duration — مدة انتقال العنصر الفرعي الداخل.
- reverseDuration — مدة منفصلة اختيارية للعنصر الفرعي الخارج.
- transitionBuilder — مصنع يُغلّف العنصر الفرعي في عنصر متحرك مخصص (الافتراضي:
FadeTransition). - layoutBuilder — يتحكم في طريقة تكديس العنصرين القديم والجديد أثناء الانتقال.
Key صريح لهما، يعدّهما Flutter نفس العنصر ولا يطلق الانتقال. دائماً عيّن ValueKey يتغير مع المحتوى.AnimatedSwitcher — تلاشٍ متقاطع بين عداد وأيقونة
class SwitcherDemo extends StatefulWidget {
const SwitcherDemo({super.key});
@override
State<SwitcherDemo> createState() => _SwitcherDemoState();
}
class _SwitcherDemoState extends State<SwitcherDemo> {
int _count = 0;
bool _showIcon = false;
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedSwitcher(
duration: const Duration(milliseconds: 400),
transitionBuilder: (child, animation) {
return FadeTransition(
opacity: animation,
child: ScaleTransition(scale: animation, child: child),
);
},
child: _showIcon
? const Icon(
Icons.check_circle,
key: ValueKey('icon'),
size: 80,
color: Colors.green,
)
: Text(
'$_count',
key: ValueKey(_count),
style: const TextStyle(fontSize: 72, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => setState(() => _count++),
child: const Text('زيادة'),
),
const SizedBox(width: 12),
ElevatedButton(
onPressed: () => setState(() => _showIcon = !_showIcon),
child: const Text('تبديل العرض'),
),
],
),
],
);
}
}
اختيار العنصر المناسب
- استخدم
AnimatedOpacityعندما تريد إخفاء وإظهار عنصر دون تحريكه. - استخدم
AnimatedAlignعندما تريد إنزلاق عنصر إلى موضع جديد داخل أبيه. - استخدم
AnimatedSwitcherعندما تريد استبدال عنصر بآخر مع تأثير انتقالي.
ملخص
تتبع العناصر الثلاثة نفس عقد الرسوم الضمنية: ضع قيمة خاصية جديدة داخل setState()، وفّر duration، وتكفّل Flutter بالباقي. يتحكّم AnimatedOpacity في الشفافية، ويتحكّم AnimatedAlign في الموضع القائم على المحاذاة، ويُحوّل AnimatedSwitcher بين عناصر فرعية مختلفة كلياً. إتقان هذه الثلاثة يغطي الغالبية العظمى من احتياجات حركة واجهة المستخدم اليومية دون الحاجة إلى واجهات AnimationController منخفضة المستوى.