physics simulation Animation physics simulation Animation

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

في هذا المقال، سنستعرض خطوة بخطوة كيفية تنفيذ مثل هذه الرسوم في Flutter، بالاعتماد على دليل Flutter الرسمي (Cookbook). سواء كنت مطورًا مبتدئًا أو محترفًا، ستجد هنا أمثلة كود عملية ونصائح لتحقيق أفضل النتائج، مع التركيز على تحسين الأداء وتجربة المستخدم.


ما هي الرسوم المتحركة القائمة على الفيزياء؟

ببساطة:

  • الرسوم التقليدية في Flutter (Tween Animations) تعتمد على الانتقال من قيمة إلى أخرى خلال فترة زمنية.
  • أما الرسوم الفيزيائية (Physics-based animations) فهي تحاكي الحركة الطبيعية مثل الزنبرك أو الجاذبية.

في Flutter، يمكننا استخدام مكتبة:

import 'package:flutter/physics.dart';

لإنشاء محاكاة فيزيائية تجعل العناصر تستجيب لإيماءات المستخدم بطريقة طبيعية ومقنعة.


لماذا نحتاجها؟

  • تضيف إحساسًا بالحيوية للتطبيق.
  • تجعل التفاعل يبدو وكأنه يحدث في العالم الحقيقي.
  • مثال: عند سحب بطاقة وإفلاتها، تعود بحركة سلسة تشبه حركة نابض، مما يقلل من الإحساس بالجمود أو “الآلية”.

خطوات التنفيذ

سنقسم التنفيذ إلى أربع مراحل، مع أمثلة كود عملية:


1. إعداد متحكم الرسوم المتحركة (AnimationController)

أول خطوة: إنشاء StatefulWidget مع متحكم AnimationController.

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(home: PhysicsCardDragDemo()));
}

class PhysicsCardDragDemo extends StatelessWidget {
  const PhysicsCardDragDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: const DraggableCard(child: FlutterLogo(size: 128)),
    );
  }
}

class DraggableCard extends StatefulWidget {
  const DraggableCard({required this.child, super.key});

  final Widget child;

  @override
  State<DraggableCard> createState() => _DraggableCardState();
}

class _DraggableCardState extends State<DraggableCard> with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 1),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Align(child: Card(child: widget.child));
  }
}

2. إضافة إيماءات السحب (Gestures)

الآن نريد أن يتغير موضع البطاقة عند السحب:

class _DraggableCardState extends State<DraggableCard> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  Alignment _dragAlignment = Alignment.center;

  @override
  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size;
    return GestureDetector(
      onPanUpdate: (details) {
        setState(() {
          _dragAlignment += Alignment(
            details.delta.dx / (size.width / 2),
            details.delta.dy / (size.height / 2),
          );
        });
      },
      child: Align(
        alignment: _dragAlignment,
        child: Card(child: widget.child),
      ),
    );
  }
}

الآن يمكن سحب البطاقة في أي مكان.


3. إرجاع البطاقة إلى المركز (Animation with Tween)

بعد إفلات البطاقة، نريدها أن تعود لمركز الشاشة:

void _runAnimation() {
  _animation = _controller.drive(
    AlignmentTween(
      begin: _dragAlignment,
      end: Alignment.center,
    ),
  );
  _controller.reset();
  _controller.forward();
}

ثم نربطها بـ onPanEnd:

onPanEnd: (details) {
  _runAnimation();
},

4. استخدام محاكاة زنبركية (Spring Simulation)

الآن الجزء الممتع! نستبدل الحركة الخطية بمحاكاة زنبركية طبيعية:

import 'package:flutter/physics.dart';

void _runAnimation(Offset pixelsPerSecond, Size size) {
  _animation = _controller.drive(
    AlignmentTween(
      begin: _dragAlignment,
      end: Alignment.center,
    ),
  );

  final unitsPerSecondX = pixelsPerSecond.dx / size.width;
  final unitsPerSecondY = pixelsPerSecond.dy / size.height;
  final unitVelocity = Offset(unitsPerSecondX, unitsPerSecondY).distance;

  const spring = SpringDescription(mass: 30, stiffness: 1, damping: 1);
  final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);

  _controller.animateWith(simulation);
}

وهكذا، تصبح الحركة طبيعية: البطاقة تعود للمركز وكأنها مربوطة بزنبرك!


نصائح لتحسين الأداء

  • استخدم vsync لتوفير الطاقة.
  • جرّب معاملات مختلفة لـ SpringDescription للحصول على حركة أنسب.
  • اختبر على أجهزة متنوعة لضمان سلاسة التجربة.
  • يمكنك الدمج مع Hero Animations لانتقالات أكثر واقعية بين الشاشات.

الخاتمة

الرسوم الفيزيائية في Flutter قادرة على تحويل واجهات تطبيقاتك إلى تجربة تفاعلية واقعية.
من خلال استخدام AnimationController و SpringSimulation، يمكنك بناء حركات تشعر المستخدم وكأنها تحدث في العالم الحقيقي.

جرب الكود بنفسك، وستلاحظ الفرق الكبير في تفاعل المستخدم مع تطبيقك.


By احمد علي

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

اترك تعليقاً

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