iT邦幫忙

2024 iThome 鐵人賽

DAY 12
0
Mobile Development

從零開始以Flutter打造跨平台聊天APP系列 第 12

Day-12 在 Flutter 中使用原生方法及第三方套件實現 http 連線

  • 分享至 

  • xImage
  •  

Generated from Stable Diffusion 3 Medium

昨天提到,在開發 App 時,除了要注意前端的畫面外,我們也經常要與後端伺服器通訊。這個章節將會介紹如何在 Flutter App 中使用 http 接 API。

首先,為了模擬 http GET 方法,我們已經預先建立一個 json 檔當做 API 回傳

https://raw.githubusercontent.com/ksw2000/ironman-2024/master/flutter-practice/http_practice/msg.json
{
    "msg": "2024 iThome 鐵人賽"
}

本次教學的程式碼:https://github.com/ksw2000/ironman-2024/tree/master/flutter-practice/http_practice

使用 dart:html 建立連線

我們目前都是以 Chrome 來做 debug,因此我們先介紹使用 dart:html 中的 HttpRequest 來發起 HTTP 請求

import 'dart:html';

const target =
    'https://raw.githubusercontent.com/ksw2000/ironman-2024/master/flutter-practice/http_practice/msg.json';

Future<void> fetchData() async {
  HttpRequest.getString(target).then((res) {
    print(res);
  });
}

我們可以簡單的設計一個觸發該函式的按鈕。

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
          useMaterial3: true,
        ),
        home: Scaffold(
          appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.inversePrimary,
            title: const Text('DEMO HTTP'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                ElevatedButton.icon(
                    onPressed: () {
                      fetchData();
                    },
                    label: const Icon(Icons.touch_app))
              ],
            ),
          ),
        ));
  }
}

執行後的結果:

https://ithelp.ithome.com.tw/upload/images/20240913/20129540winljct0dY.png

此時我們只要按下螢幕上的按鈕,終端機就會將 json檔的內容顯示出來,或者有可能會噴出 CROS 的錯誤

{
    "msg": "2024 iThome 鐵人賽"
}

在 web 的平台中,考量安全性,無法進行跨網域請求,我們目前的網域是 localhost:60381,如果打到非同個網域的地方很有可能會被擋住。通常在測試時,前端的伺服器與後端的伺服器可能掛在不同平台上,也因此,我們可以在 debug 時禁用一些安全性規則

flutter run -d chrome --web-browser-flag "--disable-web-security"

使用 dart:io 建立連線

在 Mobile 的平台中,無法使用 dart:html 這個套件,而在 Web 平台不能使用 dart:io 這個套件。

如何使用 dart:io 建立連線?首先應該建立一個 HttpClient 物件,接著使用 getgetUrl GET 方法開啟 http 連線。等待連線後我們關閉連線 req.close() 。此時回傳型態是一個 HttpClientResponse 物件,由於封包是有壓縮過的,我們可以再將其利用 utf8 解編碼最後得到 responseBody

import 'dart:convert';
import 'dart:io';

const target =
    'https://raw.githubusercontent.com/ksw2000/ironman-2024/master/flutter-practice/http_practice/msg.json';

Future<void> fetchData() async {
  var client = HttpClient();
  try {
    var req = await client.getUrl(Uri.parse(target));
    var res = await req.close();
    if (res.statusCode == 200) {
      var responseBody = await res.transform(utf8.decoder).join();
      print('Response: $responseBody');
    } else {
      print('Error: ${res.statusCode}');
    }
  } catch (e) {
    print('Request failed: $e');
  } finally {
    client.close();
  }
}

這個範例我們使用 Android 進行測試。在 Android 中如果應用程式需要使用網路時必需調整權限。我們可以修改這個 XML 檔 android/app/src/main/AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>

如果我們還需要取得網路狀態(如 Wi-Fi 或行動數據狀態),則再增加以下這個權限。

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

執行後的結果:

DEMO on Android

終端機:

A Dart VM Service on Pixel 6 is available at: http://127.0.0.1:59175/sizl36wDJzM=/
The Flutter DevTools debugger and profiler on Pixel 6 is available at: http://127.0.0.1:9101?uri=http://127.0.0.1:59175/sizl36wDJzM=/
D/ProfileInstaller(12410): Installing profile for com.example.http
I/om.example.http(12410): Background concurrent mark compact GC freed 23MB AllocSpace bytes, 3(60KB) LOS objects, 89% free, 2925KB/26MB, paused 184us,9.754ms total 42.853ms
I/flutter (12410): Response: {
I/flutter (12410):     "msg": "2024 iThome 鐵人賽"
I/flutter (12410): }

如何使用 Android Debug?

如果大家手邊有 Android 實體機,可以先開啟「開發人員模式」,並啟動「USB 偵錯功能」。另外,因為線插著很容易跑掉,所以我們也建議可以使用「無線偵錯」,只要讓手機和電腦連上同一個區網且 Android 版本夠新就可以了。

ADB使用WiFi進行除錯 - ADB Over WiFi - 可丁丹尼 @ 一路往前走2.0 (35g.tw)

接著如果大家還是很懶的話,我推薦可以使用 scrcpy 將「手機」投影到「電腦」上,它的原理也是基於 adb 連線,因此只要前面有設定好,就可以啟用螢幕投放。

為什麼我喜歡用實體機 debug 呢?因為我電腦很卡,開虛擬機他會很難受😩

https://ithelp.ithome.com.tw/upload/images/20240913/2012954088tBEIekbE.png

iOS

對於 iOS 系統,預設情況下是可以使用網路的,但僅限有加密的 https,如果要存取 http 的網站必需禁用 ATS。需修改 ios/Runner/Info.plist

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>

Conditional Imports 條件導入

如果我們希望我們的 APP 可以同時相容 web 和 mobile,我們必需使用條件導入,使 flutter 在編譯時根據不同平台調用不同函式。

.
└── http_practice/
    ├── fetch_io.dart
    ├── fetch_web.dart
    ├── fetch.dart
    └── main.dart

將原本使用 dart:html 的函式放入 fetch_web.dart 中,而使用dart:io 的函式放入 fetch_io.dart 中。

接著我們編寫 fetch.dart。我們預設使用 fetch_io.dart ;若最後編譯的平台是 web,則改用 fetch_web.dart

export 'fetch_io.dart' if (dart.library.html) 'fetch_web.dart';

main.dart 中我們只需 import fetch.dart 即可。

import 'package:flutter/material.dart';
import 'package:http/fetch.dart';

void main() {
  runApp(const MyApp());
}

// ...

注意:如果各位電腦已將手機連線,使用 flutter run 時會預先在手機上執行。此時可以使用 flutter run -d chromeflutter run -d edge 改用 web 執行。

使用第三方套件 http 建立連線

前面提到的方法需要根據不同平台使用不同函式實在有點繁鎖,因此這章節會介紹使用第三方套件完成 http 連線

首先我們要安裝第三方套件 https://pub.dev/packages/http,相信這個套件大家不陌生。我們在 Day 7 時也使用過這個套件,安裝方式與 dart 不太一樣,在 flutter 中,我們會在原本的指令前再加上 flutter 前綴

flutter pub add http

使用方法如下。在這次範例中,一同介紹如何解碼 JSON。我們可以直接使用 jsonDecode 將 String 轉為 Map 物件,再去存取裡面的資料。

import 'dart:convert';

import 'package:http/http.dart' as http;

const target =
    'https://raw.githubusercontent.com/ksw2000/ironman-2024/master/flutter-practice/http_practice/msg.json';

Future<void> fetchData() async {
  http.get(Uri.parse(target)).then((res) {
    if (res.statusCode == 200) {
      final Map<String, dynamic> data = jsonDecode(res.body);
      print(data["msg"]);
    }
  });
}

運行後會可以在終端機得到

2024 iThome 鐵人賽

後記:其實這也是我第一次嘗試用條件導入的方式來處理相容問題。另外我原本是將專案命名為 http 後來要裝 http 時才發現專案名稱會和安裝包衝突,所以又換了一個名字🫠


上一篇
Day-11 在 Flutter 中使用 FutureBuilder 進行狀態管理
下一篇
Day-13 在 Flutter 中以 JS 及 Kotlin 實作 Shared preferences 保存資料
系列文
從零開始以Flutter打造跨平台聊天APP30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言