الثلاثاء، 3 نوفمبر 2020

Flutter - Accessing REST API الوصول والتعامل مع مكتبات API في فلاطر الرفرفة

 Flutter - Accessing REST API الوصول والتعامل مع مكتبات API في فلاطر الرفرفة

Flutter - Accessing REST API الوصول والتعامل مع مكتبات API في فلاطر الرفرفة

Flutter - Accessing REST API الوصول والتعامل مع مكتبات API في فلاطر الرفرفة 

يوفر Flutter حزمة http لاستخدام موارد HTTP. http هي مكتبة تستند إلى المستقبل وتستخدم ميزات الانتظار وغير المتزامن. يوفر العديد من الأساليب عالية المستوى ويبسط تطوير تطبيقات الهاتف المحمول القائمة على REST.

مفاهيم أساسية

توفر حزمة http فئة عالية المستوى و http للقيام بطلبات الويب.

  • توفر فئة http وظائف لتنفيذ جميع أنواع طلبات HTTP.

  • تقبل طرق http عنوان url ومعلومات إضافية من خلال Dart Map (بيانات النشر والعناوين الإضافية وما إلى ذلك). يطلب الخادم ويجمع الاستجابة مرة أخرى في نمط غير متزامن / انتظار. على سبيل المثال ، يقرأ الكود أدناه البيانات من عنوان url المحدد ويطبعها في وحدة التحكم.

print(await http.read('https://flutter.dev/'));

بعض الدوال الأساسية هي كما يلي -

  • Reaf - اطلب عنوان url المحدد من خلال طريقة GET وأعد الاستجابة كـ Future <String>

  • get - اطلب عنوان url المحدد من خلال طريقة GET وأعد الاستجابة كـ Future <Response>. الاستجابة هي فئة تحتوي على معلومات الاستجابة.

  • post - اطلب عنوان url المحدد من خلال طريقة POST عن طريق نشر البيانات المقدمة وإعادة الرد على أنه مستقبلي <استجابة>

  • Put - اطلب عنوان url المحدد من خلال طريقة PUT وأعد الاستجابة كـ Future <Response>

  • head - اطلب عنوان url المحدد من خلال طريقة HEAD وأعد الاستجابة كـ Future <Response>

  • Delete - اطلب عنوان url المحدد من خلال طريقة DELETE وأعد الاستجابة كـ Future <Response>

يوفر http أيضًا فئة عميل كلاينت HTTP قياسية أكثر ، عميل. يدعم العميل الاتصال المستمر. سيكون مفيدًا عند تقديم الكثير من الطلبات إلى خادم معين. يجب إغلاقه بشكل صحيح باستخدام طريقة الإغلاق. خلاف ذلك ، فهو مشابه لفئة http. رمز عينة كما يلي -

var client = new http.Client(); 
try { 
   print(await client.get('https://flutter.dev/')); 
} 
finally { 
   client.close(); 
}


الوصول إلى واجهة برمجة تطبيقات خدمة المنتج

دعنا ننشئ تطبيقًا بسيطًا للحصول على بيانات المنتج من خادم ويب ثم نعرض المنتجات باستخدام ListView .

  • قم بإنشاء تطبيق Flutter جديد في Android studio ، product_rest_app .

  • استبدل رمز بدء التشغيل الافتراضي (main.dart) بكود product_nav_app الخاص بنا .

  • انسخ مجلد الأصول من product_nav_app إلى product_rest_app وأضف الأصول داخل ملف pubspec.yaml.

flutter: 
   assets: 
      - assets/appimages/floppy.png 
      - assets/appimages/iphone.png 
      - assets/appimages/laptop.png 
      - assets/appimages/pendrive.png 
      - assets/appimages/pixel.png 
      - assets/appimages/tablet.png

  • قم بتكوين حزمة http في ملف pubspec.yaml كما هو موضح أدناه -


dependencies: 
   http: ^0.12.0+2
  • هنا ، سوف نستخدم أحدث إصدار من حزمة http. سيرسل Android studio تنبيهًا بالحزمة بأنه تم تحديث pubspec.yaml.

احدث اصدار
  • انقر فوق خيار الحصول على التبعيات. سيحصل Android studio على الحزمة من الإنترنت ويقوم بتكوينها بشكل صحيح للتطبيق.

  • استيراد حزمة http في ملف main.dart -

import 'dart:async'; 
import 'dart:convert'; 
import 'package:http/http.dart' as http;
  • قم بإنشاء ملف JSON جديد ، products.json بمعلومات المنتج كما هو موضح أدناه -

[ 
   { 
      "name": "iPhone", 
      "description": "iPhone is the stylist phone ever", 
      "price": 1000, 
      "image": "iphone.png" 
   }, 
   { 
      "name": "Pixel", 
      "description": "Pixel is the most feature phone ever", 
      "price": 800, 
      "image": "pixel.png"
   }, 
   { 
      "name": "Laptop", 
      "description": "Laptop is most productive development tool", 
      "price": 2000, 
      "image": "laptop.png" 
   }, 
   { 
      "name": "Tablet", 
      "description": "Tablet is the most useful device ever for meeting", 
      "price": 1500, 
      "image": "tablet.png" 
   }, 
   { 
      "name": "Pendrive", 
      "description": "Pendrive is useful storage medium", 
      "price": 100, 
      "image": "pendrive.png" 
   }, 
   { 
      "name": "Floppy Drive", 
      "description": "Floppy drive is useful rescue storage medium", 
      "price": 20, 
      "image": "floppy.png" 
   } 
]

  • قم بإنشاء مجلد جديد ، JSONWebServer ووضع ملف JSON ، products.json.

  • قم بتشغيل أي خادم ويب باستخدام JSONWebServer كدليل جذر له واحصل على مسار الويب الخاص به. على سبيل المثال ، http://192.168.184.1:8000/products.json. يمكننا استخدام أي خادم ويب مثل apache و nginx وما إلى ذلك ،

  • أسهل طريقة هي تثبيت تطبيق خادم http المستند إلى العقدة. اتبع الخطوات الواردة أدناه لتثبيت وتشغيل تطبيق خادم http

    • تثبيت تطبيق Nodejs ( nodejs.org )

    • انتقل إلى مجلد JSONWebServer.

cd /path/to/JSONWebServer
  • قم بتثبيت حزمة خادم HTTP باستخدام npm.

npm install -g http-server
  • الآن ، قم بتشغيل الخادم.

http-server . -p 8000 

Starting up http-server, serving . 
Available on: 
   http://192.168.99.1:8000
   http://127.0.0.1:8000 
   Hit CTRL-C to stop the server
  • قم بإنشاء ملف جديد ، Product.dart في مجلد lib وانقل فئة المنتج إليه.

  • اكتب مُنشئ مصنع في فئة المنتج ، Product.fromMap لتحويل خريطة البيانات المعينة إلى كائن المنتج. عادةً ، سيتم تحويل ملف JSON إلى كائن Dart Map ثم تحويله إلى كائن ذي صلة (المنتج).

factory Product.fromJson(Map<String, dynamic> data) {
   return Product(
      data['name'],
      data['description'], 
      data['price'],
      data['image'],
   );
}
  • الكود الكامل للمنتج هو كما يلي -

class Product {
   final String name; 
   final String description;
   final int price;
   final String image; 
   
   Product(this.name, this.description, this.price, this.image); 
   factory Product.fromMap(Map<String, dynamic> json) { 
      return Product( 
         json['name'], 
         json['description'], 
         json['price'], 
         json['image'], 
      );
   }
}
  • اكتب دالتين - parseProducts و fetchProducts - في الفئة الرئيسية لجلب معلومات المنتج وتحميلها من خادم الويب إلى كائن List <Product>.

List<Product> parseProducts(String responseBody) { 
   final parsed = json.decode(responseBody).cast<Map<String, dynamic>>(); 
   return parsed.map<Product>((json) =>Product.fromJson(json)).toList(); 
} 
Future<List<Product>> fetchProducts() async { 
   final response = await http.get('http://192.168.1.2:8000/products.json'); 
   if (response.statusCode == 200) { 
      return parseProducts(response.body); 
   } else { 
      throw Exception('Unable to fetch products from the REST API');
   } 
}
  • لاحظ النقاط التالية هنا -

    • يستخدم المستقبل في التحميل البطيء لمعلومات المنتج. التحميل الكسول هو مفهوم لتأجيل تنفيذ الكود حتى يكون ضروريًا.

    • يستخدم http.get لجلب البيانات من الإنترنت.

    • يتم استخدام json.decode لفك تشفير بيانات JSON في كائن Dart Map. بمجرد فك تشفير بيانات JSON ، سيتم تحويلها إلى قائمة <المنتج> باستخدام fromMap من فئة المنتج.

    • في فئة MyApp ، أضف متغير عضو جديد ومنتجات من النوع Future <Product> وقم بتضمينه في المُنشئ هو المشي. 

class MyApp extends StatelessWidget { 
   final Future<List<Product>> products; 
   MyApp({Key key, this.products}) : super(key: key); 
   ...
  • في فئة MyHomePage ، أضف منتجات متغيرة عضو جديدة من النوع Future <Product> وقم بتضمينها في المُنشئ. أيضًا ، قم بإزالة متغير العناصر وطريقته ذات الصلة ، استدعاء طريقة getProducts. وضع متغير المنتجات في المُنشئ. سيسمح بجلب المنتجات من الإنترنت مرة واحدة فقط عند بدء تشغيل التطبيق لأول مرة.


class MyHomePage extends StatelessWidget { 
   final String title; 
   final Future<ListList<Product>> products; 
   MyHomePage({Key key, this.title, this.products}) : super(key: key); 
   ...
  • قم بتغيير خيار الصفحة الرئيسية (MyHomePage) في طريقة إنشاء أداة MyApp في flutter لاستيعاب التغييرات المذكورة أعلاه -



  • home: MyHomePage(title: 'Product Navigation demo home page', products: products),
  • قم بتغيير الدالة الرئيسية لتضمين وسيطات <Product> المستقبلية -

void main() => runApp(MyApp(fetchProduct()));
  • قم بإنشاء عنصر واجهة مستخدم جديد ، ProductBoxList لإنشاء قائمة المنتجات في الصفحة الرئيسية.


class ProductBoxList extends StatelessWidget { 
   final List<Product> items;
   ProductBoxList({Key key, this.items}); 
   
   @override 
   Widget build(BuildContext context) {
      return ListView.builder(
         itemCount: items.length,
         itemBuilder: (context, index) {
            return GestureDetector(
               child: ProductBox(item: items[index]), 
               onTap: () {
                  Navigator.push(
                     context, MaterialPageRoute(
                        builder: (context) =gt; ProductPage(item: items[index]), 
                     ), 
                  ); 
               }, 
            ); 
         }, 
      ); 
   } 
}

لاحظ أننا استخدمنا نفس المفهوم المستخدم في تطبيق التنقل لسرد المنتج باستثناء أنه مصمم كعنصر واجهة مستخدم منفصل عن طريق تمرير المنتجات (الكائن) من النوع List <Product>.

  • أخيرًا ، قم بتعديل طريقة إنشاء عنصر واجهة مستخدم MyHomePage للحصول على معلومات المنتج باستخدام الخيار Future بدلاً من استدعاء الطريقة العادية.

Widget build(BuildContext context) { 
   return Scaffold(
      appBar: AppBar(title: Text("Product Navigation")),
      body: Center(
         child: FutureBuilder<List<Product>>(
            future: products, builder: (context, snapshot) {
               if (snapshot.hasError) print(snapshot.error); 
               return snapshot.hasData ? ProductBoxList(items: snapshot.data)
               
               // return the ListView widget : 
               Center(child: CircularProgressIndicator()); 
            }, 
         ), 
      )
   ); 
}

  • لاحظ هنا أننا استخدمنا عنصر واجهة المستخدم FutureBuilder لعرض الأداة. سيحاول FutureBuilder جلب البيانات من الخاصية المستقبلية (من النوع Future <List <Product>>). إذا قامت الخاصية المستقبلية بإرجاع البيانات ، فسوف تعرض الأداة باستخدام ProductBoxList ، وإلا ستُلقي بخطأ.

  • الكود الكامل لـ main.dart هو كما يلي -

import 'package:flutter/material.dart'; 
import 'dart:async'; 
import 'dart:convert'; 
import 'package:http/http.dart' as http; 
import 'Product.dart'; 

void main() => runApp(MyApp(products: fetchProducts())); 

List<Product> parseProducts(String responseBody) { 
   final parsed = json.decode(responseBody).cast<Map<String, dynamic>>(); 
   return parsed.map<Product>((json) => Product.fromMap(json)).toList(); 
} 
Future<List<Product>> fetchProducts() async { 
   final response = await http.get('http://192.168.1.2:8000/products.json'); 
   if (response.statusCode == 200) { 
      return parseProducts(response.body); 
   } else { 
      throw Exception('Unable to fetch products from the REST API'); 
   } 
}
class MyApp extends StatelessWidget {
   final Future<List<Product>> products; 
   MyApp({Key key, this.products}) : super(key: key); 
   
   // This widget is the root of your application. 
   @override 
   Widget build(BuildContext context) {
      return MaterialApp(
         title: 'Flutter Demo', 
         theme: ThemeData( 
            primarySwatch: Colors.blue, 
         ), 
         home: MyHomePage(title: 'Product Navigation demo home page', products: products), 
      ); 
   }
}
class MyHomePage extends StatelessWidget { 
   final String title; 
   final Future<List<Product>> products; 
   MyHomePage({Key key, this.title, this.products}) : super(key: key); 
   
   // final items = Product.getProducts();
   @override 
   Widget build(BuildContext context) { 
      return Scaffold(
         appBar: AppBar(title: Text("Product Navigation")), 
         body: Center(
            child: FutureBuilder<List<Product>>(
               future: products, builder: (context, snapshot) {
                  if (snapshot.hasError) print(snapshot.error); 
                  return snapshot.hasData ? ProductBoxList(items: snapshot.data) 
                  
                  // return the ListView widget : 
                  Center(child: CircularProgressIndicator()); 
               },
            ),
         )
      );
   }
}
class ProductBoxList extends StatelessWidget {
   final List<Product> items; 
   ProductBoxList({Key key, this.items}); 
   
   @override 
   Widget build(BuildContext context) {
      return ListView.builder(
         itemCount: items.length, 
         itemBuilder: (context, index) { 
            return GestureDetector( 
               child: ProductBox(item: items[index]), 
               onTap: () { 
                  Navigator.push(
                     context, MaterialPageRoute( 
                        builder: (context) => ProductPage(item: items[index]), 
                     ), 
                  ); 
               }, 
            ); 
         }, 
      ); 
   } 
} 
class ProductPage extends StatelessWidget { 
   ProductPage({Key key, this.item}) : super(key: key); 
   final Product item; 
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text(this.item.name),), 
         body: Center( 
            child: Container(
               padding: EdgeInsets.all(0), 
               child: Column( 
                  mainAxisAlignment: MainAxisAlignment.start, 
                  crossAxisAlignment: CrossAxisAlignment.start, 
                  children: <Widget>[
                     Image.asset("assets/appimages/" + this.item.image), 
                     Expanded( 
                        child: Container( 
                           padding: EdgeInsets.all(5), 
                           child: Column( 
                              mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                              children: <Widget>[ 
                                 Text(this.item.name, style: 
                                    TextStyle(fontWeight: FontWeight.bold)), 
                                 Text(this.item.description), 
                                 Text("Price: " + this.item.price.toString()), 
                                 RatingBox(), 
                              ], 
                           )
                        )
                     ) 
                  ]
               ), 
            ), 
         ), 
      ); 
   } 
}
class RatingBox extends StatefulWidget { 
   @override 
   _RatingBoxState createState() =>_RatingBoxState(); 
} 
class _RatingBoxState extends State<RatingBox> { 
   int _rating = 0; 
   void _setRatingAsOne() {
      setState(() { 
         _rating = 1; 
      }); 
   }
   void _setRatingAsTwo() {
      setState(() {
         _rating = 2; 
      }); 
   }
   void _setRatingAsThree() { 
      setState(() {
         _rating = 3; 
      }); 
   }
   Widget build(BuildContext context) {
      double _size = 20; 
      print(_rating); 
      return Row(
         mainAxisAlignment: MainAxisAlignment.end, 
         crossAxisAlignment: CrossAxisAlignment.end, 
         mainAxisSize: MainAxisSize.max, 
         
         children: <Widget>[
            Container(
               padding: EdgeInsets.all(0), 
               child: IconButton( 
                  icon: (
                     _rating >= 1 
                     ? Icon(Icons.star, ize: _size,) 
                     : Icon(Icons.star_border, size: _size,)
                  ), 
                  color: Colors.red[500], onPressed: _setRatingAsOne, iconSize: _size, 
               ), 
            ), 
            Container(
               padding: EdgeInsets.all(0), 
               child: IconButton(
                  icon: (
                     _rating >= 2 
                     ? Icon(Icons.star, size: _size,) 
                     : Icon(Icons.star_border, size: _size, )
                  ), 
                  color: Colors.red[500], 
                  onPressed: _setRatingAsTwo, 
                  iconSize: _size, 
               ), 
            ), 
            Container(
               padding: EdgeInsets.all(0), 
               child: IconButton(
                  icon: (
                     _rating >= 3 ? 
                     Icon(Icons.star, size: _size,)
                     : Icon(Icons.star_border, size: _size,)
                  ), 
                  color: Colors.red[500], 
                  onPressed: _setRatingAsThree, 
                  iconSize: _size, 
               ), 
            ), 
         ], 
      ); 
   } 
}
class ProductBox extends StatelessWidget {
   ProductBox({Key key, this.item}) : super(key: key); 
   final Product item; 
   
   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), height: 140, 
         child: Card(
            child: Row( 
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
               children: <Widget>[
                  Image.asset("assets/appimages/" + this.item.image), 
                  Expanded( 
                     child: Container( 
                        padding: EdgeInsets.all(5), 
                        child: Column( 
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                           children: <Widget>[ 
                              Text(this.item.name, style:TextStyle(fontWeight: FontWeight.bold)), 
                              Text(this.item.description), 
                              Text("Price: " + this.item.price.toString()), 
                              RatingBox(), 
                           ], 
                        )
                     )
                  )
               ]
            ), 
         )
      ); 
   } 
}

أخيرًا قم بتشغيل التطبيق لمعرفة النتيجة. سيكون هو نفسه مثال التنقل الخاص بنا باستثناء البيانات من الإنترنت بدلاً من البيانات المحلية الثابتة التي تم إدخالها أثناء تشفير التطبيق.






التسميات: