در این قسمت ، قصد داریم اولین نوع Animation در فلاتر رو بررسی کنیم.
در فلاتر همه چیز وابسته به Widget هست. همچون Container , Padding , Text و …
حالا برای بحث Animation ، فلاتر یک سری Widget رو ایجاد کرده که این نوع ویجت ها امکان انیمیشن شدن رو دارند یعنی همان Animated Widget
به طور مثال Animated Container داریم. همان Container که میتونه دارای انیمیشن باشه. یعنی یک نقطه ابتدا و انتها از ما دریافت کنه و مسیر این دو رو خودش Animate کنه.
در اولین مثال قصد داریم یک نمونه از همین ویجت ها رو Animate کنیم.در مرحله اول یک stateful widget ایجاد می کنیم.
import 'package:flutter/material.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyApp(),
);
}
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return Container();
}
}
حالا اپلیکیشن رو یکبار ریست می کنیم.
در دومین قدم نیاز داریم برنامه ما ساختار اپلیکیشن داشته باشه. در نتیجه از Scaffold استفاده می کنیم.
return Scaffold(
appBar: AppBar(),
body: Container(
color: Colors.white,
),);
برای حذف بنر نیز از debugShowCheckedModeBanner استفاده کنید.
debugShowCheckedModeBanner: false,
حالا میایم و یک Animated Widget ایجاد می کنیم اون ویجت رو با استفاده از یک Button کنترل می کنیم.
در مرحله اول یک ویجت Center ایجاد میکنیم تا تمام ویجت ها در مرکز قرار بگیرند.
در ویجت children نیز یک Animated Widget رو ایجاد می کنیم.
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedContainer();
],
),
),
AnimatedContainer چندین ویژگی داره که دو تا از اونها کاربرد بیشتری دارند. از جمله duration و curve
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedContainer(
duration: Duration(milliseconds: 600),
),
],
),
),
پس از مشخص کردن duration نیز میایم یک عرض و ارتفاع ثابت در نظر میگیریم.
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedContainer(
duration: Duration(milliseconds: 600),
width: 300,
height: 300,
color: Colors.purple,
),
],
),
),
پس از run برنامه یک باکس بنفش برای ما نمایش داده میشه. بعد از ویجت AnimatedContainer از SizedBox استفاده می کنیم که یک button رو با فاصله از مربع نمایش دهیم.
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedContainer(
duration: Duration(milliseconds: 600),
width: 300,
height: 300,
color: Colors.purple,
),
SizedBox(height: 50,),
],
),
),
برای ایجاد یک button نیز از ویجت RaisedButton استفاده می کنیم و پارامترهای child و onPressed رو پیاده سازی میکنیم.
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedContainer(
duration: Duration(milliseconds: 600),
width: 300,
height: 300,
color: Colors.purple,
),
SizedBox(height: 50,),
RaisedButton(
child: Text("Press"),
onPressed: (){
},
)
],
),
),
دقت کنید تا الان هیچ عملیاتی روی button ایجاد نشده.
خب تا اینجا ، یک باکس 300 در 300 و یک button ایجاد کردیم. حالا قصد داریم با فشردن دکمه Press عرض و ارتفاع باکس رو با یک ساختار Animate کم کنیم.
پس در مرحله اول باید عرض و ارتفاع باکس رو متغییر کنیم.
در مرحله اول یک مقدار boolean تعریف می کنیم.
bool isBig=false;
حالا در پارامترهای width , height داریم.
width: isBig==false ? 300 : 100,
height: isBig==false ? 300 : 100,
حالا قصد داریم با فشردن button عرض و ارتفاع این باکس رو مدیریت کنیم.
پس در نتیجه از تابع setState استفاده می کنیم.
setState(() {
isBig=!isBig;
});
خب تا اینجا انیمیشن باکس ما از طریق button در حال مدیریت هست. این انیمیشن ما به صورت خطی هست و ما میتونیم از طریق پارامتر curve نوع های دیگه ای از انیمیشن نیز داشته باشیم.
به طور مثال easeOutQuart
در نتیجه داریم :
import 'package:flutter/material.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyApp(),
);
}
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool isBig=false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
color: Colors.white,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedContainer(
duration: Duration(milliseconds: 600),
curve: Curves.easeOutQuart,
width: isBig==false ? 300 : 100,
height: isBig==false ? 300 : 100,
color: Colors.purple,
),
SizedBox(height: 50,),
RaisedButton(
child: Text("press"),
onPressed: (){
setState(() {
isBig=!isBig;
});
},
)
],
),
),
),
);
}
}
برای مشاهده ویدئوی این درس می بایست در دوره ثبت نام نمائید.
در جلسه قبل با Animated Widgets آشنا شدیم.حالا اگر قصد داشته باشیم یک Widget رو Animate کنیم که داخل Animated Widgets نبود ، روال کار به چه صورت هست؟
یکی از راه حل ها ، استفاده از Tween هست که به ما کمک می کند ، هر Value رو با یک ساختار Animate ایجاد کنیم.Tween میاد یک مقدار ابتدا و انتها از هر نوعی ، به عنوان ورودی دریافت و بین این مقادیر رو با Animated تغییر می کند. یعنی ما می توانیم با این مقادیر ابتدا و انتها یک انیمیشن ایجاد کنیم.
حالا چطور از Tween استفاده کنیم؟
خب در جلسه قبل مثال زیر رو داشتیم
import 'package:flutter/material.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyApp(),
);
}
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State {
bool isBig=false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
color: Colors.white,
),
);
}
}
حالا قصد داریم یک Container دیگه ایجاد و از طریق Tween فاصله این دو Container رو Animate کنیم. طبیعتا ما چنین Animated Widget برای این روند نداریم و باید خودمون این ساختار رو پیاده سازی کنیم.در مرحله اول از تابع TweenAnimationBuilder استفاده می کنیم. این تابع نیز همانند Animated Widget دو ویژگی duration و curve رو نیز دارد.
child: TweenAnimationBuilder(
duration: Duration(milliseconds: 600),
curve: Curves.easeOutQuart,
),
ویژگی های دیگری که در این تابع داریم ، ویژگی tween هست که ما باید ابتدا و انتهای آیتمی که قصد داریم Animate کنیم را مشخص کنیم.در این مرحله باید مشخص کنیم چه نوع Value رو باید Animate کنیم. ما double تعریف می کنیم.
این Value داخل Tween مشخص می کند نوعی که میخواد تو این مدت 600 میلی ثانیه از نقطه ابتدا به انتها برود double هست. ما داخل tween دو پارامتر begin و end داریم. برای مقدار end یک متغیر تعریف می کنیم.
double targetValue=100;
tween: Tween(begin: 0,end:targetValue ),
حالا باید آیتم builder رو مشخص کنیم. در واقع اون قسمتی هست که میاد و موقع Animate رفرش میشه. 3 پارامتر ورودی داره.
builder: (context,double size,Widget child)
حالا باید رابط کاربری که در این builder میخوایم نمایش بدیم رو پیاده سازی کنیم.
builder: (context,double size,Widget child){
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
color: Colors.indigo,
width: 300,
height: 100,
),
SizedBox(
height: size,
),
Container(
color: Colors.indigo,
width: 300,
height: 100,
),
RaisedButton(
child: Text("press me"),
onPressed: (){
setState(() {
targetValue= targetValue==0.0 ? 100 : 0.0;
});
},
),
Text(size.toInt().toString(),style: TextStyle(fontSize: 30),)
],
);
},
در مرحله اول یک Column ایجاد و مقدار mainAxisAlignment رو center در نظر میگیریم که آیتم های ما در وسط اپ قرار بگیرن.
رنگ و عرض و ارتفاع widget رو نیز تعریف میکنیم.
Container(
color: Colors.indigo,
width: 300,
height: 100,
),
پس از ساخت widget یک sidebox نیز ایجاد میکنیم که این sidebox ارتفاع بین دو widget هست.
SizedBox(height: size,),
یک widget دیگه نیز پیاده سازی می کنیم.
Container(
color: Colors.indigo,
width: 300,
height: 100,
),
و در نهایت از طریق یک button این انیمیشن رو کنترل می کنیم.
RaisedButton(
child: Text("press me"),
onPressed: (){
setState(() {
targetValue= targetValue==0.0 ? 100 : 0.0;
});
},
),
Text(size.toInt().toString(),style: TextStyle(fontSize: 30),)
در نهایت داریم
import 'package:flutter/material.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyApp(),
);
}
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State {
double targetValue=100;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
color: Colors.white,
child: Center(
child: TweenAnimationBuilder(
duration: Duration(milliseconds: 600),
curve: Curves.easeOutQuart,
tween: Tween(begin: 0,end:targetValue ),
builder: (context,double size,Widget child){
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
color: Colors.indigo,
width: 300,
height: 100,
),
SizedBox(
height: size,
),
Container(
color: Colors.indigo,
width: 300,
height: 100,
),
RaisedButton(
child: Text("press me"),
onPressed: (){
setState(() {
targetValue= targetValue==0.0 ? 100 : 0.0;
});
},
),
Text(size.toInt().toString(),style: TextStyle(fontSize: 30),)
],
);
},
),
),
),
);
}
}
برای مشاهده ویدئوی این درس می بایست در دوره ثبت نام نمائید.
برای مشاهده ویدئوی این درس می بایست در دوره ثبت نام نمائید.
خب ما تا اینجا در مورد Animate Widget و Tween صحبت کردیم. اما مشکلی که در این دو مورد هست ، این هست که ما کنترلی روی انیمیشن نداریم. یعنی ابتدا و انتها رو به ویجت میدادیم و انیمیشن ایجاد می شد. اما در وسط انیمیشن کنترلی نداریم که بخوایم stop کنیم یا حتی انیمیشن رو برعکس کنیم. خب حالا چطور یک کنترل مشخص در مورد انیمیشن داشته باشیم؟ جواب سادست : Animation Controller که از اسم اون هم مشخص هست ، روند کنترل انیمیشن رو بر عهده داره.
یعنی به طور مثال از طریق Animation Controller می توانیم انیمیشن ویجت رو در وسط متوقف یا برعکس اجرا کنیم و یا …
حالا بریم سراغ یک مثال جذاب
AnimationController _animationController;
AnimationController چندین پارامتر داره که یکی از آنها همان duration هست.
_animationController=AnimationController(duration: Duration(seconds: 5),vsync: this);
پارامتر دیگه vsync هست. این پارامتر به ما کنترل عملکرد انیمیشن رو میده. حالا چطور این vsync رو مقدار بدیم.
به کلاس قسمت State تایپ می کنیم.
with SingleTickerProviderStateMixin
یعنی داریم :
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin
حالا به vsync پارامتر this v رو پاس می دهیم. حالا بیایم ویجت مورد نظرمون رو پیاده سازی کنیم. ویجت RotationTransition رو پیاده سازی می کنیم.
RotationTransition(
alignment: Alignment.center,
turns: _animationController,
child: Container(
color: Colors.cyan,
width: 200,
height: 200,
),
),
طبیعتا برای کنترل انیمیشن هم یک button ایجاد می کنیم.
RaisedButton(
child: Text("toggle"),
onPressed: (){
if(_animationController.isAnimating){
_animationController.stop();
}else{
_animationController.repeat();
}
},
)
دقت کنید که initState حتما باید فراخوانی بشه تا AnimationController ما مقدار دهی بشه.
حالا اگر بیایم در تابع initState انیمیشن رو با تابع forward فراخوانی کنیم و برنامه رو ریست کنیم ، انیمیشن اجرا می شود.
_animationController.repeat();
یعنی داریم :
void initState() {
// TODO: implement initState
super.initState();
_animationController=AnimationController(duration: Duration(seconds: 5),vsync: this);
_animationController.forward();
}
حالا قصد داریم از طریق Button انیمیشن ویجت رو کنترل کنیم.
حالا میایم از طریق تابع onPressed یک شرط پیاده سازی می کنیم.
if(_animationController.isAnimating){
_animationController.stop();
}else{
_animationController.repeat();
}
خب در نهایت از طریق قطعه کد زیر ما میتونیم روی انیمیشن یک ویجت از طریق Animation Controller کنترل داشته باشیم.
import 'package:flutter/material.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyApp(),
);
}
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin{
AnimationController _animationController;
@override
void initState() {
// TODO: implement initState
super.initState();
_animationController=AnimationController(duration: Duration(seconds: 5),vsync: this);
_animationController.repeat();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
color: Colors.white,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RotationTransition(
alignment: Alignment.center,
turns: _animationController,
child: Container(
color: Colors.cyan,
width: 200,
height: 200,
),
),
RaisedButton(
child: Text("toggle"),
onPressed: (){
if(_animationController.isAnimating){
_animationController.stop();
}else{
_animationController.repeat();
}
},
)
],
),
),
),
);
}
}
برای مشاهده ویدئوی این درس می بایست در دوره ثبت نام نمائید.
در جلسه قبل بحث Animation Controller رو مورد بررسی قراردادیم و حال در این جلسه میخوایم چند تا انیمیشن رو با هم ترکیب کنیم و با استفاده از Animation Controller اونها رو مدیریت کنیم. قصد داریم یک دیالوگ رو با انیمیشن پیاده سازی کنیم. برای شروع یک Animation Controller ایجاد می کنیم.
AnimationController _animationController;
و در داخل تابع initState این Animation Controller رو پیاده سازی می کنیم.
void initState() {
// TODO: implement initState
super.initState();
_animationController =
AnimationController(duration: Duration(seconds: 2), vsync: this);
}
حالا برای ایجاد انیمیشن دیالوگ یک ویجت از نوع stateless به نام PopupWidget ایجاد می کنیم. در مرحله اول ، چندین انیمیشن پایه به نام های opacity , width , height , controller از نوع double ایجاد می کنیم.
class PopupWidget extends StatelessWidget {
Animation opacity;
Animation width;
Animation height;
Animation controller;
@override
Widget build(BuildContext context) {
return Container;
}
}
حالا این انیمیشن ها رو در تابع constructor مقداردهی می کنیم.
PopupWidget(this.controller) {
opacity = Tween(begin: 0, end: 1.0).animate(CurvedAnimation(
parent: controller, curve: Interval(0.0, 0.1, curve: Curves.ease)));
width = Tween(begin: 0, end: 300.0).animate(CurvedAnimation(
parent: controller, curve: Interval(0.125, 0.225, curve: Curves.ease)));
height = Tween(begin: 2, end: 300.0).animate(CurvedAnimation(
parent: controller, curve: Interval(0.250, 0.350, curve: Curves.ease)));
}
دقت کنید که انیمیشن ما به صورت Tween هست و به همین خاطر هست که در تابع constructor از Tween استفاده می کنیم.حالا اگر بخوایم رابط کاربری رو براساس کنترلر آپدیت کنیم ، صرفا Container برای ما جوابگو نیست و باید از یک AnimatedBuilder نیز استفاده کنیم.
مهمترین پارامترهای AnimatedBuilder یکی builder و دیگری animation هست. پارامتر animation همان کنترلی هست که داریم استفاده می کنیم.builder هم دو پارامتر اصلی context و یک ویجت به نام child رو داره.
حالا میتونیم Container رو ایجاد کنیم.
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Container(
)
}
);
اما چون قصد داریم Container ما با opacity کم و زیاد کنیم ، پس در نتیجه Container رو داخل ویجت opacity قرار می دهیم.
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Opacity(
opacity: opacity.value,
child: Container(
width: width.value,
height: height.value,
),
);
}
);
حالا میتونیم یک سری استایل رو به باکس موردنظرمون نسبت بدیم.
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Opacity(
opacity: opacity.value,
child: Container(
width: width.value,
height: height.value,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.all(Radius.circular(20)),
boxShadow: [
BoxShadow(
color: Colors.grey[500],
blurRadius: 20,
)
]),
child: Center(
child: Text(
"Welearn",
)),
),
);
},
);
برای مشاهده ویدئوی این درس می بایست در دوره ثبت نام نمائید.
حالا در این بخش قصد داریم یکی از جذاب ترین ویجت های فلاتر صحبت کنیم ، که به ما کمک میکنه انیمیشن ها رو بین دو صفحه منتقل کنیم. برای اینکار ویجتی داریم به نام Hero. این ویجت کمک میکنه که ما یک object رو با انیمیشن به صفحه دیگری منتقل کنیم.
انیمیشنهای Hero، یک عنصر مانند یک تصویر رو انتخاب میکنند. این تصویر از این پس Hero نامیده میشود و زمانی که گذار صفحه (معمولاً از طریق کلیک روی تصویر) آغاز میشود، این Hero به صفحه بعدی اپلیکیشن پرواز میکند. زمانی که کاربر دوباره به صفحه قبلی بازگردد، انیمیشن در جهت دیگر اجرا میشود و تصویر به محل قبلی خود بازمیگردد. ما در این مقاله نه تنها مبانی مقدماتی انیمیشنهای Hero را بررسی میکنیم، بلکه آن را سفارشیسازی نیز میکنیم.
ایجاد یک انیمیشن ساده Hero
انیمیشنهای Hero احتمالاً یکی از سادهترین انیمیشنهایی هستند که میتوان در فلاتر اجرا کرد و نیازمند تنظیمات زیادی نیستند. اگر نگاهی به تصویر متحرک نمونه فوق بیندازید میبینید که آیکون اپلیکیشن مربوطه در هر دو صفحه وجود دارد. تنها چیزی که برای پیادهسازی این انیمیشن نیاز داریم، این است که به فلاتر بگوییم این دو صفحه و جایگاه آیکون به هم لینک شدهاند.
نظرات دوره آموزش مجازی انیمیشن در فلاتر Flutter