Flutter 構建完整應用手冊-聯網

從互聯網上獲取數據

從大多數應用程序獲取互聯網上的數據是必要的。 幸運的是,Dart和Flutter爲這類工做提供了工具!html

路線java

  1. 使用http包發出網絡請求
  2. 將響應轉換爲自定義Dart對象
  3. 用Flutter獲取並顯示數據

1.使用http包發出網絡請求

http包提供了從互聯網獲取數據的最簡單方法。web

在這個例子中,咱們將使用http.get方法從JSONPlaceholder REST API獲取示例文章。json

Future<http.Response> fetchPost() {
  return http.get('https://jsonplaceholder.typicode.com/posts/1');
}

http.get方法返回一個包含ResponseFutureapi

  • Future是與異步操做一塊兒工做的核心Dart類。 它用於表示將來某個時間可能會出現的潛在價值或錯誤。
  • http.Response類包含從成功的http調用收到的數據。

2.將響應轉換爲自定義Dart對象

雖然提出網絡請求很容易,但使用原始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>。 爲此,咱們須要:

  • 使用dart:convert包將響應正文轉換爲json Map
  • 使用fromJson工廠函數將json Map轉換爲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

3.用Flutter獲取並顯示數據

爲了獲取數據並將其顯示在屏幕上,咱們可使用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'],
    );
  }
}

使用WebSockets

除了正常的HTTP請求外,咱們還可使用WebSockets鏈接到服務器。 WebSocket容許與服務器進行雙向通訊而無需輪詢。

在這個例子中,咱們將鏈接到由websocket.org提供的測試服務器。 服務器將簡單地發回咱們發送給它的相同消息!

路線

  • 鏈接到WebSocket服務器
  • 監聽來自服務器的消息
  • 將數據發送到服務器
  • 關閉WebSocket鏈接

1.鏈接到WebSocket服務器

web_socket_channel包提供了咱們須要鏈接到WebSocket服務器的工具。

該軟件包提供了一個WebSocketChannel,它容許咱們既監聽來自服務器的消息,又將消息推送到服務器。

在Flutter中,咱們能夠建立一個鏈接到服務器的WebSocketChannel

final channel = new IOWebSocketChannel.connect('ws://echo.websocket.org');

2.監聽來自服務器的消息

如今咱們創建了鏈接,咱們能夠收聽來自服務器的消息。

在咱們發送消息給測試服務器以後,它會發回相同的消息。

咱們如何聽取消息並顯示它們? 在這個例子中,咱們將使用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重建!

3.將數據發送到服務器

爲了將數據發送到服務器,咱們將消息add到由WebSocketChannel提供的sink接收器。

channel.sink.add('Hello!');

這個怎麼用
WebSocketChannel提供了一個StreamSink來將消息推送到服務器。
StreamSink類提供了將同步或異步事件添加到數據源的通常方法。

4.關閉WebSocket鏈接

在咱們完成使用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();
  }
}

相關文章
相關標籤/搜索