แอพเคลื่อนไหวด้วย Flutter
เผยแพร่แล้ว: 2022-03-10แอพสำหรับแพลตฟอร์มใด ๆ ได้รับการยกย่องเมื่อพวกเขาใช้งานง่าย ดูดี และให้ข้อเสนอแนะที่น่าพึงพอใจต่อการโต้ตอบของผู้ใช้ แอนิเมชั่นเป็นวิธีหนึ่งในการทำเช่นนั้น
Flutter ซึ่งเป็นเฟรมเวิร์กข้ามแพลตฟอร์มได้เติบโตเต็มที่ในช่วงสองปีที่ผ่านมาเพื่อรวมการสนับสนุนเว็บและเดสก์ท็อป ได้รับชื่อเสียงว่าแอพที่พัฒนาขึ้นนั้นมีความราบรื่นและดูดี ด้วยการสนับสนุนแอนิเมชั่นที่หลากหลาย วิธีการเขียน UI อย่างเปิดเผย “Hot Reload” และคุณสมบัติอื่นๆ ตอนนี้เป็นเฟรมเวิร์กข้ามแพลตฟอร์มที่สมบูรณ์แล้ว
หากคุณกำลังเริ่มต้นใช้งาน 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 แล้วกด Next
ตอนนี้คุณต้องป้อนข้อมูลเฉพาะโครงการ เช่น ชื่อและเส้นทางของโครงการ โดเมนบริษัท และอื่นๆ ดูภาพด้านล่างได้เลยครับ

เพิ่มชื่อโปรเจ็กต์ เส้นทาง 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
ซึ่งมีโครงสร้างพื้นฐานสำหรับการสร้างแอปที่สอดคล้องกับดีไซน์ Material ในการทำให้โค้ดมีโครงสร้างมากขึ้น ให้สร้างไฟล์ 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 มีรายการใบเสนอราคาแบบฮาร์ดโค้ดทั้งหมด จุดหนึ่งที่ควรทราบที่นี่คือรายการที่เป็นวัตถุคงที่ ซึ่งหมายความว่าสามารถใช้ที่อื่นได้โดยไม่ต้องสร้างวัตถุใหม่ของคลาส Quotes สิ่งนี้ถูกเลือกโดยการออกแบบเนื่องจากรายการด้านบนทำหน้าที่เป็นยูทิลิตี้

เพิ่มรหัสต่อไปนี้ในไฟล์นี้:
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
วิดเจ็ตนี้สร้างบนวิดเจ็ต Opacity โดยเพิ่มการสนับสนุนแอนิเมชั่นโดยนัย เราจะใช้มันอย่างไร? จำสถานการณ์ของเราไว้: เราต้องแสดงปุ่มถัดไปพร้อมการมองเห็นแบบเคลื่อนไหว เรารวมวิดเจ็ตปุ่มไว้ในวิดเจ็ต 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
ณ จุดนี้ ป้ายชื่อจะอยู่ตรงกลางหน้าจอ ด้านบนจะดูดีขึ้นมาก เนื่องจากเราต้องการให้ตรงกลางหน้าจอแสดงเครื่องหมายคำพูด พูดง่ายๆ ก็คือ การจัดตำแหน่งของวิดเจ็ตป้ายชื่อควรเปลี่ยนจากตรงกลางเป็นด้านบน และคงจะดีไม่น้อยถ้าจะทำให้การเปลี่ยนการจัดแนวนี้เคลื่อนไหวไปพร้อมกับแอนิเมชั่น cross-fade ก่อนหน้านี้ มาทำกัน
และเช่นเคย สามารถใช้เทคนิคหลายอย่างเพื่อให้บรรลุสิ่งนี้ เนื่องจากวิดเจ็ตป้ายชื่อถูกจัดกึ่งกลางอยู่แล้ว การจัดตำแหน่งให้เคลื่อนไหวจึงง่ายกว่าการจัดการค่าบนและด้านซ้ายของวิดเจ็ตอย่างมาก วิดเจ็ต 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 เป็นหนึ่งในวิธีการที่ง่ายกว่า สะอาดกว่า และอ่านง่ายกว่า ตัวอย่างเช่น ในการรันโค้ดหลังจาก 1 วินาที:
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 เรียกว่า "Ticker" Flutter มีเมธอดที่จะแจ้งให้อ็อบเจ็กต์ทราบทุกครั้งที่มีเหตุการณ์เฟรมใหม่เกิดขึ้น ถือได้ว่าเป็นการส่งสัญญาณว่า “ลงมือเลย! … ทำมันตอนนี้! … ทำมันตอนนี้! …”
วิดเจ็ต AnimatedSize
ต้องการคุณสมบัติ — vsync — ซึ่งยอมรับผู้ให้บริการทิกเกอร์ วิธีที่ง่ายที่สุดในการรับผู้ให้บริการทิกเกอร์คือการเพิ่ม Mixin ในชั้นเรียน มีการใช้งานผู้ให้บริการทิกเกอร์พื้นฐานสองแบบ: SingleTickerProviderStateMixin
ซึ่งจัดเตรียมทิกเกอร์เดียว และ TickerProviderStateMixin
ซึ่งมีหลายอย่าง
การใช้งานเริ่มต้นของ Ticker ใช้เพื่อทำเครื่องหมายเฟรมของแอนิเมชั่น ในกรณีนี้จะใช้อย่างหลัง เพิ่มเติมเกี่ยวกับมิกซ์อิน
// 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
เป็นผู้สมัครที่สมบูรณ์แบบ
วิดเจ็ตนี้จะเปลี่ยนตำแหน่งของเด็กโดยอัตโนมัติในช่วงเวลาที่กำหนดเมื่อใดก็ตามที่ตำแหน่งที่ระบุเปลี่ยนแปลง จุดหนึ่งที่ควรทราบ: ใช้งานได้ก็ต่อเมื่อวิดเจ็ตหลักเป็น "สแต็ก" วิดเจ็ตนี้ค่อนข้างเรียบง่ายและใช้งานง่าย มาดูกัน.
// 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 Docs
- “ความรู้เบื้องต้นเกี่ยวกับแอนิเมชั่น,” Flutter Docs
- Flutter Codelabs
หมายเหตุบรรณาธิการ : ขอบคุณมากสำหรับ Ahmad Awais สำหรับความช่วยเหลือของเขาในการทบทวนบทความนี้