مفاهیم مقدماتی فلاتر
در سطح اینترنت مقالات زیادی در مورد مفاهیم مقدماتی فلاتر وجود دارد که خیلی هم خوب و شفاف نوشته شدهاند. با این وجود مشکل اکثر آنها این است که برای مطالعهی این مقالات هم باید تا حدودی با فلاتر و زبان برنامهنویسی دارت آشنا باشید.
در این مقاله تلاش میکنیم تا جای ممکن از این مشکل جلوگیری کنیم. ما از پایه شروع میکنیم و سعی میکنیم به ترتیب تمامی مسائل مقدماتی را پشت سر بگذاریم. در طول این مقاله ما از تمامی ویجتهای پایه استفاده میکنیم، یک رابط کاربری منحصر به فرد خلق میکنیم، از ماژولهای Native استفاده خواهیم کرد و از اپلیکیشن ایجاد شده برای هر دو سیستم عامل اندروید و iOS خروجی میگیریم.
فلاتر چیست؟
فلاتر یک پلتفرم جدید ولی در عین حال پیشرو است و توانسته است توجه کمپانیهای متعددی که اخیرا نرمافزارهای خود را به بازار عرضه کردهاند جلب کند. به دلیل سادگی بالای این پلتفرم نسبت به وب اپلیکیشنها طرفداران زیادی دارد. همچنین سرعت بالاتر آن نسبت به اپلیکیشنهای Native نیز یکی دیگر از دلایل محبوب پلتفرم برنامه نویسی موبایل فلاتر است.
عملکرد و کارایی بالای فلاتر با کمک تکنیکهای مختلفی به دست آمده است:
- برخلاف دیگر پلتفرم های محبوب برنامه نویسی موبایل، فلاتر به هیچ عنوان از جاوا اسکریپت استفاده نمیکند و به جای زبان برنامه نویسی دارت را جایگزین کرده است. این زبان به کد باینری تبدیل خواهد شد. به همین خاطر است که برنامههای نوشته شده با فلاتر با همان عملکرد Native زبانهای C شئ گرا، سویفت، جاوا یا کوتلین هم اجرا میشود.
- فلاتر از اجزای رابط کاربری Native استفاده نمیکند. این موضوع ممکن است در نگاه اول به عنوان یک نقطه ضعف شناخته شود. با این وجود به دلیل اینکه این اجزا داخل خود فلاتر تعبیه شدهاند، هیچ لایه ارتباطی بین ظاهر برنامه و کد وجود ندارد. این موضوع باعث خواهد شد که بسته به گرافیک گوشی موبایل بازیها با بهترین سرعت ممکن اجرا شوند. در این حالت تمامی کلیدها، متنها، عناصر رسانهای و پس زمینهها توسط موتور گرافیکی فلاتر کشیده میشوند. همچنین لازم به ذکر است که این موضوع باعث کاهش حجم نرمافزار هم خواهد شد. برای مثال برنامهی «Hello World» در iOS فقط ۲٫۵ مگابایت و در اندروید فقط ۴ مگابایت است.
- فلاتر از روش اعلامی (declarative) برای ایجاد UI خود با کمک ویجتها استفاده میکند. برای اینکه ویجتها حداکثر کارایی خود را داشته باشند، فقط وقتی رندر خواهند شد که لازم باشد (معمولا وقتی که وضعیت آنها تغییر کند).
- علاوه بر تمامی موارد بالا، این پلتفرم برنامه نویسی موبایل از قابلیت Hot-Reload نیز پشتیبانی میکند. این قابلیت قبلا در فضای برنامه نویسی وب وجود داشت اما در پلتفرمهای Native موبایل امکان استفاده از آن وجود نداشت. این قابلیت به فریمورک فلاتر اجازه میدهد که به صورت خودکار درختچهی ویجت (Widget Tree) را بازسازی کند و شما تاثیر تغییراتی که اعمال کردهاید را مشاهده کنید.
زبان برنامه نویسی دارت
دارت (Dart) یک زبان برنامه نویسی است که ما در دارت از آن برای خلق نرم افزارهای مورد نیاز خودمان استفاده میکنیم. در صورتی که تجربه کار با جاوا یا جاوا اسکریپت را داشته باشید، یادگیری این زبان اصلا سخت نیست و به سرعت میتوانید آن را یاد بگیرید.
زبان برنامه نویسی دارت به عنوان یک زبان برنامه نویسی کلاینت محور برای پلتفرمهای مختلف طراحی شده است. دو نفر از برنامه نویسهای گوگل در سال ۲۰۱۱ اجازهی خلق این زبان برنامهنویسی را کسب کردند. هدف اولیه این دو خلق زبانی بود که بتوان با استفاده از آن هم برای ساخت اپلیکیشنهای موبایل و هم برنامههای دسکتاپ استفاده کرد. همچنین با وجود اینکه این موضوع به صورت واضح بیان نشده است اما به نظر میرسد که یکی از اهداف دارت، معرفی به عنوان جایگزین جاوا اسکریپت بوده است.
مزایای استفاده از دارت
برخلاف دیگر زبانهای برنامه نویسی دارت به شکلی طراحی شده است که پروسه طراحی و برنامه نویسی تا جای ممکن سریع و راحت باشد. به همین دلیل به همراه آن ابزارهای داخلی متعددی مثل پکیج منیجر، کامپایلرها و مترجمهای متنوع و… عرضه شده است. همچنین ماشین مجازی دارد و امکان ساخت برنامهی Just-In-Time به شما اجازه میدهد بلافاصله کدهای خودتان را تست کنید.
یکی از مزایای اصلی استفاده از دارت امکان کامپایل AOT (Ahead-of-Time) است. این ویژگی به شما اجازه میدهد کدهای نوشته شده را به زبان ماشین ترجمه کنید. در نتیجه میتوانید کدهای باینری را به صورت Native اجرا کنید.
سازگار با دنیای مدرن
علاوه بر اینکه این زبان خیلی منعطف است، استفاده از آن راحت میباشد و به خوبی هم با جاوا اسکریپت سازگار است، حتی در صورتی که در قسمتهای دیگری از کد هم خطایی وجود داشته باشد، با دارت میتوانید کدی که نوشتهاید را اجرا کنید. این موضوع به برنامه نویس ها اجازه میدهد بخش کوچکی از کد خود را صرف نظر از اینکه برنامه کامل شده است یا نه تست کنید.
سرعت بالا
دارت یک زبان برنامه نویسی سریع است. تقریبا در تمامی موارد برنامههایی که توسط دارت نوشته شدهاند با کمک یک کامپایلر منبع به منبع سریعتر کامپایل میشوند. همچنین میتوان انتظار داشت که این زبان برنامه نویسی رابط کاربری بهینهتری نسبت به دیگر زبانهای برنامه نویسی پیشرو داشته باشد.
چه افرادی از دارت استفاده میکنند؟
گوگل، Mews، Ag Flow و Blossom برخی از شرکتهایی هستند که از زبان برنامه نویسی دارت استفاده میکنند. به عقیده خیلی از مردم یادگیری دارت به دلیل شباهت آن به دیگر برنامههای برنامه نویسی سریعتر است. این زبان کاملا مدرن، کاربردی و منعطف است و با دیگر زبانهای برنامه نویسی نیز سازگاری دارد.
نصب فلاتر
در ادامه نحوهی نصب فلاتر روی سیستم عامل ویندوز را با هم یاد میگیریم. گامهای اولیه برای نصب فلاتر به شرح زیر هستند:
دانلود SDK فلاتر
- با مراجعه به این لینک (https://flutter.dev/docs/get-started/install/windows) آخرین نسخهی پایدار فلاتر را دانلود کنید.
- فایل دانلود شده را اکسترکت کنید و محتویات آن را در پوشهی دلخواه (برای مثال C:\src\flutter) قرار دهید.
نکته: فلاتر را در مسیرهایی که به دسترسی ویژه نیاز دارند (مثل C:\Program Files) قرار ندهید.
اکنون میتوانید دستورات فلاتر را در کنسول آن وارد کنید.
به روز رسانی مسیرها
در صورتی که میخواهید دستورات فلاتر را در کنسول ویندوز نیز اجرا کنید باید قدمهای زیر را دنبال کنید و فلاتر را به متغیر محیطی PATH اضافه کنید:
- در بخش جستجوی منوی استارت عبارت «env» را وارد کنید و گزینهی Edit environment variable for your account را انتخاب کنید
- در بخش User Variables به دنبال گزینهای با نام Path بگردید
- در صورتی که این گزینه وجود داشت، پس از وارد کردن یک عدد «;» آدرس «flutter\bin» را وارد کنید.
- در صورتی هم که این گزینه وجود نداشت یک متغیر جدید به نام Path ایجاد کنید و مسیر «flutter\bin» را به آن اختصاص دهید.
برای اینکه این تغییرات اعمال شوند باید تمامی کنسولهایی که باز هستند را ببندید و دوباره باز کنید.
اجرای دستور flutter doctor
در این مرحله باید به کمک یک کنسول که مسیر فلاتر برای آن تعریف شده است، قطعه کد زیر را اجرا کنید. این قطعه کد تمامی مواردی که ممکن است به آنها نیاز داشته باشید را به شما نمایش خواهد داد:
flutter doctor
این دستور پس از بررسی گزارشی از نصب فلاتر به شما نمایش خواهد داد. با دقت این گزارش را بررسی کنید تا مواردی که ممکن است برای نصب به آنها نیاز داشته باشید و یا قدمهای بعدی را متوجه شوید. (با متن بولد نمایش داده میشوند.)
برای مثال:
[-] Android toolchain - develop for Android devices
Android SDK at D:\Android\sdk
✗ Android SDK is missing command line tools; download from https://goo.gl/XxQghQ
Try re-installing or updating your Android SDK,
visit https://flutter.dev/setup/#android-setup for detailed instructions.
در ادامه در مورد تکمیل کردن هر کدام از این تسکها و اتمام پروسه نصب صحبت خواهیم کرد. پس از اینکه تمامی موارد موردنیاز را نصب کردید، میتوانید دوباره دستور Flutter Doctor را اجرا کنید تا مطمئن شوید تمامی موارد به درستی انجام شدهاند.
راه اندازی شبیهساز اندروید
نصب Android Studio
- اندروید استودیو را از اینجا (https://developer.android.com/studio) دانلود و نصب کنید.
- مراحل نصب را دنبال کنید و سپس آخرین نسخه SDK اندروید، ابزارهای خط فرمان اندروید و Android SDK Build Tools را که برای برنامه نویسی اندروید ضروری هستند را نصب کنید.
آمادهسازی محیط شبیه ساز اندروید
برای اجرا و تست نرمافزارهای خودتان روی شبیهساز اندروید گامهای زیر را دنبال کنید:
- قابلیت VM acceleration را روی سیستم خودتان فعال کنید.
- پس از اجرای اندروید استودیو روی آیکون AVD Manager کلیک کنید و گزینهی Create Virtual Device را انتخاب کنید.
- یکی از دستگاههای موجود را انتخاب کنید و گزینه Next را انتخاب کنید.
- در این مرحله باید یکی از ایمیجهای موجود را انتخاب کنید و با گزینه Next به مرحله بعدی بروید.
- در قسمت Emulated Performance گزینهی Hardware – GLES 2.0 را انتخاب کنید.
- پس از اینکه گزینههای انتخاب شده را بررسی کردید روی کلید Finish کلیک کنید.
- روی گزینهی Run در تولبار کلیک کنید. شبیهساز اجرا میشود و محیط پیشفرض را بسته به گوشی انتخابی و سیستم عامل آن به شما نمایش خواهد داد.
ساختار پروژه در فلاتر
یکی از مفاهیم مقدماتی فلاتر که لازم است قبل از شروع برنامه نویسی با آن آشنا باشید، ساختار پروژهی آن است. قبل از شروع لازم است نگاهی به پروژهای داشته باشیم که توسط فریمورک فلاتر ایجاد خواهد داشت. در این پروژه فایلهای زیر وجود دارد:
- Lib/– مثل مدیر پروژه دارت (pub) تمامی کدهای شما در این دایرکتوری قرار خواهد گرفت.
- yaml– لیستی از پکیجهای مورد نیاز برای اجرای نرمافزار داخل این فایل نگهداری خواهد شد. دقیقا مثل کاری که package.json انجام میدهد. به یاد داشته باشید که در پروژههای فلاتر نمیتوانید به صورت مستقیم از دستور pub استفاده کنید بلکه باید از دستور زیر استفاده کنید:
flutter pub get <package_name> - Test/– همانطور که از اسم آن مشخص است، فایلهای موجود در این دایرکتوری برای تست نرمافزار کاربرد دارند و وجود آنها ضروری است.
- دایرکتوریهای iOS و android– این دو دایرکتوری محل نگهداری کد اختصاصی هر پلتفرم از جمله آیکونها و تنظیمات اختصاصی مربوط به Premissonها هستند.
در حال حاضر نیازی نیست که در مورد فایلهای موجود در هر دایرکتوری اطلاعات بیشتری داشته باشیم. در حال حاضر لازم است به فولدر lib/ مراجعه کنیم و فایل main.dart را باز کنیم. همانطور که میتوان حدس زد این فایل محل شروع نرمافزار ما میباشد. درست مثل زبان برنامه نویسی C نرمافزاری که طراحی میکند با فراخوانی تابع main() اجرا خواهد شد.
ویجتها در فلاتر
یکی دیگر از مفاهیم مقدماتی فلاتر ویجتهای آن است. در فلاتر همه چیز بر مبنای ویجت است. عناصر رابط کاربری، استایلها، تمها و حتی وضعیت برنامه توسط ویجتهای خاص مدیریت خواهد شد. اجازه دهید برای آشنایی بیشتر با این بخش از فلاتر لازم است با نوشتن یک برنامه ساده، با برخی از ویجتهای پایه آن آشنا شویم:
اجرای اولین نرمافزار در فلاتر
برای نوشتن اولین نرم افزار در فلاتر باید از اندروید استودیو استفاده کنیم. پس از اجرای Android Studio، در صفحه خوشامدگویی گزینهی Create New Flutter Project را انتخاب کنید.
سپس گزینهی New Flutter Application را انتخاب کنید
در مرحله بعدی باید تنظیمات مربوط به نرم افزار فلاتر را انجام دهیم. در این مرحله تنظیمات پایه نرمافزار مثل اسم آن و محل پروژه را انتخاب کنید
در مرحله بعدی که آخرین مرحله است باید یک اسم منحصر به فرد برای پکیج خودتان انتخاب کنید. این اسم باید کاملا منحصر به فرد باشد و حتی مشابه اسم دیگر نرمافزارهای موجود در اپ استور و گوگل پلی نباشد.
روی کلید Finish کلیک کنید تا اندروید استودیو یک اپلیکیشن فلاتر جدید برای شما ایجاد کند. در این اپلیکیشن برخی کدهای ساده نیز وجود دارد:
کد پیشفرض فلاتر آمادهی اجرا است. در عکس بالا شما دو فلش را مشاهده میکنید. فلش اول به شبیهساز اندروید اشاره میکند. در صورتی که بیشتر از یک شبیهساز اندروید برای اندروید استودیو تعریف کرده باشید میتوانید هر کدام از آنها را برای تست نرم افزار خودتان انتخاب کنید. فلش دوم هم به کلید Run اشاره میکند. روی این کلید کلیک کنید تا نرمافزار شما ایجاد و روی شبیهساز انتخابی اجرا شود.
نوشتن نرمافزار Hello World در فلاتر
کدی که توسط خود فلاتر در زمان ایجاد پروژه نوشته میشود کد خوبی است و عملکرد مناسبی هم دارد. با این وجود ممکن است درک آن برای یک فرد مبتدی که با مفاهیم مقدماتی فلاتر آشنا نیست خیلی سخت باشد. شما میتوانید این کد را به صورت کامل حذف کنید و کد زیر را جایگزین آن کنید. فقط کافی است این کد را در فایل lib/main.dart کپی کنید:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Center(child: Text('My first app')),
),
body: Text('Hello World'),
)
),
);
}
این کد یک نرمافزار جدید در فلاتر با عنوان My First App و یک ویجت متنی با عنوان Hello World ایجاد خواهد کرد. در صورتی که کد بالا را در شبیه ساز اندروید اجرا کنید، ظاهر آن شبیه به تصویر زیر خواهد بود:
ویجت های بی حالت (Stateless)
اکنون که متوجه شدیم استفاده از ویجتها در فلاتر چقدر راحت است، قدم بعدی این است که ویجتهای اختصاصی خودمان را ایجاد کنیم. به صورت کلی دو نوع ویجت وجود دارد: ویجت بی حالت و ویجت حالت دار. البته تعداد آنها خیلی بیشتر است ولی برای مفاهیم مقدماتی فلاتر آشنایی با این دو کافی است.
در مثالهای قبلی ما از ویجتهای بی حالت (Stateless) استفاده کردیم. البته انتخاب این اسم بدین معنی نیست که این ویجتها کاملا بی حالت هستند. ویجتها در واقع کلاسهای دارت هستند که میتوانند با ویژگیهای اختصاصی تعریف شوند. تغییر این ویژگیها در ویجتهای بی حالت باعث تغییر مواردی که از قبل رندر شدهاند نخواهد شد اما در ویجتهای حالت دار به روز رسانی یک ویژگی باعث فعالسازی هوکهای چرخه زندگی آن ویجت خواهد شد و محتویات آن با وضعیت جدید به روز رسانی خواهد شد. دلیل انتخاب ویجتهای بی حالت برای شروع این است که استفاده از آنها راحتتر است.
برای ایجاد یک ویجت بی حالت به موارد زیر نیاز داریم:
- یک اسم مناسب برای کلاس موردنظر
- ارثبری (Extend) کلاس موردنظر از کلاس StatelessWidget
- قرار دادن متد build() داخل آن. این متد یک آرگومان از نوع BuildContext دریافت خواهد کرد و نتیجهای از نوع Widget بر میگرداند.
مثال:
class GreenFrog extends StatelessWidget {
const GreenFrog({ Key key }) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(color: const Color(0xFF2DBD3A));
}
}
بارگذاری مجدد خودکار (Hot Reload)
قابلیت Hot Reload یا بارگذاری مجدد به شما کمک خواهد کرد خیلی ساده و با سرعت بالا تغییرات موردنیاز خود را اعمال کنید و بدون نیاز به ریستارت کردن نرم افزار، این تغییرات را مشاهده کنید. با استفاده از این قابلیت پس از اینکه کدهای جدیدی به نرمافزار شما افزوده شود، فلاتر به صورت خودکار درختچهی ویجتها (Widget Tree) را بازسازی خواهد کرد و شما قادر خواهید بود نتیجه تغییرات خود را بلافاصله مشاهده کنید.
برای استفاده از این قابلیت در فلاتر کافی است نرم افزار خودتان را با فشار دادن کلیدهای Ctrl+S ذخیره کنید و یا کلیدی که در نوار ابزار با علامت جرقه مشخص شده است را فشار دهید.
ویجتهای Stateful در فلاتر
ویجت های با حالت یا Stateful خیلی ساده هستند و حتی از نوع بی حالت هم سادهترند. با این وجود یک تفاوت جزئی وجود دارد و آن این است که این ویجتها به خودی خود وجود ندارند و برای نگهداری حالت ویجت به یک کلاس اضافی نیاز دارید. علاوه بر این بخش بصری ویجت به وضعیت آن تبدیل خواهد شد.
قطعه کد زیر مثالی از ویجتهای بی حالت است:
class Counter extends StatefulWidget {
// The state is stored not in the widget, but in the specific class
// that is created by createState()
@override
State<Counter> createState() => _CounterState();
// The result of the function is an object, that must be
// of the type State<Counter> (where Counter is the name of our widget)
}
کد بالا یک ویجت خالی را ایجاد خواهد کرد که فقط حاوی یک متد است و هیچگونه وضعیت یا رابط کاربری خاصی داخل آن وجود ندارد. دلیل این موضوع هم بهینهسازی بهتر نرمافزار شما است.
عنصر حالت یا State نیز اصلا پیچیده نیست. در واقع این کلاس هم مانند ویجت بی حالت است و تنها تفاوت آنها کلاس والد یا Parent آن است.
class _CounterState extends State<Counter> {
// Finally, we can declare dynamic variables inside of our classes,
// to store the state of our widgets
// In this case, we'll store the number
int counter = 0;
// The rest is super simple, we just implement the familiar to us build() method,
// in the same way as we did it for our [StatelessWidget]
@override
Widget build(BuildContext context) {
// Almost nothing has changed since the last example.
// I've added comments to highlight the difference
return Center(
child: GestureDetector(
onTap: () {
// Once the button is tapped we increase the value of [counter] variable
setState(() {
// Using setState() is required to trigger lifecycle hooks
// so the widget will know that it should be updated
++counter;
});
},
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF17A2B8),
),
width: 80.0,
child: Center(
child: Text( // here we print the value of the [counter]
'$counter', // to see how it changes
style: TextStyle(fontSize: 30.0),
),
),
),
),
);
}
}
قطعه کد بالا یک شمارنده است که با لمس دایره مرکزی عدد روی آن تغییر خواهد کرد.
در صورتی که دقت کرده باشید اسم کلاس State با یک زیرخط شروع شده است. در زبان برنامه نویسی دارت تمامی نامهایی که با زیرخط شروع میشوند خصوصی هستند و برای فایلهای دیگر غیرقابل دسترسی میباشند. معمولا نیازی نیست که خارج از یک کتابخانهی خاص کلاسهای State قابل دسترسی باشند؛ به همین خاطر بهتر است آنها را به صورت خصوصی تعریف کنیم.
همانطور که مشاهده کردید استفاده از ویجتهای با حالت (Stateful Widget) خیلی ساده است.
البته قبل از اینکه آموزش مفاهیم مقدماتی فلاتر را به پایان برسانیم اجازه دهید با چند ویجت جالب دیگر هم آشنا شویم. اینبار نیازی نیست که تک تک خطوط را به شما آموزش دهیم، زیرا با استفاده از آموزشهای این مقاله به سادگی میتوانید بخش زیادی از کد را متوجه شوید:
import 'package:flutter/widgets.dart';
main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.ltr,
child: Container(
padding: EdgeInsets.symmetric(
vertical: 60.0,
horizontal: 80.0,
),
color: Color(0xFFFFFFFF),
child: Content(),
),
);
}
}
class Content extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Counter('Manchester United'),
Counter('Juventus'),
],
);
}
}
class Counter extends StatefulWidget {
final String _name;
Counter(this._name);
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int count = 0;
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(bottom: 10.0),
padding: EdgeInsets.all(4.0),
decoration: BoxDecoration(
border: Border.all(color: Color(0xFFFD6A02)),
borderRadius: BorderRadius.circular(4.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// [widget] is the property of the State class that stores
// the instance of the [StatefulWidget] ([Counter] in our case)
_CounterLabel(widget._name),
_CounterButton(
count,
onPressed: () {
setState(() {
++count;
});
},
),
],
),
);
}
}
class _CounterLabel extends StatelessWidget {
static const textStyle = TextStyle(
color: Color(0xFF000000),
fontSize: 26.0,
);
final String _label;
_CounterLabel(this._label);
@override
Widget build(BuildContext context) {
return Text(
_label,
style: _CounterLabel.textStyle,
);
}
}
class _CounterButton extends StatelessWidget {
final count;
final onPressed;
_CounterButton(this.count, {@required this.onPressed});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPressed,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 6.0),
decoration: BoxDecoration(
color: Color(0xFFFD6A02),
borderRadius: BorderRadius.circular(4.0),
),
child: Center(
child: Text(
'$count',
style: TextStyle(fontSize: 20.0),
),
),
),
);
}
}
در قطعه کد بالا از ویجتهای Column() و Row() برای تغییر ظاهر نرمافزار استفاده شده است. ویجت Column معمولا برای تعریف ستون و ویجت Row نیز برای تعریف ردیف و ویژگیهای آن مورد استفاده قرار میگیرد. در مقالات بعدی بیشتر راجع به این دو ویجت و کاربردهای هر کدام صحبت خواهیم کرد.
سخن نهایی
امیدواریم مطالعه این مقاله در درک بهتر مفاهیم مقدماتی فلاتر به شما کمک کرده باشد. در صورتی که سوالی در این مورد دارید، میتوانید از طریق بخش نظرات همین مطلب با ما در ارتباط باشید، کارشناسان مکتبخونه آماده هستند که به تمامی سوالات شما در اسرع وقت پاسخ دهند.