تحريك التطبيقات برفرفة
نشرت: 2022-03-10يتم الإشادة بالتطبيقات الخاصة بأي نظام أساسي عندما تكون بديهية وحسنة المظهر وتقدم تعليقات ممتعة لتفاعلات المستخدم. الرسوم المتحركة هي إحدى الطرق للقيام بذلك.
لقد نضج Flutter ، وهو إطار عمل متعدد المنصات ، في العامين الماضيين ليشمل دعم الويب وسطح المكتب. لقد اكتسب سمعة أن التطبيقات التي تم تطويرها معه سلسة وحسن المظهر. من خلال دعم الرسوم المتحركة الغني ، والطريقة التصريحية لكتابة واجهة المستخدم ، و "إعادة التحميل السريع" ، وغيرها من الميزات ، أصبح الآن إطار عمل كامل عبر الأنظمة الأساسية.
إذا كنت تبدأ مع Flutter وترغب في تعلم طريقة غير تقليدية لإضافة الرسوم المتحركة ، فأنت في المكان الصحيح: سوف نستكشف عالم الرسوم المتحركة وأدوات الحركة ، وهي طريقة ضمنية لإضافة الرسوم المتحركة.
يعتمد Flutter على مفهوم الحاجيات. كل مكون مرئي من التطبيق عبارة عن عنصر واجهة مستخدم - فكر فيها كمشاهد في Android. يوفر Flutter دعمًا للرسوم المتحركة باستخدام فئة الرسوم المتحركة وكائن "AnimationController" للإدارة و "Tween" لاستيفاء نطاق البيانات. تعمل هذه المكونات الثلاثة معًا لتوفير رسوم متحركة سلسة. نظرًا لأن هذا يتطلب إنشاء الرسوم المتحركة وإدارتها يدويًا ، فإنها تُعرف بأنها طريقة واضحة للرسوم المتحركة.
الآن اسمحوا لي أن أقدم لكم أدوات الرسوم المتحركة والحركة. يوفر Flutter العديد من الأدوات التي تدعم الرسوم المتحركة بطبيعتها. ليست هناك حاجة لإنشاء كائن متحرك أو أي وحدة تحكم ، حيث يتم التعامل مع جميع الرسوم المتحركة بواسطة هذه الفئة من الأدوات. ما عليك سوى اختيار الأداة المناسبة للرسوم المتحركة المطلوبة وتمرير قيم خصائص عنصر واجهة المستخدم لتحريكها. هذه التقنية هي طريقة ضمنية للرسوم المتحركة.
يحدد الرسم البياني أعلاه تقريبًا التسلسل الهرمي للرسوم المتحركة في Flutter ، كيف يتم دعم الرسوم المتحركة الصريحة والضمنية.
بعض أدوات الرسوم المتحركة التي تتناولها هذه المقالة هي:
-
AnimatedOpacity
-
AnimatedCrossFade
-
AnimatedAlign
-
AnimatedPadding
-
AnimatedSize
-
AnimatedPositioned
لا يوفر Flutter عناصر واجهة مستخدم متحركة محددة مسبقًا فحسب ، بل يوفر أيضًا عنصر واجهة مستخدم عام يسمى AnimatedWidget
، والذي يمكن استخدامه لإنشاء عناصر واجهة مستخدم متحركة ضمنية مخصصة. كما يتضح من الاسم ، تنتمي هذه الأدوات المصغّرة إلى فئة عناصر واجهة المستخدم الرسومية والحركة ، وبالتالي فهي تمتلك بعض الخصائص المشتركة التي تسمح لنا بجعل الرسوم المتحركة أكثر سلاسة وأفضل مظهرًا.
اسمحوا لي أن أشرح هذه الخصائص المشتركة الآن ، حيث سيتم استخدامها لاحقًا في جميع الأمثلة.
-
duration
المدة التي سيتم خلالها تحريك المعلمات. -
reverseDuration
مدة الحركة العكسية. -
curve
المنحنى المراد تطبيقه عند تحريك المعلمات. يمكن الحصول على القيم المقحمة من التوزيع الخطي أو ، إذا تم تحديدها ، يمكن أخذها من منحنى.
لنبدأ الرحلة بإنشاء تطبيق بسيط نسميه "مقتبس". سيعرض عرض أسعار عشوائي في كل مرة يبدأ فيها التطبيق. هناك شيئان يجب ملاحظتهما: أولاً ، سيتم ترميز كل هذه الاقتباسات في التطبيق ؛ وثانيًا ، لن يتم حفظ أي بيانات للمستخدم.
ملاحظة : يمكن العثور على جميع ملفات هذه الأمثلة على GitHub.
ابدء
يجب تثبيت Flutter وستحتاج إلى بعض الإلمام بالتدفق الأساسي قبل الانتقال. أفضل مكان للبدء هو "استخدام Flutter من Google لتطوير الأجهزة المحمولة عبر الأنظمة الأساسية حقًا".
أنشئ مشروع Flutter جديدًا في Android Studio.
سيؤدي هذا إلى فتح معالج مشروع جديد ، حيث يمكنك تكوين أساسيات المشروع.
في شاشة اختيار نوع المشروع ، توجد أنواع مختلفة من مشاريع Flutter ، كل منها يلبي سيناريو معين .. في هذا البرنامج التعليمي ، اختر Flutter Application واضغط على التالي .
أنت الآن بحاجة إلى إدخال بعض المعلومات الخاصة بالمشروع: اسم المشروع والمسار ومجال الشركة وما إلى ذلك. الق نظرة على الصورة أدناه.
أضف اسم المشروع ومسار Flutter SDK وموقع المشروع ووصفًا اختياريًا للمشروع. اضغط على التالي .
يتطلب كل تطبيق (سواء كان Android أو iOS) اسم حزمة فريدًا. عادة ، يمكنك استخدام عكس مجال موقع الويب الخاص بك ؛ على سبيل المثال ، com.google أو com.yahoo. اضغط على " إنهاء " لإنشاء تطبيق Flutter يعمل.
بمجرد إنشاء المشروع ، سترى الشاشة الموضحة أعلاه. افتح ملف main.dart (المميز في لقطة الشاشة). هذا هو ملف التطبيق الرئيسي. نموذج المشروع مكتمل في حد ذاته ، ويمكن تشغيله مباشرة على محاكي أو جهاز مادي دون أي تعديل.
استبدل محتوى ملف main.dart بمقتطف الشفرة التالي:
import 'package:animated_widgets/FirstPage.dart'; import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Animated Widgets', debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, accentColor: Colors.redAccent, ), home: FirstPage(), ); } }
يقوم هذا الرمز بتنظيف ملف main.dart فقط عن طريق إضافة معلومات بسيطة ذات صلة بإنشاء تطبيق جديد. تقوم فئة MyApp بإرجاع كائن: عنصر واجهة مستخدم MaterialApp
، والذي يوفر الهيكل الأساسي لإنشاء تطبيقات تتوافق مع التصميم متعدد الأبعاد. لجعل الكود أكثر تنظيمًا ، أنشئ ملفي dart جديدين داخل مجلد lib : FirstPage.dart و Quotes.dart .
سيحتوي FirstPage.dart على جميع التعليمات البرمجية المسؤولة عن جميع العناصر المرئية (الحاجيات) المطلوبة لتطبيقنا المسعور. يتم التعامل مع جميع الرسوم المتحركة في هذا الملف.
ملاحظة : لاحقًا في المقالة ، تتم إضافة جميع مقتطفات التعليمات البرمجية لكل عنصر واجهة مستخدم متحرك إلى هذا الملف كأبناء عنصر واجهة مستخدم Scaffold. لمزيد من المعلومات ، قد يكون هذا المثال على GitHub مفيدًا.
ابدأ بإضافة التعليمات البرمجية التالية إلى FirstPage.dart . هذا هو الكود الجزئي حيث ستتم إضافة أشياء أخرى لاحقًا.
import 'dart:math'; import 'package:animated_widgets/Quotes.dart'; import 'package:flutter/material.dart'; class FirstPage extends StatefulWidget { @override State createState() { return FirstPageState(); } } class FirstPageState extends State with TickerProviderStateMixin { bool showNextButton = false; bool showNameLabel = false; bool alignTop = false; bool increaseLeftPadding = false; bool showGreetings = false; bool showQuoteCard = false; String name = ''; double screenWidth; double screenHeight; String quote; @override void initState() { super.initState(); Random random = new Random(); int quoteIndex = random.nextInt(Quotes.quotesArray.length); quote = Quotes.quotesArray[quoteIndex]; } @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; screenHeight = MediaQuery.of(context).size.height; return Scaffold( appBar: _getAppBar(), body: Stack( children: [ // All other children will be added here. // In this article, all the children widgets are contained // in their own separate methods. // Just method calls should be added here for the respective child. ], ), ); } }
import 'dart:math'; import 'package:animated_widgets/Quotes.dart'; import 'package:flutter/material.dart'; class FirstPage extends StatefulWidget { @override State createState() { return FirstPageState(); } } class FirstPageState extends State with TickerProviderStateMixin { bool showNextButton = false; bool showNameLabel = false; bool alignTop = false; bool increaseLeftPadding = false; bool showGreetings = false; bool showQuoteCard = false; String name = ''; double screenWidth; double screenHeight; String quote; @override void initState() { super.initState(); Random random = new Random(); int quoteIndex = random.nextInt(Quotes.quotesArray.length); quote = Quotes.quotesArray[quoteIndex]; } @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; screenHeight = MediaQuery.of(context).size.height; return Scaffold( appBar: _getAppBar(), body: Stack( children: [ // All other children will be added here. // In this article, all the children widgets are contained // in their own separate methods. // Just method calls should be added here for the respective child. ], ), ); } }
import 'dart:math'; import 'package:animated_widgets/Quotes.dart'; import 'package:flutter/material.dart'; class FirstPage extends StatefulWidget { @override State createState() { return FirstPageState(); } } class FirstPageState extends State with TickerProviderStateMixin { bool showNextButton = false; bool showNameLabel = false; bool alignTop = false; bool increaseLeftPadding = false; bool showGreetings = false; bool showQuoteCard = false; String name = ''; double screenWidth; double screenHeight; String quote; @override void initState() { super.initState(); Random random = new Random(); int quoteIndex = random.nextInt(Quotes.quotesArray.length); quote = Quotes.quotesArray[quoteIndex]; } @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; screenHeight = MediaQuery.of(context).size.height; return Scaffold( appBar: _getAppBar(), body: Stack( children: [ // All other children will be added here. // In this article, all the children widgets are contained // in their own separate methods. // Just method calls should be added here for the respective child. ], ), ); } }
import 'dart:math'; import 'package:animated_widgets/Quotes.dart'; import 'package:flutter/material.dart'; class FirstPage extends StatefulWidget { @override State createState() { return FirstPageState(); } } class FirstPageState extends State with TickerProviderStateMixin { bool showNextButton = false; bool showNameLabel = false; bool alignTop = false; bool increaseLeftPadding = false; bool showGreetings = false; bool showQuoteCard = false; String name = ''; double screenWidth; double screenHeight; String quote; @override void initState() { super.initState(); Random random = new Random(); int quoteIndex = random.nextInt(Quotes.quotesArray.length); quote = Quotes.quotesArray[quoteIndex]; } @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; screenHeight = MediaQuery.of(context).size.height; return Scaffold( appBar: _getAppBar(), body: Stack( children: [ // All other children will be added here. // In this article, all the children widgets are contained // in their own separate methods. // Just method calls should be added here for the respective child. ], ), ); } }
يحتوي ملف Quotes.dart على قائمة بكافة عروض الأسعار المشفرة. نقطة واحدة يجب ملاحظتها هنا هي أن القائمة هي كائن ثابت. هذا يعني أنه يمكن استخدامه في أماكن أخرى دون إنشاء كائن جديد لفئة الاقتباسات. يتم اختيار هذا حسب التصميم ، حيث تعمل القائمة أعلاه ببساطة كأداة مساعدة.
أضف الكود التالي إلى هذا الملف:
class Quotes { static const quotesArray = [ "Good, better, best. Never let it rest. 'Til your good is better and your better is best", "It does not matter how slowly you go as long as you do not stop.", "Only I can change my life. No one can do it for me." ]; }
أصبح الهيكل العظمي للمشروع جاهزًا الآن ، لذلك دعونا نوضح أكثر من ذلك بقليل.
AnimatedOpacity
لإضفاء لمسة شخصية على التطبيق ، سيكون من الجيد معرفة اسم المستخدم ، لذلك دعنا نطلبه ونعرض الزر التالي. حتى يقوم المستخدم بإدخال اسمه ، يكون هذا الزر مخفيًا ، وسيظهر برشاقة عند إعطاء الاسم. نحن بحاجة إلى نوع من الرسوم المتحركة للرؤية للزر ، ولكن هل هناك عنصر واجهة مستخدم لذلك؟ نعم هنالك.
أدخل AnimatedOpacity
. تعتمد هذه الأداة على عنصر واجهة مستخدم التعتيم عن طريق إضافة دعم الحركة الضمني. كيف نستخدمها؟ تذكر السيناريو الخاص بنا: نحتاج إلى إظهار الزر التالي مع رؤية متحركة. نحن نلف أداة الزر داخل عنصر واجهة AnimatedOpacity
، ونقوم بتغذية بعض القيم المناسبة وإضافة شرط لتشغيل الرسوم المتحركة - ويمكن لـ Flutter التعامل مع الباقي.
_getAnimatedOpacityButton() { return AnimatedOpacity( duration: Duration(seconds: 1), reverseDuration: Duration(seconds: 1), curve: Curves.easeInOut, opacity: showNextButton ? 1 : 0, child: _getButton(), ); }
تحتوي أداة AnimatedOpacity
على خاصيتين إلزاميتين:
-
opacity
تعني القيمة 1 أنه مرئي تمامًا ؛ 0 (صفر) تعني مخفي. أثناء الرسوم المتحركة ، يقحم Flutter القيم بين هذين النقيضين. يمكنك أن ترى كيف يتم وضع الشرط لتغيير الرؤية ، وبالتالي تشغيل الرسوم المتحركة. -
child
القطعة الفرعية التي ستظهر بها الرسوم المتحركة.
يجب أن تفهم الآن مدى بساطة إضافة الرسوم المتحركة للرؤية باستخدام عنصر واجهة المستخدم الضمني. وجميع هذه الأدوات تتبع نفس الإرشادات وهي سهلة الاستخدام. دعنا ننتقل إلى المرحلة التالية.
AnimatedCrossFade
لدينا اسم المستخدم ، لكن الأداة لا تزال تنتظر الإدخال. في الخطوة السابقة ، عندما يقوم المستخدم بإدخال اسمه ، نعرض الزر التالي. الآن ، عندما يضغط المستخدم على الزر ، أريد التوقف عن قبول الإدخال وإظهار الاسم الذي تم إدخاله. هناك العديد من الطرق للقيام بذلك ، بالطبع ، ولكن ربما يمكننا إخفاء عنصر واجهة الإدخال وإظهار عنصر واجهة مستخدم نص غير قابل للتعديل. لنجربها باستخدام عنصر واجهة المستخدم AnimatedCrossFade
.
تتطلب هذه الأداة طفلين ، حيث يتلاشى عنصر واجهة المستخدم بينهما بناءً على بعض الشروط. أحد الأشياء المهمة التي يجب مراعاتها أثناء استخدام هذه الأداة هو أن كلا الطفلين يجب أن يكونا بنفس العرض. إذا كان الارتفاع مختلفًا ، فسيتم قطع القطعة الأطول من الأسفل. في هذا السيناريو ، سيتم استخدام عنصرين من عناصر واجهة المستخدم كأطفال: الإدخال والتسمية.
_getAnimatedCrossfade() { return AnimatedCrossFade( duration: Duration(seconds: 1), alignment: Alignment.center, reverseDuration: Duration(seconds: 1), firstChild: _getNameInputWidget(), firstCurve: Curves.easeInOut, secondChild: _getNameLabelWidget(), secondCurve: Curves.easeInOut, crossFadeState: showNameLabel ? CrossFadeState.showSecond : CrossFadeState.showFirst, ); }
تتطلب هذه الأداة مجموعة مختلفة من المعلمات الإلزامية:
-
crossFadeState
هذه الحالة تحدد أي طفل يجب إظهاره. -
firstChild
يحدد الطفل الأول لهذه القطعة. -
secondChild
يحدد الطفل الثاني.
AnimatedAlign
في هذه المرحلة ، يتم وضع تسمية الاسم في وسط الشاشة. سيبدو أفضل بكثير في الجزء العلوي ، حيث نحتاج إلى وسط الشاشة لإظهار عروض الأسعار. ببساطة ، يجب تغيير محاذاة أداة تسمية الاسم من المركز إلى الأعلى. ألن يكون من الجيد تحريك تغيير المحاذاة هذا جنبًا إلى جنب مع الرسوم المتحركة السابقة ذات التلاشي المتقاطع؟ دعنا نقوم به.
كما هو الحال دائمًا ، يمكن استخدام العديد من التقنيات لتحقيق ذلك. نظرًا لأن عنصر واجهة مستخدم تسمية الاسم محاذي للوسط بالفعل ، فإن تحريك محاذاته سيكون أبسط بكثير من معالجة القيم العلوية واليسرى للأداة. تعتبر أداة AnimatedAlign
مثالية لهذه الوظيفة.
لبدء هذا الرسم المتحرك ، يلزم وجود مشغل. الغرض الوحيد من هذه الأداة هو تحريك تغيير المحاذاة ، لذا فهي لا تحتوي إلا على عدد قليل من الخصائص: إضافة طفل ، وتعيين محاذاة ، وتشغيل تغيير المحاذاة ، وهذا كل شيء.
_getAnimatedAlignWidget() { return AnimatedAlign( duration: Duration(seconds: 1), curve: Curves.easeInOut, alignment: alignTop ? Alignment.topLeft : Alignment.center, child: _getAnimatedCrossfade(), ); }
لها خاصيتان إلزاميتان فقط:
- طفل:
الطفل الذي سيتم تعديل محاذاة. - انتقام:
قيمة المحاذاة المطلوبة.
هذه الأداة بسيطة حقًا ولكن النتائج أنيقة. علاوة على ذلك ، رأينا مدى سهولة استخدام عنصرين متحركين مختلفين لإنشاء رسوم متحركة أكثر تعقيدًا. هذا هو جمال الحاجيات المتحركة.
AnimatedPadding
الآن لدينا اسم المستخدم في الأعلى ، متحرك بسلاسة دون بذل الكثير من الجهد ، باستخدام أنواع مختلفة من عناصر واجهة المستخدم المتحركة. دعنا نضيف تحية ، "مرحبًا" قبل الاسم. ستؤدي إضافة عنصر واجهة مستخدم نصي بقيمة "مرحبًا" في الجزء العلوي إلى جعله يتداخل مع أداة نص الترحيب ، على غرار الصورة أدناه.
ماذا لو كانت أداة نص الاسم تحتوي على بعض المساحة المتروكة على اليسار؟ ستنجح بالتأكيد زيادة المساحة المتروكة اليسرى ، لكن انتظر: هل يمكننا زيادة المساحة المتروكة ببعض الرسوم المتحركة؟ نعم ، وهذا ما يفعله AnimatedPadding
. لجعل كل هذا يبدو أفضل بكثير ، دعنا نجعل عنصر واجهة نص التحيات يتلاشى ويزداد حجم مساحة أداة نص الاسم في نفس الوقت.
_getAnimatedPaddingWidget() { return AnimatedPadding( duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn, padding: increaseLeftPadding ? EdgeInsets.only(left: 28.0) : EdgeInsets.only(left: 0), child: _getAnimatedCrossfade(), ); }
نظرًا لأن الرسوم المتحركة أعلاه يجب أن تحدث فقط بعد اكتمال المحاذاة المتحركة السابقة ، فنحن بحاجة إلى تأخير تشغيل هذه الرسوم المتحركة. استطراداً من الموضوع لفترة وجيزة ، هذه لحظة جيدة للحديث عن آلية شائعة لإضافة التأخير. يوفر Flutter العديد من هذه التقنيات ، لكن مُنشئ Future.delayed هو أحد الأساليب الأبسط والأنظف والأكثر قابلية للقراءة. على سبيل المثال ، لتنفيذ جزء من التعليمات البرمجية بعد ثانية واحدة:
Future.delayed(Duration(seconds: 1), (){ sum = a + b; // This sum will be calculated after 1 second. print(sum); });
نظرًا لأن مدة التأخير معروفة بالفعل (محسوبة من مدد الرسوم المتحركة السابقة) ، يمكن تشغيل الرسم المتحرك بعد هذا الفاصل الزمني.
// Showing “Hi” after 1 second - greetings visibility trigger. _showGreetings() { Future.delayed(Duration(seconds: 1), () { setState(() { showGreetings = true; }); }); } // Increasing the padding for name label widget after 1 second - increase padding trigger. _increaseLeftPadding() { Future.delayed(Duration(seconds: 1), () { setState(() { increaseLeftPadding = true; }); }); }
هذه الأداة لها خاصيتان إلزاميتان فقط:
-
child
الطفل الموجود داخل هذه الأداة ، والذي سيتم تطبيق المساحة المتروكة عليه. -
padding
مقدار المساحة المراد إضافتها.
AnimatedSize
اليوم ، سيتضمن أي تطبيق به نوع من الرسوم المتحركة تكبير أو تصغير المكونات المرئية لجذب انتباه المستخدم (يُطلق عليها عادةً اسم الرسوم المتحركة المتدرجة). لماذا لا تستخدم نفس التقنية هنا؟ يمكننا أن نظهر للمستخدم اقتباسًا تحفيزيًا يتم تكبيره من وسط الشاشة. اسمح لي بتقديمك إلى عنصر واجهة المستخدم AnimatedSize
، والذي يتيح تأثيرات التكبير والتصغير ، التي يتم التحكم فيها عن طريق تغيير حجم الطفل.
تختلف هذه الأداة قليلاً عن غيرها عندما يتعلق الأمر بالمعلمات المطلوبة. نحن بحاجة إلى ما يسميه Flutter "شريط". يحتوي Flutter على طريقة للسماح للكائنات بمعرفة متى يتم تشغيل حدث إطار جديد. يمكن اعتباره شيئًا يرسل إشارة تقول ، "افعلها الآن! … افعلها الآن! … افعلها الآن! ... "
تتطلب أداة AnimatedSize
خاصية - vsync - والتي تقبل مزود المؤشر. أسهل طريقة للحصول على مزود الأسهم هي إضافة Mixin إلى الفصل. هناك نوعان من تطبيقات موفر الأسهم الأساسية: SingleTickerProviderStateMixin
، والتي توفر مؤشرًا واحدًا ؛ و TickerProviderStateMixin
، الذي يوفر العديد من ملفات.
يتم استخدام التطبيق الافتراضي لشريط المؤشر لتمييز إطارات الرسم المتحرك. في هذه الحالة ، يتم استخدام الأخير. المزيد عن الخلطات.
// Helper method to create quotes card widget. _getQuoteCardWidget() { return Card( color: Colors.green, elevation: 8.0, child: _getAnimatedSizeWidget(), ); } // Helper method to create animated size widget and set its properties. _getAnimatedSizeWidget() { return AnimatedSize( duration: Duration(seconds: 1), curve: Curves.easeInOut, vsync: this, child: _getQuoteContainer(), ); } // Helper method to create the quotes container widget with different sizes. _getQuoteContainer() { return Container( height: showQuoteCard ? 100 : 0, width: showQuoteCard ? screenWidth - 32 : 0, child: Center( child: Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: Text(quote, style: TextStyle(color: Colors.white, fontWeight: FontWeight.w400, fontSize: 14),), ), ), ); } // Trigger used to show the quote card widget. _showQuote() { Future.delayed(Duration(seconds: 2), () { setState(() { showQuoteCard = true; }); }); }
الخصائص الإلزامية لهذه الأداة:
-
vsync
مزود المؤشر المطلوب لتنسيق الرسوم المتحركة وتغييرات الإطار. < -
child
الطفل الذي يتغير حجمه سيتم تحريكه.
أصبح من السهل الآن ترويض الرسوم المتحركة للتكبير والتصغير.
AnimatedPositioned
رائعة! يتم تكبير الاقتباسات من المركز لجذب انتباه المستخدم. ماذا لو انزلق لأعلى من الأسفل أثناء التكبير؟ دعونا نحاول ذلك. تتضمن هذه الحركة اللعب بموضع عنصر واجهة عرض الأسعار وتحريك التغييرات في خصائص الموضع. AnimatedPositioned
هو المرشح المثالي.
تنقل هذه الأداة تلقائيًا موضع الطفل خلال مدة معينة كلما تغير الموضع المحدد. نقطة واحدة يجب ملاحظتها: لا تعمل إلا إذا كانت الأداة الرئيسية الخاصة بها هي "Stack". هذه الأداة بسيطة جدًا وسهلة الاستخدام. دعنا نرى.
// Helper method to create the animated positioned widget. // With position changes based on “showQuoteCard” flag. _getAnimatedPositionWidget() { return AnimatedPositioned( duration: Duration(seconds: 1), curve: Curves.easeInOut, child: _getQuoteCardWidget(), top: showQuoteCard ? screenHeight/2 - 100 : screenHeight, left: !showQuoteCard ? screenWidth/2 : 12, ); }
هذه الأداة لها خاصية إلزامية واحدة فقط:
-
child
القطعة التي سيتم تغيير موقعها.
إذا لم يكن من المتوقع أن يتغير حجم الطفل جنبًا إلى جنب مع موضعه ، فسيكون SlideTransition
هو البديل الأكثر أداءً لهذه الأداة.
ها هي الرسوم المتحركة الكاملة لدينا:
خاتمة
الرسوم المتحركة هي جزء لا يتجزأ من تجربة المستخدم. لا تقلل التطبيقات أو التطبيقات الثابتة ذات الرسوم المتحركة المبتذلة من الاحتفاظ بالمستخدمين فحسب ، بل تقلل أيضًا من سمعة المطور في تقديم النتائج.
اليوم ، تحتوي التطبيقات الأكثر شيوعًا على نوع من الرسوم المتحركة الدقيقة لإسعاد المستخدمين. يمكن للتعليقات المتحركة على طلبات المستخدم أن تجذبهم أيضًا لاستكشاف المزيد. يوفر Flutter الكثير من الميزات للتطوير عبر الأنظمة الأساسية ، بما في ذلك الدعم الغني للرسوم المتحركة السلسة والمتجاوبة.
يحتوي Flutter على دعم إضافي رائع يسمح لنا باستخدام الرسوم المتحركة من مطورين آخرين. الآن وقد نضج إلى الإصدار 1.9 ، مع الكثير من الحب من المجتمع ، لا بد أن يتحسن Flutter في المستقبل. أود أن أقول الآن إنه وقت رائع لتعلم Flutter!
مزيد من الموارد
- "أدوات الرسوم المتحركة والحركة" ، مستندات Flutter
- "مقدمة إلى الرسوم المتحركة" ، Flutter Docs
- Flutter Codelabs
ملاحظة المحرر : شكراً جزيلاً لأحمد عويس على مساعدته في مراجعة هذا المقال.