Flattern - Zugriff auf die REST-API

Flutter bietet ein http-Paket zum Verbrauch von HTTP-Ressourcen. http ist eine zukunftsbasierte Bibliothek und verwendet Funktionen zum Warten und Asynchronisieren. Es bietet viele Methoden auf hoher Ebene und vereinfacht die Entwicklung von REST-basierten mobilen Anwendungen.

Grundlegendes Konzept

Das http-Paket bietet eine High-Level-Klasse und http für Webanfragen.

  • Die http-Klasse bietet Funktionen zum Ausführen aller Arten von HTTP-Anforderungen.

  • http-Methoden akzeptieren eine URL und zusätzliche Informationen über Dart Map (Post-Daten, zusätzliche Header usw.). Es fordert den Server an und sammelt die Antwort wieder im asynchronen / wartenden Muster. Der folgende Code liest beispielsweise die Daten aus der angegebenen URL und druckt sie in der Konsole aus.

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

Einige der Kernmethoden sind wie folgt:

  • read - Fordern Sie die angegebene URL über die GET-Methode an und geben Sie die Antwort als Future <String> zurück

  • get- Fordern Sie die angegebene URL über die GET-Methode an und geben Sie die Antwort als Future <Response> zurück. Antwort ist eine Klasse, die die Antwortinformationen enthält.

  • post - Fordern Sie die angegebene URL über die POST-Methode an, indem Sie die angegebenen Daten veröffentlichen und die Antwort als Future <Response> zurückgeben

  • put - Fordern Sie die angegebene URL über die PUT-Methode an und geben Sie die Antwort als Future <Response> zurück

  • head - Fordern Sie die angegebene URL über die HEAD-Methode an und geben Sie die Antwort als Future <Response> zurück

  • delete - Fordern Sie die angegebene URL über die DELETE-Methode an und geben Sie die Antwort als Future <Response> zurück

http bietet auch eine Standard-HTTP-Clientklasse, den Client. Client unterstützt dauerhafte Verbindung. Dies ist hilfreich, wenn viele Anfragen an einen bestimmten Server gestellt werden müssen. Es muss ordnungsgemäß mit der Methode close geschlossen werden. Ansonsten ähnelt es der http-Klasse. Der Beispielcode lautet wie folgt:

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

Zugriff auf die Produktservice-API

Lassen Sie uns eine einfache Anwendung erstellen, um Produktdaten von einem Webserver abzurufen und die Produkte dann mit ListView anzuzeigen .

  • Erstellen Sie eine neue Flutter- Anwendung in Android Studio, product_rest_app .

  • Ersetzen Sie den Standardstartcode (main.dart) durch unseren product_nav_app- Code.

  • Kopieren Sie den Assets-Ordner von product_nav_app nach product_rest_app und fügen Sie Assets in die Datei pubspec.yaml ein.

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
  • Konfigurieren Sie das http-Paket in der Datei pubspec.yaml wie unten gezeigt -

dependencies: 
   http: ^0.12.0+2
  • Hier verwenden wir die neueste Version des http-Pakets. Android Studio sendet eine Paketbenachrichtigung, dass die Datei pubspec.yaml aktualisiert wird.

  • Klicken Sie auf die Option Abhängigkeiten abrufen. Android Studio holt das Paket aus dem Internet und konfiguriert es ordnungsgemäß für die Anwendung.

  • Importieren Sie das http-Paket in die Datei main.dart.

import 'dart:async'; 
import 'dart:convert'; 
import 'package:http/http.dart' as http;
  • Erstellen Sie eine neue JSON-Datei, products.json, mit Produktinformationen wie unten gezeigt -

[ 
   { 
      "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" 
   } 
]
  • Erstellen Sie einen neuen Ordner, JSONWebServer, und platzieren Sie die JSON-Datei products.json.

  • Führen Sie einen beliebigen Webserver mit JSONWebServer als Stammverzeichnis aus und rufen Sie den Webpfad ab. Zum Beispiel http://192.168.184.1:8000/products.json. Wir können jeden Webserver wie Apache, Nginx usw. verwenden.

  • Am einfachsten ist es, eine knotenbasierte http-Server-Anwendung zu installieren. Führen Sie die folgenden Schritte aus, um die http-Server-Anwendung zu installieren und auszuführen

    • Installieren Sie die Nodejs-Anwendung ( nodejs.org ).

    • Wechseln Sie zum Ordner JSONWebServer.

cd /path/to/JSONWebServer
  • Installieren Sie das http-Server-Paket mit npm.

npm install -g http-server
  • Führen Sie nun den Server aus.

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
  • Erstellen Sie eine neue Datei, Product.dart, im lib-Ordner und verschieben Sie die Product-Klasse hinein.

  • Schreiben Sie einen Factory-Konstruktor in die Produktklasse Product.fromMap, um die zugeordnete Datenzuordnung in das Product-Objekt zu konvertieren. Normalerweise wird die JSON-Datei in ein Dart Map-Objekt und anschließend in ein relevantes Objekt (Produkt) konvertiert.

factory Product.fromJson(Map<String, dynamic> data) {
   return Product(
      data['name'],
      data['description'], 
      data['price'],
      data['image'],
   );
}
  • Der vollständige Code des Product.dart lautet wie folgt:

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'], 
      );
   }
}
  • Schreiben Sie zwei Methoden - parseProducts und fetchProducts - in die Hauptklasse, um die Produktinformationen vom Webserver abzurufen und in das List <Product> -Objekt zu laden.

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');
   } 
}
  • Beachten Sie hier die folgenden Punkte -

    • Future wird verwendet, um die Produktinformationen zu verzögern. Lazy Loading ist ein Konzept, um die Ausführung des Codes zu verschieben, bis es notwendig ist.

    • http.get wird verwendet, um die Daten aus dem Internet abzurufen.

    • Mit json.decode werden die JSON-Daten in das Dart Map-Objekt dekodiert. Sobald JSON-Daten dekodiert sind, werden sie mit fromMap der Produktklasse in List <Produkt> konvertiert.

    • Fügen Sie in der MyApp-Klasse eine neue Elementvariable hinzu, Produkte vom Typ Future <Produkt>, und fügen Sie sie in den Konstruktor ein.

class MyApp extends StatelessWidget { 
   final Future<List<Product>> products; 
   MyApp({Key key, this.products}) : super(key: key); 
   ...
  • Fügen Sie in der MyHomePage-Klasse neue Produkte für Elementvariablen vom Typ Future <Produkt> hinzu und fügen Sie sie in den Konstruktor ein. Entfernen Sie außerdem die Elementvariable und ihre relevante Methode, den Methodenaufruf getProducts. Platzieren der Produktvariablen im Konstruktor. Die Produkte können beim ersten Start der Anwendung nur einmal aus dem Internet abgerufen werden.

class MyHomePage extends StatelessWidget { 
   final String title; 
   final Future<ListList<Product>> products; 
   MyHomePage({Key key, this.title, this.products}) : super(key: key); 
   ...
  • Ändern Sie die Home-Option (MyHomePage) in der Erstellungsmethode des MyApp-Widgets, um die oben genannten Änderungen zu berücksichtigen.

home: MyHomePage(title: 'Product Navigation demo home page', products: products),
  • Ändern Sie die Hauptfunktion so, dass sie zukünftige <Produkt> -Argumente enthält -

void main() => runApp(MyApp(fetchProduct()));
  • Erstellen Sie ein neues Widget, ProductBoxList, um die Produktliste auf der Startseite zu erstellen.

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]), 
                     ), 
                  ); 
               }, 
            ); 
         }, 
      ); 
   } 
}

Beachten Sie, dass wir das gleiche Konzept wie in der Navigationsanwendung verwendet haben, um das Produkt aufzulisten, außer dass es als separates Widget konzipiert ist, indem Produkte (Objekt) vom Typ List <Produkt> übergeben werden.

  • Ändern Sie abschließend die Erstellungsmethode des MyHomePage- Widgets, um die Produktinformationen mithilfe der Option "Zukunft" anstelle des normalen Methodenaufrufs abzurufen .

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()); 
            }, 
         ), 
      )
   ); 
}
  • Beachten Sie hier, dass wir das FutureBuilder-Widget zum Rendern des Widgets verwendet haben. FutureBuilder versucht, die Daten aus seiner Future-Eigenschaft abzurufen (vom Typ Future <List <Produkt >>). Wenn die zukünftige Eigenschaft Daten zurückgibt, wird das Widget mithilfe von ProductBoxList gerendert, andernfalls wird ein Fehler ausgegeben.

  • Der vollständige Code des main.dart lautet wie folgt:

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(), 
                           ], 
                        )
                     )
                  )
               ]
            ), 
         )
      ); 
   } 
}

Führen Sie schließlich die Anwendung aus, um das Ergebnis anzuzeigen. Es ist dasselbe wie in unserem Navigationsbeispiel, außer dass die Daten aus dem Internet stammen und nicht aus lokalen, statischen Daten, die beim Codieren der Anwendung eingegeben wurden.