Flutter - Accesso all'API REST
Flutter fornisce un pacchetto http per utilizzare le risorse HTTP. http è una libreria basata sul futuro e utilizza le funzionalità di attesa e asincrona. Fornisce molti metodi di alto livello e semplifica lo sviluppo di applicazioni mobili basate su REST.
Concetti basilari
Il pacchetto http fornisce una classe di alto livello e http per eseguire le richieste web.
La classe http fornisce funzionalità per eseguire tutti i tipi di richieste HTTP.
I metodi http accettano un URL e informazioni aggiuntive tramite Dart Map (dati di post, intestazioni aggiuntive, ecc.). Richiede il server e raccoglie la risposta in un pattern async / await. Ad esempio, il codice seguente legge i dati dall'URL specificato e li stampa nella console.
print(await http.read('https://flutter.dev/'));
Alcuni dei metodi principali sono i seguenti:
read - Richiedi l'URL specificato tramite il metodo GET e restituisci la risposta come Future <String>
get- Richiedi l'URL specificato tramite il metodo GET e restituisci la risposta come Future <Response>. La risposta è una classe che contiene le informazioni sulla risposta.
post - Richiedi l'URL specificato tramite il metodo POST inviando i dati forniti e restituendo la risposta come Future <Response>
put - Richiedi l'URL specificato tramite il metodo PUT e restituisci la risposta come Future <Response>
head - Richiedi l'URL specificato tramite il metodo HEAD e restituisci la risposta come Future <Response>
delete - Richiedi l'URL specificato tramite il metodo DELETE e restituisci la risposta come Future <Response>
http fornisce anche una classe client HTTP più standard, client. il client supporta la connessione persistente. Sarà utile quando molte richieste devono essere fatte a un particolare server. Deve essere chiuso correttamente utilizzando il metodo di chiusura. Altrimenti, è simile alla classe http. Il codice di esempio è il seguente:
var client = new http.Client();
try {
print(await client.get('https://flutter.dev/'));
}
finally {
client.close();
}
Accesso all'API del servizio prodotto
Creiamo una semplice applicazione per ottenere i dati del prodotto da un server web e quindi mostrare i prodotti utilizzando ListView .
Crea una nuova applicazione Flutter in Android Studio, product_rest_app .
Sostituisci il codice di avvio predefinito (main.dart) con il nostro codice product_nav_app .
Copia la cartella degli asset da product_nav_app a product_rest_app e aggiungi gli asset all'interno del file 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
Configura il pacchetto http nel file pubspec.yaml come mostrato di seguito:
dependencies:
http: ^0.12.0+2
Qui useremo l'ultima versione del pacchetto http. Android Studio invierà un avviso del pacchetto che il pubspec.yaml è aggiornato.
Fare clic sull'opzione Ottieni dipendenze. Android Studio otterrà il pacchetto da Internet e lo configurerà correttamente per l'applicazione.
Importa il pacchetto http nel file main.dart -
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
Crea un nuovo file JSON, products.json con le informazioni sul prodotto come mostrato di seguito -
[
{
"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"
}
]
Crea una nuova cartella, JSONWebServer e inserisci il file JSON, products.json.
Esegui qualsiasi server web con JSONWebServer come directory principale e ottieni il suo percorso web. Ad esempio, http://192.168.184.1:8000/products.json. Possiamo usare qualsiasi server web come apache, nginx ecc.,
Il modo più semplice è installare un'applicazione server http basata su nodi. Seguire i passaggi indicati di seguito per installare ed eseguire l'applicazione server http
Installa l'applicazione Nodejs ( nodejs.org )
Vai alla cartella JSONWebServer.
cd /path/to/JSONWebServer
Installa il pacchetto http-server utilizzando npm.
npm install -g http-server
Ora esegui il 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
Crea un nuovo file, Product.dart nella cartella lib e sposta la classe Product al suo interno.
Scrivere un costruttore di fabbrica nella classe Product, Product.fromMap per convertire la mappa dei dati mappati nell'oggetto Product. Normalmente, il file JSON verrà convertito in oggetto Dart Map e quindi convertito in oggetto pertinente (prodotto).
factory Product.fromJson(Map<String, dynamic> data) {
return Product(
data['name'],
data['description'],
data['price'],
data['image'],
);
}
Il codice completo del Product.dart è il seguente:
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'],
);
}
}
Scrivete due metodi - parseProducts e fetchProducts - nella classe principale per recuperare e caricare le informazioni sul prodotto dal server web nell'oggetto 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');
}
}
Nota i seguenti punti qui:
Il futuro viene utilizzato per caricare in modo pigro le informazioni sul prodotto. Il caricamento lento è un concetto per rinviare l'esecuzione del codice fino a quando non è necessario.
http.get viene utilizzato per recuperare i dati da Internet.
json.decode viene utilizzato per decodificare i dati JSON nell'oggetto Dart Map. Una volta che i dati JSON sono stati decodificati, verranno convertiti in List <Product> utilizzando fromMap della classe Product.
Nella classe MyApp, aggiungi una nuova variabile membro, prodotti di tipo Future <Product> e includila nel costruttore.
class MyApp extends StatelessWidget {
final Future<List<Product>> products;
MyApp({Key key, this.products}) : super(key: key);
...
Nella classe MyHomePage, aggiungi nuovi prodotti di variabili membro di tipo Future <Product> e includili nel costruttore. Inoltre, rimuovere la variabile degli elementi e il relativo metodo, chiamata al metodo getProducts. Posizionamento della variabile prodotti nel costruttore. Permetterà di recuperare i prodotti da Internet solo una volta al primo avvio dell'applicazione.
class MyHomePage extends StatelessWidget {
final String title;
final Future<ListList<Product>> products;
MyHomePage({Key key, this.title, this.products}) : super(key: key);
...
Modificare l'opzione home (MyHomePage) nel metodo di creazione del widget MyApp per accogliere le modifiche di cui sopra -
home: MyHomePage(title: 'Product Navigation demo home page', products: products),
Modificare la funzione principale per includere argomenti <Prodotto> futuro -
void main() => runApp(MyApp(fetchProduct()));
Crea un nuovo widget, ProductBoxList per creare l'elenco dei prodotti nella home page.
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]),
),
);
},
);
},
);
}
}
Si noti che abbiamo utilizzato lo stesso concetto utilizzato nell'applicazione di navigazione per elencare il prodotto, tranne per il fatto che è progettato come un widget separato passando prodotti (oggetto) di tipo List <Product>.
Infine, modifica il metodo build del widget MyHomePage per ottenere le informazioni sul prodotto utilizzando l'opzione Future invece della normale chiamata al metodo.
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());
},
),
)
);
}
Nota che abbiamo utilizzato il widget FutureBuilder per renderizzare il widget. FutureBuilder proverà a recuperare i dati dalla sua proprietà future (di tipo Future <List <Product>>). Se la proprietà futura restituisce dati, renderà il widget utilizzando ProductBoxList, altrimenti genera un errore.
Il codice completo del main.dart è il seguente:
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(),
],
)
)
)
]
),
)
);
}
}
Infine esegui l'applicazione per vedere il risultato. Sarà lo stesso del nostro esempio di navigazione tranne per il fatto che i dati provengono da Internet anziché i dati statici locali inseriti durante la codifica dell'applicazione.