دسترسی به REST API در برنامه نویسی فلاتر
دسترسی به REST API در برنامه نویسی فلاتر
در این درس از آموزش های برنامه نویسی سایت سورس باران، در مورد دسترسی به REST API در برنامه نویسی فلاتر صحبت می کنیم.
فلاتر پکیج http را برای مصرف منابع HTTP فراهم می کند. http یک کتابخانه مبتنی بر آینده است و از ویژگی های انتظار و همگام سازی استفاده می کند. این روش های سطح بالایی را فراهم می کند و توسعه برنامه های تلفن همراه مبتنی بر REST را ساده می کند.
مفاهیم کلی
پکیج http یک کلاس سطح بالا و http را برای انجام درخواست های وب فراهم می کند.
کلاس http قابلیت انجام انواع درخواست های HTTP را فراهم می کند.
متد های http یک آدرس اینترنتی و اطلاعات اضافی را از طریق نقشه دارت می پذیرند (داده های ارسال، عناوین اضافی و غیره.). از سرور درخواست می کند و پاسخ را با الگوی async/await جمع می کند. به عنوان مثال، کد زیر داده ها را از آدرس اینترنتی مشخص شده خوانده و آنها را در کنسول چاپ می کند.
1 |
print(await http.read('https://flutter.dev/')); |
برخی از متد های اصلی به شرح زیر است –
- url -read مشخص شده را از طریق روش GET درخواست کنید و پاسخ را به عنوان <Future <String برگردانید
- url -get مشخص شده را از طریق روش GET درخواست کنید و پاسخ را به عنوان <Future <Response برگردانید. پاسخ کلاسی است که اطلاعات پاسخ را در خود نگه می دارد.
- post – با ارسال داده های ارائه شده، آدرس url مشخص شده را از طریق POST درخواست کنید و پاسخ را به عنوان <Future <Response>برگردانید
- put – آدرس url مشخص شده را از طریق روش PUT درخواست کنید و پاسخ را به عنوان Future <Response> برگردانید
- head– آدرس url مشخص شده را از طریق روش HEAD درخواست کرده و پاسخ را به عنوان <Future <Response برگردانید
- delete – آدرس url مشخص شده را از طریق روش DELETE درخواست کرده و پاسخ را به عنوان <Future <Response برگردانید
http همچنین یک کلاس استاندارد HTTP کلاینت ارائه می دهد. کلاینت از اتصال مداوم پشتیبانی می کند. هنگامی که درخواست زیادی از یک سرور خاص درخواست شود مفید خواهد بود. با استفاده از روش بستن باید به درستی بسته شود. در غیر این صورت، شبیه کلاس http است. کد نمونه به شرح زیر است –
1 2 3 4 5 6 7 |
var client = new http.Client(); try { print(await client.get('https://flutter.dev/')); } finally { client.close(); } |
دسترسی به سرویس خدمات API
اجازه دهید یک برنامه ساده ایجاد کنیم تا داده های محصول را از یک وب سرور دریافت کنیم و سپس محصولات را با استفاده از ListView نشان دهیم.
- یک برنامه جدید Flutter در Android studio ، product_rest_app ایجاد کنید.
- کد راه اندازی پیش فرض (main.dart) را با کد product_nav_app جایگزین کنید.
- پوشه assets را از Product_nav_app در Product_rest_app کپی کرده و assets را در داخل فایل pubspec.yaml اضافه کنید.
1 2 3 4 5 6 7 8 |
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 پیکربندی کنید –
1 2 |
dependencies: http: ^0.12.0+2 |
در اینجا، ما از آخرین نسخه پکیج http استفاده خواهیم کرد. Android studio هشدار پکیجی را ارسال می کند که pubspec.yaml به روز شده است.
گزینه دریافت وابستگی ها را کلیک کنید. Android studio پکیج را از اینترنت دریافت کرده و به درستی برای برنامه پیکربندی می کند.
پکیج http را در پرونده main.dart وارد کنید –
1 2 3 |
import 'dart:async'; import 'dart:convert'; import 'package:http/http.dart' as http; |
همانطور که در زیر نشان داده شده است، یک فایل جدید JSON ، products.json با اطلاعات محصول ایجاد کنید
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
[ { "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-server است. برای نصب و اجرای برنامه سرور http مراحل زیر را دنبال کنید
- برنامه Nodejs را نصب کنید (nodejs.org)
- به پوشه JSONWebServer بروید.
1 |
cd /path/to/JSONWebServer |
- بسته سرور http را با استفاده از npm نصب کنید.
1 |
npm install -g http-server |
- اکنون، سرور را اجرا کنید.
1 2 3 4 5 6 7 |
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 را به داخل آن منتقل کنید.
- یک سازنده کارخانه را در کلاس Product ، Product.fromMap بنویسید تا نقشه برداری داده را به شی Product تبدیل کند. به طور معمول، فایل JSON به شی دارت نقشه تبدیل شده و سپس به شی مربوطه تبدیل می شود (محصول).
1 2 3 4 5 6 7 8 |
factory Product.fromJson(Map<String, dynamic> data) { return Product( data['name'], data['description'], data['price'], data['image'], ); } |
کد کامل Product.dart به شرح زیر است –
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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'], ); } } |
برای واکشی و دانلود اطلاعات محصول از سرور وب در لیست List <Product> دو روش – تجزیه محصولات و واکشی محصولات – در کلاس اصلی بنویسید
1 2 3 4 5 6 7 8 9 10 11 12 |
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'); } } |
به نکات زیر در اینجا توجه کنید –
- Future برای lazy load اطلاعات محصول استفاده می شود. Lazy loading مفهومی برای به تعویق انداختن اجرای کد تا زمان ضرورت است.
- http.get برای واکشی داده ها از اینترنت استفاده می شود.
- json.decode برای رمزگشایی داده های JSON در شی دارت استفاده می شود. پس از رمزگشایی داده های JSON ، با استفاده از از نقشه کلاس Product به لیست <Product> تبدیل می شود.
- در کلاس MyApp، متغیر عضو جدید محصولات از نوع <Future <Product اضافه کنید و آن را در سازنده قرار دهید.
1 2 3 4 |
class MyApp extends StatelessWidget { final Future<List<Product>> products; MyApp({Key key, this.products}) : super(key: key); ... |
- در کلاس MyHomePage، محصولات متغیر عضو جدید را از نوع <Future <Product= اضافه کرده و در سازنده قرار دهید. همچنین، متغیر موارد و متد مربوط به آن را حذف کنید، فراخوانی متد getProducts. قرار دادن متغیر محصولات در سازنده این امکان را برای شما فراهم می کند تا هنگام شروع برنامه، محصولات را فقط یک بار از اینترنت واکشی کنید.
1 2 3 4 5 |
class MyHomePage extends StatelessWidget { final String title; final Future<ListList<Product>> products; MyHomePage({Key key, this.title, this.products}) : super(key: key); ... |
- گزینه (Home (MyHomePage را در روش ساخت ویجت MyApp تغییر دهید تا تغییرات بالا را در خود جای دهید –
1 |
home: MyHomePage(title: 'Product Navigation demo home page', products: products), |
- تابع اصلی را تغییر دهید تا شامل آرگومانهای <Future <Product شود –
1 |
void main() => runApp(MyApp(fetchProduct())); |
برای ایجاد لیست محصولات در صفحه اصلی ، یک ویجت جدید ProductBoxList ایجاد کنید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
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 بدست آورید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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 خود دریافت کند (از نوع <Future <List <Product>). اگر ویژگی Future داده ها را بازگرداند، ویجت را با استفاده از ProductBoxList ارائه می دهد، در غیر این صورت خطایی ایجاد می شود.
کد کامل main.dart به شرح زیر است:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
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(), ], ) ) ) ] ), ) ); } } |
در آخر برنامه را اجرا کنید تا نتیجه را ببینید. این همان مثال پیمایش خواهد بود، به جز اینکه داده ها از اینترنت به جای داده های استاتیک محلی، هنگام کدگذاری برنامه وارد می شوند.
لیست جلسات قبل آموزش برنامه نویسی فلاتر
- معرفی برنامه نویسی فلاتر
- آموزش نصب فلاتر
- اصول ایجاد یک برنامه فلاتر در Android Studio
- معماری فریم ورک برنامه نویسی فلاتر
- مقدمه ای بر برنامه نویسی دارت
- مقدمه ای بر ویجت ها در برنامه نویسی فلاتر
- آموزش طرح بندی در برنامه نویسی فلاتر
- ژست های حرکتی در برنامه نویسی فلاتر
- مدیریت State در برنامه نویسی فلاتر
- آموزش انیمیشن در برنامه نویسی فلاتر
- آموزش نوشتن کد خاص اندروید در برنامه نویسی فلاتر
- آموزش نوشتن کد مخصوص IOS در برنامه نویسی فلاتر
- مقدمه ای بر پکیج ها در برنامه نویسی فلاتر
دیدگاه شما