آموزش فلاتر ( Flutter ) - ارتباط با سرور Dio


https://files.virgool.io/upload/users/35634/posts/oljoppi6tbvw/zwpemtw9rrtj.png

چند وقتی میشه با فلاتر آشنا شدم. نسبت به تجربه‌ای که تو اندروید داشتم طراحی UI تو فلاتر خیلی آسون‌تره واسه همین تصمیم گرفتم یه پروژه با فلاتر درست کنم و تصمیم دارم پروژمو این جا با شما به اشتراک بزارم. تو این مجموعه از پست‌ها چند تا از مهم‌ترین موضوعات فلاتر رو باهم بررسی می‌کنیم و در نهایت یک پروژه کامل رو به پایان می‌رسونیم. این پست چهارمین پست از این سری هستش و در اون به ارتباط با سرور با استفاده کتابخونه Dio می‌پردازیم. برای دیدن قسمت قبل می‌تونید به این لینک مراجعه کنید.

مطالبی که تو این مجموعه یاد می‌گیریم به چند قسمت تقسیم میشن:


ساخت سرور فیک

خوب قبل از این که یه راست بریم سراغ کتابخونه Dio باید یه سرور داشته باشیم که بتونیم داده‌هارو از روی اون بخونیم. تو این اپ ما از سرور فیک JSONPlaceholder استفاده کردیم. این سایت به ما این امکان رو میده که از API ها از پیش آماده شدش استفاده کنیم، علاوه بر اون با استفاده از یک فایل Json این امکان رو به ما میده که سرور API فیک خودمون رو هم بسازیم. از طریق این لینک می‌تونید به قسمتی برید که آموزش میده سرور فیک خودتون رو بسازید. در این پروژه هم از این فایل json به عنوان منبع سرور فیک استفاده شده. بیشتر از این توضیحی نمیدیم و میریم سر اصل مطلب. اگر خواستید می‌تونید دایکیومنت‌های سایت رو بررسی کنید و سرورفیک خودتون رو بسازید.


کتابخانه Dio

کتابخانه Dio قابلیت‌های زیادی از جمله پشتیبانی از Interceptor ها، تنظیمات(پیکربندی) سراسری، کنسل کردن درخواست‌ها، دانلود فایل‌ها و ... داره. توضیحات خود کتابخونه Dio:

A powerful Http client for Dart, which supports Interceptors, Global configuration, FormData, Request Cancellation, File downloading, Timeout etc.

واسه استفاده از این کتابخونه اول باید اون رو به پروژه اضافه کنید. پس کد زیر رو به فایل pubspec.yaml اضافه کنید.

dependencies:
        dio: 3.x.x  #latest version

دستور flutter pub get رو اجرا کنید و دیگه همه چیز آمادس.


متدهای GET , POST , PUT , DELETE

متد GET

کد زیر یک نمونه ساده از دریافت داده با استفاده از متد GET هستش.

import 'package:dio/dio.dart';
void getHttp() async {
  try {
      Dio dio= Dio();
      Response response = await dio.get(&quothttp://www.google.com&quot);
      print(response);
  } catch (e) {
      print(e)
  }
}

در کد بالا یه در خواست با متد GET ارسال شده که نمونه Dio درخواست رو ارسال میکنه و منتظر میمونه تا جوابو دریافت کنه و بعد جواب رو تو کنسول چاپ میکنه. و همچنین سایت google در اولین پارمتر به عنوان url برای متد فرستاده میشه.


متد POST

از متد POST زمانی استفاده می‌کنیم که به عنوان مثال بخوایم چیزی رو برای سرور ارسال کنیم.

import 'package:dio/dio.dart';
void postHttp() async {
  try {
      Dio dio= Dio();
      Response response = dio.post(&quoturl.com&quot, data: {&quotid&quot: 12, &quotname&quot: &quotwendu&quot});
      print(response);
  } catch (e) {
      print(e)
  }
}

تو این متد از روش پست استفاده کردیم و علاوه بر آدرس پارامتر data رو هم برای Dio ارسال کردیم. این پارامتر همون داده‌هایی هست که تو متدها POST و PUT برای API خودمون ارسال می‌کنیم. اگه از نرم‌افزار postman برای تست استفاده کرده باشین میتونین این پارامتر‌هارو تو تب body اضافه کنید.


متد PUT

متد PUT معمولا زمانی استفاده میشه که می‌خواید مقادیری رو اپدیت کنید.

import 'package:dio/dio.dart';
void putHttp() async {
  try {
      Dio dio= Dio();
      Response response = dio.put(&quoturl.com&quot, data: {&quotid&quot: 12, &quotname&quot: &quotwendu&quot});
      print(response);
  } catch (e) {
      print(e)
  }
}

این متد هم دقیقا مثل متد POST نوشته میشه.


متد DELETE

زمانی استفاده میشه که بخوایم یه مقدار رو حذف کنیم.

import 'package:dio/dio.dart';
void deleteHttp() async {
  try {
      Dio dio= Dio();
      Response response = dio.delete(&quoturl.com/2&quot);
      print(response);
  } catch (e) {
      print(e)
  }
}

تو این مثال درواقع داریم آیتمی با id دو رو حذف می‌کنیم. خوب تا اینجا که به نظرم کار خیلی ساده بود.


رسیدگی به Error ها

ارور ها و اکسپشن ها مربوط به Dio با استفاده از کلاس DioError مورد بررسی قرار میگره. این به این معنیه که وقتی اررور یا اکسپشنی اتفاق میوفته در عبارت try-catch، یه ابجکت DioError پرتاپ میشه و ما با استفاده از catch اون رو مورد بررسی قرار میدیم. به تیکه کد زیر توجه کنید.

try {
    //404
    await dio.get(&quothttps://wendux.github.io/xsddddd&quot);
} on DioError catch(e) {
    if(e.response) {
        print(e.response.data)
        print(e.response.headers)
        print(e.response.request)
    } else{
        print(e.request)
        print(e.message)
    }
}

در کد بالا ما ابجک DioError رو گرفتیم و داده ها داخلش رو پرینت کردیم شما هم میتونید این کار رو انجام بدید. اما یه نکته مهم درمورد اررور ها وجود داره. اررور ها به دو دسته تقسیم میشن:

  • ارور های سیستمی
  • ارور های response(پاسخ)

از ارورای سیستمی میشه به خاموش بود کانکشن های اینترنت (مثل WiFi و دیتا) یا قطع بودن اینترنت اشاره کرد و ....

و از اررور های پاسخ میشه به اررور 404 اشارده کرد( در واقع Dio هر ریسپانسی که status کدش بین 200 تا 300 نباشه رو به صورت یک اررور برمیگردونه- البته این اپشین قابل تنظیمه).


کلاس Request Option

این کلاس زمانی استفاده میشه که میخواید با استفاده از Dio یه درخواستی رو ارسال کنید. کار با این کلاس خیلی مهمه و با استفاده ازش میتونید درخواست هاتون رو شخصی سازی کنید. این کلاس از متغیر هایی زیر تشکیل شده

Request Option
{
/// Http method.
String method;

/// Request base url, it can contain sub path, like: &quothttps://www.google.com/api/&quot.
String baseUrl;

/// Http request headers.
Map<String, dynamic> headers;

/// Timeout in milliseconds for opening  url.
int connectTimeout;

///  Whenever more than [receiveTimeout] (in milliseconds) passes between two events from response stream, [Dio] will throw the [DioError] with [DioErrorType.RECEIVE_TIMEOUT].
int receiveTimeout;

/// Request data, can be any type.
T data;

/// path will be added to base url
String path=&quot&quot

/// request content type
String contentType;

/// request Response type
ResponseType responseType;

  /// `validateStatus` defines whether the request is successful
  ValidateStatus validateStatus;
  
  Map<String, dynamic> extra;
  
  /// Common query parameters
  Map<String, dynamic /*String|Iterable<String>*/ > queryParameters;  
  }


توضیحات مختصری درمورد هرکدوم از مقادیر بالا:

  • متغیر Method: به عنوان نوع نوع درخواست http میشه ازش استفاده کرد. (Post, Put, Delete,Get)
  • متغیر BaseUrl: به عنوان آدرس اصلی استفاده میشه. مثلا اگه دامنه Api هاتون مشخصه میشه این متغیر رو ثابت تعریف کنید.
  • متغیر Headers: که با استفاده از یه Map میتونید هدر های درخواستتون رو مشخص کنید.
  • متغیر connectTimeout و receiveTimeout: اولی برای تا زمان اتمام برقراری ارتباط و دومی برای زمان ارتباط دریافت جواب هستش.
  • و مواردی که کاملا مشخص هستند و خودتون میتونید مطالعه کنید و از بحث این آموزش خارجه.

با استفاده از این کلاس میتونید درخواست هاتون رو شخصی سازی کنید. همچنین میتونید یه ابجکت ثابت برای همه ی درخواست هاتون استفاده کنید. برای اینکار میتونید یه requestOption درست کنید و به وقتی دارین ابجکت Dio رو میسازید به این ابجکت پاسش کنید. به کد زیر نگاه کنید.


استفاده از Dio در این پروژه

ما در این پروژه از Dio استفاده کردیم، و یه کلاس به اسم سرویس درست کردیم تا کارهای مربوط به ارتباط با سرور مون رو اونجا پیاده سازی کنیم. میتونید به آدرس زیر برید و با توجه به چیز هایی که یاد گرفتید کد مربوط به پیاده سازی Api رو مطاله کنید.

https://github.com/payam-zahedi/featured-food-sample/blob/master/lib/api/service.dart

لینک کلاس


تو کلاس بالا مهم ترین بخش، متد دریافت اطلاعات مربوط به غذاهاست. اگر به کد زیر نگاه کنید متوجه میشید که با استفاده از متد GET، یه درخواست به فایل /db ارسال کردیم و بعد از پارس کردن json یه لیستی از غذا هارو برمیگردونیم.

static Future<FoodList> fetchData() async {
  try {
    Response response = await getInstance().get(&quot/db&quot);
    if (response.statusCode == 200) {
      // If server returns an OK response, parse the JSON.
      var jsonString = json.encode(response.data);
      Map userMap = jsonDecode(jsonString);
      return FoodList.fromJson(userMap);
    } else {
      // If that response was not OK, throw an error.
      throw DioError(message :'exception throwed in Try');
    }
  } on DioError catch(e) {
      throw Exception(&quotSomeThing is Wrong: \n ${_checkError(e)}&quot);
  }
}


اگر پیشهاد، انتقاد یا نظری دارید خوشحال میشم که با به اشتراک گزاشتنش باعث بحبود عملکردم بشید ممنون.


منابع

https://pub.dev/packages/dio

https://medium.com/flutter-community/dio-interceptors-in-flutter-17be4214f363

https://blog.usejournal.com/flutter-http-requests-with-dio-rxdart-and-bloc-da325ca5fe33