從大多數應用程序獲取互聯網上的數據是必要的。 幸運的是,Dart和Flutter爲這類工做提供了工具!html
路線java
http包提供了從互聯網獲取數據的最簡單方法。web
在這個例子中,咱們將使用http.get方法從JSONPlaceholder REST API獲取示例文章。json
Future<http.Response> fetchPost() { return http.get('https://jsonplaceholder.typicode.com/posts/1'); }
http.get方法返回一個包含Response的Future。api
雖然提出網絡請求很容易,但使用原始Future<http.Response>並不方便。 爲了讓咱們的生活更輕鬆,咱們能夠將http.Response轉換爲咱們本身的Dart對象。服務器
建立一個Post類websocket
首先,咱們須要建立一個Post類,其中包含來自咱們網絡請求的數據。 它還將包含一個工廠構造函數,容許咱們從json建立一個Post。網絡
手動轉換JSON只是一種選擇。 有關更多信息,請參閱關於JSON和序列化的完整文章。app
class Post { final int userId; final int id; final String title; final String body; Post({this.userId, this.id, this.title, this.body}); factory Post.fromJson(Map<String, dynamic> json) { return new Post( userId: json['userId'], id: json['id'], title: json['title'], body: json['body'], ); } }
將http.Response轉換爲Postless
如今,咱們將更新fetchPost函數以返回Future<Post>。 爲此,咱們須要:
Future<Post> fetchPost() async { final response = await http.get('https://jsonplaceholder.typicode.com/posts/1'); final responseJson = json.decode(response.body); return new Post.fromJson(responseJson); }
萬歲! 如今咱們有一個功能,咱們能夠調用從互聯網上獲取Post!
爲了獲取數據並將其顯示在屏幕上,咱們可使用FutureBuilder小部件! Flutter附帶FutureBuilder部件,能夠輕鬆處理異步數據源。
咱們必須提供兩個參數:
使用的Future。 在咱們的例子中,咱們將調用咱們的fetchPost()函數。
一個builder函數,告訴Flutter渲染什麼,取決於Future的狀態:加載,成功或錯誤。
new FutureBuilder<Post>( future: fetchPost(), builder: (context, snapshot) { if (snapshot.hasData) { return new Text(snapshot.data.title); } else if (snapshot.hasError) { return new Text("${snapshot.error}"); } // By default, show a loading spinner return new CircularProgressIndicator(); }, );
import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; Future<Post> fetchPost() async { final response = await http.get('https://jsonplaceholder.typicode.com/posts/1'); final responseJson = json.decode(response.body); return new Post.fromJson(responseJson); } class Post { final int userId; final int id; final String title; final String body; Post({this.userId, this.id, this.title, this.body}); factory Post.fromJson(Map<String, dynamic> json) { return new Post( userId: json['userId'], id: json['id'], title: json['title'], body: json['body'], ); } } void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Fetch Data Example', theme: new ThemeData( primarySwatch: Colors.blue, ), home: new Scaffold( appBar: new AppBar( title: new Text('Fetch Data Example'), ), body: new Center( child: new FutureBuilder<Post>( future: fetchPost(), builder: (context, snapshot) { if (snapshot.hasData) { return new Text(snapshot.data.title); } else if (snapshot.hasError) { return new Text("${snapshot.error}"); } // By default, show a loading spinner return new CircularProgressIndicator(); }, ), ), ), ); } }
爲了從許多Web服務獲取數據,您須要提供受權。 有不少方法能夠作到這一點,但也許最多見的方法是使用Authorization HTTP標頭。
http包提供了一種方便的方法來爲請求添加請求頭。 您還能夠利用dart:io軟件包來處理常見的HttpHeaders。
Future<http.Response> fetchPost() { return http.get( 'https://jsonplaceholder.typicode.com/posts/1', // Send authorization headers to your backend headers: {HttpHeaders.AUTHORIZATION: "Basic your_api_token_here"}, ); }
這個例子創建在Internet抓取數據的配方上。
import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:http/http.dart' as http; Future<Post> fetchPost() async { final response = await http.get( 'https://jsonplaceholder.typicode.com/posts/1', headers: {HttpHeaders.AUTHORIZATION: "Basic your_api_token_here"}, ); final json = json.decode(response.body); return new Post.fromJson(json); } class Post { final int userId; final int id; final String title; final String body; Post({this.userId, this.id, this.title, this.body}); factory Post.fromJson(Map<String, dynamic> json) { return new Post( userId: json['userId'], id: json['id'], title: json['title'], body: json['body'], ); } }
除了正常的HTTP請求外,咱們還可使用WebSockets鏈接到服務器。 WebSocket容許與服務器進行雙向通訊而無需輪詢。
在這個例子中,咱們將鏈接到由websocket.org提供的測試服務器。 服務器將簡單地發回咱們發送給它的相同消息!
路線
web_socket_channel包提供了咱們須要鏈接到WebSocket服務器的工具。
該軟件包提供了一個WebSocketChannel,它容許咱們既監聽來自服務器的消息,又將消息推送到服務器。
在Flutter中,咱們能夠建立一個鏈接到服務器的WebSocketChannel:
final channel = new IOWebSocketChannel.connect('ws://echo.websocket.org');
如今咱們創建了鏈接,咱們能夠收聽來自服務器的消息。
在咱們發送消息給測試服務器以後,它會發回相同的消息。
咱們如何聽取消息並顯示它們? 在這個例子中,咱們將使用StreamBuilder部件來偵聽新消息和一個Text 部件來顯示它們。
new StreamBuilder( stream: widget.channel.stream, builder: (context, snapshot) { return new Text(snapshot.hasData ? '${snapshot.data}' : ''); }, );
這個怎麼用?
WebSocketChannel從服務器提供消息Stream 。
Stream類是dart:async包的基礎部分。 它提供了一種方法來偵聽來自數據源的異步事件。 與將返回單個異步響應的Future不一樣,Stream類能夠隨着時間的推移傳遞許多事件。
StreamBuilder部件將鏈接到Stream,並在每次接收到事件時使用給定的builder函數請求Flutter重建!
爲了將數據發送到服務器,咱們將消息add到由WebSocketChannel提供的sink接收器。
channel.sink.add('Hello!');
這個怎麼用
WebSocketChannel提供了一個StreamSink來將消息推送到服務器。
StreamSink類提供了將同步或異步事件添加到數據源的通常方法。
在咱們完成使用WebSocket以後,咱們將要關閉鏈接! 爲此,咱們能夠關閉sink。
channel.sink.close();
import 'package:flutter/foundation.dart'; import 'package:web_socket_channel/io.dart'; import 'package:flutter/material.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { final title = 'WebSocket Demo'; return new MaterialApp( title: title, home: new MyHomePage( title: title, channel: new IOWebSocketChannel.connect('ws://echo.websocket.org'), ), ); } } class MyHomePage extends StatefulWidget { final String title; final WebSocketChannel channel; MyHomePage({Key key, @required this.title, @required this.channel}) : super(key: key); @override _MyHomePageState createState() => new _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { TextEditingController _controller = new TextEditingController(); @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(widget.title), ), body: new Padding( padding: const EdgeInsets.all(20.0), child: new Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ new Form( child: new TextFormField( controller: _controller, decoration: new InputDecoration(labelText: 'Send a message'), ), ), new StreamBuilder( stream: widget.channel.stream, builder: (context, snapshot) { return new Padding( padding: const EdgeInsets.symmetric(vertical: 24.0), child: new Text(snapshot.hasData ? '${snapshot.data}' : ''), ); }, ) ], ), ), floatingActionButton: new FloatingActionButton( onPressed: _sendMessage, tooltip: 'Send message', child: new Icon(Icons.send), ), // This trailing comma makes auto-formatting nicer for build methods. ); } void _sendMessage() { if (_controller.text.isNotEmpty) { widget.channel.sink.add(_controller.text); } } @override void dispose() { widget.channel.sink.close(); super.dispose(); } }