ایجاد انیمیشن اسکرول در فلاتر (Flutter) — از صفر تا صد

۲۳۰ بازدید
آخرین به‌روزرسانی: ۰۳ مهر ۱۴۰۲
زمان مطالعه: ۵ دقیقه
ایجاد انیمیشن اسکرول در فلاتر (Flutter) — از صفر تا صد

در این مقاله نگاهی به شیوه ساخت انیمیشن‌های اسکرول سفارشی از صفر تا صد با استفاده از SDK فلاتر خواهیم داشت. فلاتر یک ابزار پرقدرت برای ایجاد اپلیکیشن‌های نیتیو موبایل است که به صورتی عالی اجرا می‌شوند و انعطاف‌پذیری خارق‌العاده‌ای در زمینه خلق تجربیات کاربری غنی فراهم می‌سازد.

انیمیشن اسکرول در فلات

اگر به محیط فلاتر دسترسی ندارید به صفحه نصب (+) آن بروید.

شروع

در دمویی که در این مقاله می‌خواهیم بسازیم یک پروژه پیش‌فرض با استفاده از دستور flutter create ایجاد می‌کنیم و تنها از کلاس‌هایی استفاده می‌کنیم که مستقیماً درون فلاتر وجود دارند و لذا نیاز به افزودن هیچ وابستگی به پروژه نداریم. در اغلب موارد، یک وظیفه (مانند انیمیشن سفارشی) می‌تواند مستقیماً و بدون نیاز به کتابخانه‌های اضافی اجرا شود.

ایده اصلی این اپلیکیشن نمونه، ایجاد یک نمای لیستی ساده با چند آیتم است و زمانی که این نما به سمت پایین اسکرول می‌شود، در پس‌زمینه یک چرخ‌دنده در جهت‌های ساعت‌گرد یا پادساعت‌گرد می‌چرخد. این کار با کمک گرفتن از کنترلر اسکرول روی نمای لیستی و ارسال یک پس‌زمینه انیمیشنی به آن صورت می‌گیرد که یک ویجت سفارشی می‌سازد که همگام با موقعیت اسکرول به چرخش درمی‌آید.

نقطه ورود اپلیکیشن

کار خود را با فایل اصلی اپلیکیشن یعنی lib/main.dart آغاز می‌کنیم:

1import 'package:flutter/material.dart';
2import 'demo-card.dart';
3import 'items.dart';
4import 'animated-bg.dart';
5
6void main() => runApp(AnimationDemo());
7
8class AnimationDemo extends StatelessWidget {
9
10    @override
11    Widget build(BuildContext context) {
12
13        return MaterialApp(
14            title: 'Flutter Demo',
15            theme: ThemeData(primarySwatch: Colors.deepPurple),
16            home: MyHomePage(title: 'Flutter Animation Demo'),
17        );
18    }
19}
20
21class MyHomePage extends StatefulWidget {
22
23    MyHomePage({Key key, this.title}) : super(key: key);
24    final String title;
25
26    @override
27    _MyHomePageState createState() => _MyHomePageState();
28}
29
30class _MyHomePageState extends State<MyHomePage> {
31
32    ScrollController _controller = new ScrollController();
33
34    List<DemoCard> get _cards =>
35        items.map((Item _item) => DemoCard(_item)).toList();
36
37    @override
38    Widget build(BuildContext context) {
39
40        return Scaffold(
41
42            backgroundColor: Colors.black,
43            appBar: AppBar(title: Text(widget.title)),
44            body: Stack(
45
46                alignment: AlignmentDirectional.topStart,
47                children: <Widget>[
48
49                    AnimatedBackground(controller: _controller),
50                    Center(
51                        child: ListView(
52                            controller: _controller,
53                            children: _cards
54                        )
55                    )
56                ]
57            )
58        );
59    }
60}

در فایل main.dart، برخی دستورهای ایمپورت برای کامپوننت‌های زیر وجود دارند:

  • demo-card.dart – یک ویجت کارت از یک آیتم می‌سازد.
  • items.dart – کلاس آیتم را تعریف کرده و محتوایی برای اپلیکیشن دمو ایجاد می‌کند.
  • animated-bg.dart – ویجت پس‌زمینه انیمیشنی را می‌سازد.

کلاس اپلیکیشن اصلی (AnimationDemo) یک اپلیکیشن ابتدایی می‌سازد که ویجت صفحه اصلی پیش‌فرض (MyHomePage) را پوشش می‌دهد. در کلاس MyHomePage یک مشخصه به نام controller_ وجود دارد که به یک وهله جدید از کلاس ScrollController مقداردهی می‌شود تا در ادامه به هر دو کامپوننت AnimatedBackground و ListView ارسال شود. کامپوننت AnimatedBackground به چرخاندن چرخ رنده در پس‌زمینه می‌پردازد و ListView نیز لیست اسکرول شونده از آیتم‌های دمو را رندر می‌کند. یک مشخصه به نام cards_ نیز وجود دارد که با لیستی از اشیای Item آغاز می‌شود که از items.dart ایمپورت شده‌اند و لیستی از ویجت‌های DemoCard برای رندر شدن در ListView بازگشت می‌دهد.

کلاس Item

نخستین فایل ایمپورت شده که به بررسی آن می‌پردازیم فایل lib/items.dart است:

1import 'package:flutter/material.dart';
2
3class Item {
4
5	String name;
6	String description;
7	MaterialColor color;
8	IconData icon;
9	Item(this.name, this.description, this.color, this.icon);
10}
11
12List<Item> items = [
13	Item('A', "Something cool", Colors.amber, Icons.ac_unit),
14	Item('B', "Hey, why not?", Colors.cyan, Icons.add_photo_alternate),
15	Item('C', "This might be OK", Colors.indigo, Icons.airplay),
16	Item('D', "Totally awesome", Colors.green, Icons.crop),
17	Item('E', "Rockin out", Colors.pink, Icons.album),
18	Item('F', "Take a look", Colors.blue, Icons.adb)
19];

کلاس Item ساختمان داده‌ای را ارائه می‌کند که همراه با وهله‌ای از DemoCard ارسال می‌شود تا در ListView رندر شود. در این مورد چیز خاصی زیادی برای تنظیم به جز یک نام، توضیح (که در حال حاضر در دمو بی‌استفاده است)، رنگ و آیکون برای رندر شدن هر آیتم وجود ندارد. لیستی از اشیای آیتم‌ها به عنوان لیستی از محتوای ساده ارائه می‌شود که درون ListView رندر می‌شوند.

کلاس DemoCard

فایل بعدی که بررسی می‌کنیم lib/demo-card.dart نام دارد:

1import 'package:flutter/material.dart';
2import 'items.dart';
3
4class DemoCard extends StatelessWidget {
5
6    DemoCard(this.item);
7    final Item item;
8
9    static final Shadow _shadow = Shadow(offset: Offset(2.0, 2.0), color: Colors.black26);
10    final TextStyle _style = TextStyle(color: Colors.white70, shadows: [_shadow]); 
11
12    @override
13    Widget build(BuildContext context) {
14
15        return Card(
16                
17            elevation: 3,
18            shape: RoundedRectangleBorder(
19                side: BorderSide(width: 1, color: Colors.black26),
20                borderRadius: BorderRadius.circular(32)
21            ),
22            color: item.color.withOpacity(.7),
23            child: Container(
24
25                constraints: BoxConstraints.expand(height: 256),
26                child: RawMaterialButton(
27
28                    onPressed: () {  },
29                    child: Column(
30                        
31                        mainAxisAlignment: MainAxisAlignment.spaceAround,
32                        crossAxisAlignment: CrossAxisAlignment.stretch,
33                        children: <Widget>[
34                            Row(
35                                mainAxisAlignment: MainAxisAlignment.spaceAround,
36                                children: <Widget>[
37
38                                    Text(item.name, style: _style.copyWith(fontSize: 64)),
39                                    Icon(item.icon, color: Colors.white70, size: 72),
40                                ]
41                            )
42                        ],
43                    ),
44                )
45            )
46        );
47    }
48}

کلاس DemoCard در سازنده خود یک آیتم دریافت می‌کند و یک ویجت کارت بازگشت می‌دهد که عناصر Text و Icon را رندر می‌کند که به نمایش نام و آیکون تعریف‌شده برای هر آیتم می‌پردازند. برخی استایل‌بندی‌های ساده نیز با استفاده از Shadow ،TextStyle و RoundedRectangleBorder همراه با ارتفاع کارت (که روی 3 تنظیم‌شده) اعمال می‌شوند. ویجت‌های Column و Row برای گسترش عناصر فرزند روی کارت استفاده می‌شوند.

کلاس AnimatedBackground

ما بهترین بخش کار را برای انتها نگه داشته‌ایم. بنابراین در این بخش به بررسی فایل lib/animated-bg.dart می‌پردازیم:

1import 'package:flutter/material.dart';
2import 'dart:math' as math;
3
4class AnimatedBackground extends StatefulWidget {
5
6    AnimatedBackground({Key key, this.controller}) : super(key: key);
7
8    final ScrollController controller;
9
10    @override
11    _AnimatedBackgroundState createState() => _AnimatedBackgroundState();
12}
13
14class _AnimatedBackgroundState extends State<AnimatedBackground> {
15
16    get offset => widget.controller.hasClients ? widget.controller.offset : 0;
17
18    @override
19    Widget build(BuildContext context) {
20    
21        return AnimatedBuilder(
22
23            animation: widget.controller,
24            builder: (BuildContext context, Widget child) {
25
26                return  OverflowBox(
27                    
28                    maxWidth: double.infinity,
29                    alignment: Alignment(4, 3),
30                    child: Transform.rotate(
31                        angle: ((math.pi * offset) / -1024),
32                        child: Icon(Icons.settings, size: 512, color: Colors.white)
33                    )
34                );
35            }
36        );
37    }
38}

در همان ابتدای فایل، کتابخانه dart:math ایمپورت شده تا به ثابت π دسترسی پیدا کنیم که برای اجرای محاسبات تبدیل چرخشی برای گردش چرخ‌دنده مورد نیاز است. سازنده کلاس AnimatedBackground یک مقدار ScrollController می‌گیرد که اقدام به اجرای چرخش می‌کند. مشخصه offset_ در صورتی که کنترلر کلاینت‌هایی داشته باشد یعنی کنترلر به درستی مقداردهی شده باشد و به یک عنصر اسکرول شنونده واقعی مانند ListView قلاب شده باشد یک آفست بازگشت می‌دهد و در غیر این صورت مقدار صفر بازمی‌گرداند. متد build یک AnimatedBuilder بازگشت می‌دهد که کنترلر را می‌گیرد و یک OverflowBox می‌سازد که همراستا با چرخ‌دنده و خارج از صفحه جای می‌گیرد.

مقادیر 4 و 3 موجب می‌شوند که چرخ‌دنده در گوشه پایین-چپ دستگاه تست که یک شبیه‌ساز آیفون XR است، قرار گیرند. در عمل مقادیر Alignment باید از مختصات عرض و ارتفاع صفحه دستگاه محاسبه شوند تا مقادیر دقیقی برای قرار گرفتن چرخ‌دنده در موقعیت مطلوب در هر دستگاه به دست آید. با این حال ما در این مثال تلاش کرده‌ایم همه چیز تا حد امکان ساده باشد.

آخرین بخش جایی است که خود انیمیشن در آن اتفاق می‌افتد و این همان متد استاتیک rotate در کلاس است. این کلاس یک مقدار angle و یک child می‌گیرد و فرزند به وسیله زاویه مورد نظر به چرخش درمی‌آید. در این دمو ما می‌خواهیم که چرخ‌دنده به آرامی حرکت کند و بیشتر به فیزیک واقعی شبیه باشد تا چرخش سریع دیوانه‌وار که در زمان تعیین مقدار چرخش 1:1 به دست می‌آید. همچنین می‌خواهیم چرخ‌دنده در جهت پادساعت‌گرد بچرخد، گرچه از نظر فیزیکی لیست را به حرکت درمی‌آورد بنابراین offset را در math.pi ضرب کرده و سپس حاصل‌ضرب را بر 1024- تقسیم می‌کنیم.

سخن پایانی

این مقاله و پروژه نمونه صرفاً برخی از قابلیت‌هایی که فلاتر برای ایجاد تجربیات کاربری متحرک سفارشی ارائه می‌کنند را نشان دادند. ما با استفاده از تنها چند کلاس ابتدایی و مسیر ساده، یک انیمیشن کامل داریم که یک عنصر کاملاً ملال‌آور مانند صفحه تنظیمات را به چیزی کاملاً جذاب تبدیل می‌کند.

این مفاهیم می‌توانند برای ایجاد «صفحه‌های آغازین» (splash screens)، انیمیشن‌های بارگذاری، گذار صفحه، جلوه‌های نوتیفیکیشن یا هر چیز دیگری مورد استفاده قرار گیرند. تقریباً هر چیزی که یک مقدار Double به عنوان آرگومان می‌گیرد می‌تواند انیمیت شود و در نتیجه پیاده‌سازی سرراستی از جلوه‌های مختلف مانند چرخش در این مثال به دست می‌آید. همچنین میزان شفافیت، موقعیت و بسیاری مشخصه‌های دیگر نیز قابل تنظیم هستند.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما توصیه می‌شوند:

==

بر اساس رای ۲ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
itnext
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *