Flutter-accessing-rest-api
Flutter-REST APIへのアクセス
Flutterは、HTTPリソースを消費するhttpパッケージを提供します。 httpはFutureベースのライブラリであり、awaitおよびasync機能を使用します。 多くの高レベルのメソッドを提供し、RESTベースのモバイルアプリケーションの開発を簡素化します。
基本概念
httpパッケージは、Web要求を行うための高レベルのクラスとhttpを提供します。
- httpクラスは、すべてのタイプのHTTP要求を実行する機能を提供します。
- httpメソッドは、URL、およびDart Mapを介した追加情報(投稿データ、追加ヘッダーなど)を受け入れます。 サーバーを要求し、async/awaitパターンで応答を収集します。 たとえば、次のコードは、指定されたURLからデータを読み取り、コンソールに出力します。
print(await http.read('https://flutter.dev/'));
コアメソッドのいくつかは次のとおりです-
- 読み取り-GETメソッドを使用して指定されたURLを要求し、Future <String>として応答を返します
- get -GETメソッドを使用して指定されたURLを要求し、Future <Response>として応答を返します。 応答は、応答情報を保持するクラスです。
- post -指定されたURLをPOSTメソッドで要求し、提供されたデータをポストして、Future <Response>として応答を返します。
- put -PUTメソッドを介して指定されたURLを要求し、Future <Response>として応答を返します
- head -HEADメソッドで指定されたURLを要求し、Future <Response>として応答を返します
- delete -DELETEメソッドを使用して指定されたURLを要求し、Future <Response>として応答を返します
httpは、より標準的なHTTPクライアントクラスであるclientも提供します。 クライアントは永続的な接続をサポートします。 特定のサーバーに対して大量の要求が行われる場合に役立ちます。 closeメソッドを使用して適切に閉じる必要があります。 それ以外の場合は、httpクラスに似ています。 サンプルコードは次のとおりです-
var client = new http.Client();
try {
print(await client.get('https://flutter.dev/'));
}
finally {
client.close();
}
製品サービスAPIへのアクセス
Webサーバーから製品データを取得する単純なアプリケーションを作成し、_ListView_を使用して製品を表示します。
- Androidスタジオで_product_rest_app_に新しい_Flutter_アプリケーションを作成します。
- デフォルトの起動コード(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
- 以下に示すように、pubspec.yamlファイルでhttpパッケージを構成します-
dependencies:
http: ^0.12.0+2
- ここでは、httpパッケージの最新バージョンを使用します。 Androidスタジオは、pubspec.yamlが更新されたことを示すパッケージアラートを送信します。
- [依存関係の取得]オプションをクリックします。 Androidスタジオはインターネットからパッケージを取得し、アプリケーション用に適切に構成します。
- main.dartファイルにhttpパッケージをインポートします-
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をルートディレクトリとしてWebサーバーを実行し、Webパスを取得します。 たとえば、http://192.168.184.1:8000/products.json。 apache、nginxなどの任意のWebサーバーを使用できます。
最も簡単な方法は、ノードベースのhttpサーバーアプリケーションをインストールすることです。 http-serverアプリケーションをインストールして実行するには、以下の手順に従ってください
- Nodejsアプリケーションをインストールします(https://nodejs.org/en/[nodejs.org])
- JSONWebServerフォルダーに移動します。
cd/path/to/JSONWebServer
- npmを使用してhttp-serverパッケージをインストールします。
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
- libフォルダーに新しいファイルProduct.dartを作成し、Productクラスをその中に移動します。
- ProductクラスのファクトリコンストラクタProduct.fromMapを記述して、マップされたデータMapをProductオブジェクトに変換します。 通常、JSONファイルはDart Mapオブジェクトに変換されてから、関連するオブジェクト(製品)に変換されます。
factory Product.fromJson(Map<String, dynamic> data) {
return Product(
data['name'],
data['description'],
data['price'],
data['image'],
);
}
- Product.dartの完全なコードは次のとおりです-
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'],
);
}
}
- メインクラスに2つのメソッドparseProductsとfetchProductsを記述して、Webサーバーから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');
}
}
ここで次の点に注意してください-
- Futureは、製品情報を遅延ロードするために使用されます。 遅延読み込みは、必要になるまでコードの実行を延期する概念です。
- http.getは、インターネットからデータを取得するために使用されます。
- json.decodeは、JSONデータをDart Mapオブジェクトにデコードするために使用されます。 JSONデータがデコードされると、ProductクラスのfromMapを使用してList <Product>に変換されます。
- MyAppクラスで、新しいメンバー変数、タイプFuture <Product>の製品を追加し、コンストラクターに含めます。
class MyApp extends StatelessWidget {
final Future<List<Product>> products;
MyApp({Key key, this.products}) : super(key: key);
...
- MyHomePageクラスで、Future <Product>型の新しいメンバー変数製品を追加し、コンストラクターに含めます。 また、items変数とその関連メソッドであるgetProductsメソッド呼び出しを削除します。 製品変数をコンストラクターに配置します。 アプリケーションを最初に起動したときに一度だけインターネットから製品を取得できます。
class MyHomePage extends StatelessWidget {
final String title;
final Future<ListList<Product>> products;
MyHomePage({Key key, this.title, this.products}) : super(key: key);
...
- 上記の変更に対応するために、MyAppウィジェットのビルドメソッドでホームオプション(MyHomePage)を変更します-
home: MyHomePage(title: 'Product Navigation demo home page', products: products),
- Future <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 >>型のfutureプロパティからデータを取得しようとします。 futureプロパティがデータを返す場合、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(),
],
)
)
)
]
),
)
);
}
}
最後に、アプリケーションを実行して結果を確認します。 アプリケーションのコーディング中に入力されたローカルの静的データではなく、インターネットからのデータであること以外は、_Navigation_の例と同じです。