Flutter實現Service + UI 全面跨平臺

作者:Karl_wei

前言:

Flutter作為跨平臺的UI框架,其可行性已經被市場所認可。UI跨端后,我們自然會希望一些運行在終端的小服務也能跨端,特別是當這個小服務還涉及到一些 UI 的展示。

我們希望Flutter能承擔這個角色,讓其跨端能力更進一步。

需求背景

我們希望在整機設備上,運行一個后臺服務,用戶通過ip地址即可調用運行在設備上的能力,同時這個服務還能喚起一些UI視圖。
舉個例子:假如路由器有Android、windows、mac三個系統的終端,需要提供一個管理后臺供用戶設置,那么路由器的后臺服務能力最好是能夠跨這三個系統的。

web后臺框架

Dart是支持編寫后臺服務的,它提供了 shelf 庫,以處理HTTP請求。整個項目,我們都是圍繞shelf庫的能力集進行開發的。

靜態資源 → shelf_static

從需求我們可以了解到,我們需要提供給用戶一個web管理后臺進行管理,web的資源自然是放在服務端的。這里我們使用 shelf_static 庫,使用非常的簡單,就一個創建靜態資源操作器的接口。

import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_static/shelf_static.dart';void main() {var handler = createStaticHandler('example/files',defaultDocument: 'index.html');io.serve(handler, 'localhost', 8080);
}

需要注意的是,必須傳入本地的絕對路徑,指定默認的文件入口。Flutter中,資源一般以asset的方式導入,在編譯過程中以二進制的形式打包在應用中,并不是普通格式的文件,那么如何傳入給createStaticHandler?
我們通過AssetBundle獲取到這些文件的字節流,并轉化成File保存到指定路徑,這個路徑就是靜態資源的路徑。

static Future<String> copyAssets() async {int now = DateTime.now().millisecondsSinceEpoch;String folderPath = '/sdcard';final manifestContent = await rootBundle.loadString('AssetManifest.json');final Map<String, dynamic> manifestMap = json.decode(manifestContent);final assetList = manifestMap.keys.where((String key) => key.startsWith('assets/web')).toList();for (final asset in assetList) {await copyAsset(asset, folderPath);}print('移動文件耗時 = ${DateTime.now().millisecondsSinceEpoch - now}毫秒');return '$folderPath/assets/web';
}static Future<File> copyAsset(String assetName, String localPath) async {int lastSeparatorIndex = assetName.lastIndexOf('/');Directory directory = Directory('$localPath${Platform.pathSeparator}${assetName.substring(0, lastSeparatorIndex)}');if (!directory.existsSync()) directory.createSync(recursive: true);ByteData data = await rootBundle.load(assetName);Uint8List bytes = data.buffer.asUint8List();final file = File('$localPath${Platform.pathSeparator}$assetName');await file.writeAsBytes(bytes);return file;
}

調用copyAssets可以拿到路徑,整個過程一般不會超過500ms,視文件體積而定。

路由 → shelf_route

現在我們已經可以訪問靜態資源了,接下來需要提供一系列的接口供前端調用,這個時候我們需要用到 shelf_route 庫。
shelf_route 支持 RESTful 風格的路由,可以處理客戶端的 GET、POST、PUT、DELETE 等 HTTP 請求,也可以從 HTTP 路徑中自動提取參數。每個路由會提供request請求體,最終返回Response的構造函數即可。
用法很簡單,下面簡單演示下如何編寫一個登錄接口。

import 'package:shelf_router/shelf_router.dart' as self_router;self_router.Router app = self_router.Router();// TODO:使用mount,前綴使用模塊命名
app.post(Apis.login, userLogin);
app.post(Apis.resetPwd, resetPassword);
app.post(Apis.signOut, singOutHandle);

Future<Response> userLogin(Request request) async {final requestBody = await request.readAsString();final Map<String, dynamic> body = json.decode(requestBody);Auth auth = Auth();var info = await auth.getUserInfo();if (info.$1 == body['username'] && info.$2 == body['password']) {String token = await auth.generateToken(body['username'], body['password']);return Response.ok(BaseResponse(Code.success, data: {'token': token}, msg: '登錄成功').toString());} else {return Response.ok(BaseResponse(Code.reject, msg: '賬號密碼錯誤').toString());}
}

中間件 → helf_multipart

一般后臺服務,都需要對部分接口進行鑒權操作,這部分的邏輯一般是通用的,一般開發過程中我們會用到中間件的機制
中間件通常被用于攔截和處理請求與響應之間的過程,以實現一些公共的應用邏輯和功能,比如認證、日志記錄、錯誤處理等等。
在Flutter中,我們使用 shelf_multipart 這個庫,通過Pipeline可以加上Middleware,這個中間件是應用于所有路由的,因此某些接口不需要這個中間件操作,直接在白名單內過濾即可;innerHandler則是執行對應的響應操作。

var middleHandler = const Pipeline().addMiddleware(authMiddleware); // 添加中間件

Middleware authMiddleware = (Handler innerHandler) {return (Request request) async {String path = request.url.path.split('?').first;if (!whitelist.contains(path)) { // 過濾白名單String? token = request.headers['Authorization'];Auth auth = Auth();var authVerify = await auth.verifyToken(token); // 驗證tokenif (!authVerify.$1) {return Response.unauthorized(BaseResponse(Code.reject, msg: authVerify.$2!).toString());} else {auth.updateTokenTime(); // 有操作則續費token時長}}final response = await innerHandler(request);return response;};
};

websocket → shelf_websocket

上面所寫的都是提供HTTP服務的,在業務中也經常存在需要websocket,我們使用 shelf_websocket 庫。跟靜態資源一樣,單一的能力只需要提供最簡單的接口:webSocketHandler

import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_web_socket/shelf_web_socket.dart';
import 'package:web_socket_channel/web_socket_channel.dart';void main() {var webSocketHandler = webSocketHandler((webSocket) {webSocket.stream.listen((message) {webSocket.sink.add("echo $message");});});shelf_io.serve(handler, 'localhost', 8080).then((server) {print('Serving at ws://${server.address.host}:${server.port}');});
}

最后我們需要把所有的handler都整合成一個服務,傳給io.serve

Handler cascadeHandler = Cascade().add(handler).add(app).add(webSocketHandler).handler; // 合并靜態資源、路由、websocket// 合入中間件
// 創建本機服務,端口8888
await io.serve(middleHandler.addHandler(cascadeHandler), '0.0.0.0', 8888);

通用服務能力

用戶鑒權

一般這種小型本機服務,登錄用戶都是互斥的,用戶權限管理我們可以簡單的使用:hive + JWT token。
采用hive來保存用戶信息,通過 dart_jsonwebtoken 庫生成token,然后在中間件攔截,對header中攜帶的token信息進行驗證,從而達到鑒權的目的。

Future<String> generateToken(String userName, String password) async {Box box = await Hive.openBox(_boxName);JWT jwt = JWT({'userName': userName,'password': password,},jwtId: const Uuid().v4(),);String token = jwt.sign(SecretKey(_secretKey));await box.put(Constant.userNameKey, userName);await box.put(Constant.pwdKey, password);await box.put(Constant.tokenKey, token);updateTokenTime();return token;
}

文件上傳

一般web后臺,都會把文件資源存儲在另一個文件服務中,比如:七牛云。不過既然是小服務,我們也希望dart能擁有這個能力。
文件上傳的路由,參數一般都是form表單;當解析到request為isMultipart時,則對文件流進行讀取,并寫到本地路徑中。
特別需要注意的是:Dart是單線程,寫文件這種耗時io操作,必須使用IOSink + stream方式寫入,不然內存會拉滿,大文件會直接讓應用崩潰。

app.post(Apis.upload, uploadFile);Future<Response> uploadFile(Request request) async {if (!request.isMultipart) {return Response.ok('Not a multipart request');} else if (request.isMultipartForm) {String? filename;String? path;await for (var part in request.parts) {var contentDisposition = part.headers['content-disposition'];filename = RegExp(r'filename="([^"]*)"').firstMatch(contentDisposition!)?.group(1);path = '${await CommonUtils.getDownloadPath()}$filename';File? file = File(path);IOSink sink = file.openWrite();await sink.addStream(part);await sink.flush();await sink.close();}return Response.ok(BaseResponse(Code.success, data: {"filePath": path}).toString());} 
}

運行機制:Service + UI

使用Flutter編寫這種后臺服務,還有一個好處是可以跨平臺的展示UI。比如:需要后臺彈出一些設置成功的toast,這個時候就非常的方便了。

Android平臺,我們在Android Service上創建一個Flutter Engine,可以直接執行到Dart代碼;當我們需要展示UI的時候,只需要通過我們的多窗口插件打開一個懸浮窗即可。

Windows平臺,我們目前還沒有在C++ 服務上運行dart代碼,而是通過把窗口設置為0在后臺運行著;當需要展示UI的時候,恢復窗口大小,然后進入指定的UI界面即可。

結語

在常規業務場景基本都不會使用dart開發后臺服務;針對整機小型服務的需求,我認為Flutter還是挺香的,內存不存在隱患,還能前后端都跨平臺。
本篇文章,分享了整個shelf框架編寫web服務的經驗,我認為在這個小眾的類目中這篇文章算是非常齊全了;同時我們也驗證了Flutter/Dart在web服務的可行性,Flutter的業務價值進一步提升~

Android 學習筆錄

Android 性能優化篇:https://qr18.cn/FVlo89
Android 車載篇:https://qr18.cn/F05ZCM
Android 逆向安全學習筆記:https://qr18.cn/CQ5TcL
Android Framework底層原理篇:https://qr18.cn/AQpN4J
Android 音視頻篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(內含Compose):https://qr18.cn/A0gajp
Kotlin 篇:https://qr18.cn/CdjtAF
Gradle 篇:https://qr18.cn/DzrmMB
OkHttp 源碼解析筆記:https://qr18.cn/Cw0pBD
Flutter 篇:https://qr18.cn/DIvKma
Android 八大知識體:https://qr18.cn/CyxarU
Android 核心筆記:https://qr21.cn/CaZQLo
Android 往年面試題錦:https://qr18.cn/CKV8OZ
2023年最新Android 面試題集:https://qr18.cn/CgxrRy
Android 車載開發崗位面試習題:https://qr18.cn/FTlyCJ
音視頻面試題錦:https://qr18.cn/AcV6Ap

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/43448.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/43448.shtml
英文地址,請注明出處:http://en.pswp.cn/news/43448.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

去除UI切圖邊緣上多余的線條

最近接到UI切圖&#xff0c;放進項目&#xff0c;顯示邊緣有多余線條&#xff0c;影響UI美觀。開始以為切圖沒切好&#xff0c;實則不是。如圖&#xff1a; ->解決&#xff1a; 將該圖片資源WrapMode改為Clamp

2021年03月 C/C++(三級)真題解析#中國電子學會#全國青少年軟件編程等級考試

第1題&#xff1a;找和為K的兩個元素 在一個長度為n(n < 1000)的整數序列中&#xff0c;判斷是否存在某兩個元素之和為k。 時間限制&#xff1a;1000 內存限制&#xff1a;65536 輸入 第一行輸入序列的長度n和k&#xff0c;用空格分開。 第二行輸入序列中的n個整數&#xff…

Rancher-RKE-install 部署k8s集群

一、為什么用Rancher-RKE-install 1.CNCF認證的k8s安裝程序。 2.有中文文檔。 二、安裝步驟 1.下載Rancher-Rke的二進制包-下面是項目的地址 GitHub - rancher/rke: Rancher Kubernetes Engine (RKE), an extremely simple, lightning fast Kubernetes distrib…

探索樹算法:C語言實現二叉樹與平衡樹

探索樹算法&#xff1a;C語言實現二叉樹與平衡樹 樹是計算機科學中一個重要且廣泛應用的數據結構&#xff0c;它在許多領域都有著重要作用。本篇博客將深入介紹兩種常見的樹算法&#xff1a;二叉樹遍歷和平衡二叉樹&#xff08;AVL樹&#xff09;&#xff0c;并提供在C語言中的…

Python學習筆記_基礎篇(五)_數據類型之字典

一.基本數據類型 整數&#xff1a;int 字符串&#xff1a;str(注&#xff1a;\t等于一個tab鍵) 布爾值&#xff1a; bool 列表&#xff1a;list 列表用[] 元祖&#xff1a;tuple 元祖用&#xff08;&#xff09; 字典&#xff1a;dict 注&#xff1a;所有的數據類型都存在想對…

Python Opencv實踐 - 圖像平移

import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR)#圖像平移 #cv.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) # M是仿射變換矩陣&#xff0c;對于平移來說M是一…

《Zookeeper》源碼分析(十五)之 選舉算法

FastLeaderElection FastLeaderElection實現了接口Election&#xff0c;選舉方法為lookForLeader()&#xff0c;選舉算法的核心邏輯也在該方法中。 數據結構 構造函數 start() 啟動選舉通信網絡 lookForLeader() 選舉核心算法 FastLeaderElection.logicalclock屬性用于標…

從零開發短視頻電商 自動化測試WebUI端到端測試-Playwright

文章目錄 Playwright是什么Playwright入門示例添加Maven依賴示例代碼啟動驗證 功能自動等待內置Web斷言可視化UI模式減慢操作截圖錄屏腳本錄制 高級識別驗證碼 Playwright是什么 https://playwright.dev/ https://playwright.dev/java/ Playwright為現代 Web 應用程序提供可…

linux 系統中vi 編輯器和庫的制作和使用

目錄 1 vim 1.1 vim簡單介紹 1.2 vim的三種模式 1.3 vim基本操作 1.3.1命令模式下的操作 1.3.2 切換到文本輸入模式 1.3.3 末行模式下的操作 2 gcc編譯器 2.1 gcc的工作流程 2.2 gcc常用參數 3 靜態庫和共享&#xff08;動態&#xff09;庫 3.1庫的介紹 3.2靜態…

實現Java異步調用的高效方法

文章目錄 為什么需要異步調用&#xff1f;Java中的異步編程方式1. 使用多線程2. 使用Java異步框架 異步調用的關鍵細節結論 &#x1f389;歡迎來到Java學習路線專欄~實現Java異步調用的高效方法 ☆* o(≧▽≦)o *☆嗨~我是IT陳寒&#x1f379;?博客主頁&#xff1a;IT陳寒的博…

Python 3 使用HBase 總結

HBase 簡介和安裝 請參考文章&#xff1a;HBase 一文讀懂 Python3 HBase API HBase 前期準備 1 安裝happybase庫操作hbase 安裝該庫 pip install happybase2 確保 Hadoop 和 Zookeeper 可用并開啟 確保Hadoop 正常運行 確保Zookeeper 正常運行3 開啟HBase thrift服務 使用命…

【EI復現】一種建筑集成光儲系統規劃運行綜合優化方法(Matlab代碼實現)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;歡迎來到本博客????&#x1f4a5;&#x1f4a5; &#x1f3c6;博主優勢&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客內容盡量做到思維縝密&#xff0c;邏輯清晰&#xff0c;為了方便讀者。 ??座右銘&a…

目標檢測(Object Detection)

文章目錄 1. 目標檢測1.1 目標檢測簡要概述及名詞解釋1.2 IOU1.3 TP TN FP FN1.4 precision&#xff08;精確度&#xff09;和recall&#xff08;召回率&#xff09; 2. 邊框回歸Bounding-Box regression3. Faster R-CNN3.1 Faster-RCNN&#xff1a;conv layer3.2 Faster-RCNN&…

跨境電商平臺(例如阿里巴巴、蝦皮)的商品數據如何收集?

跨境電商是指通過互聯網&#xff0c;以跨越國家或地區邊界的方式進行電子商務交易的商業行為。傳統的電子商務通常是在同一國家或地區內進行&#xff0c;而跨境電商則側重于跨國貿易。跨境電商通過在線平臺&#xff08;如阿里巴巴、亞馬遜等&#xff09;或第三方服務商&#xf…

【數據結構】堆的實現,堆排序以及TOP-K問題

目錄 1.堆的概念及結構 2.堆的實現 2.1初始化堆 2.2銷毀堆 2.3取堆頂元素 2.4返回堆的大小 2.5判斷是否為空 2.6打印堆 2.7插入元素 2.8堆的向上調整 2.9彈出元素 2.10堆的向下調整 3. 建堆時間復雜度 4. 堆的應用 4.1 堆排序 4.2 TOP-K問題 1.堆的概念及結構 …

FFmpeg5.0源碼閱讀——VideoToobox硬件解碼

摘要&#xff1a;本文描述了FFmpeg中videotoobox解碼器如何進行解碼工作&#xff0c;如何將一個編碼的碼流解碼為最終的裸流。 ??關鍵字&#xff1a;videotoobox,decoder,ffmpeg ??VideoToolbox 是一個低級框架&#xff0c;提供對硬件編碼器和解碼器的直接訪問。 它提供視頻…

WebRTC音視頻通話-RTC直播本地視頻及相冊視頻文件

WebRTC音視頻通話-RTC直播本地視頻及相冊視頻文件 WebRTC音視頻通話-RTC直播本地視頻文件效果圖如下 WebRTC音視頻通話-RTC直播本地視頻文件時候&#xff0c;用到了AVPlayer、CADisplayLink。 一、通過AVPlayer播放本地視頻 AVPlayer是什么&#xff1f; AVPlayer是基于AV…

35_windows環境debug Nginx 源碼-CLion配置CMake和啟動

文章目錄 生成 CMakeLists.txt 組態檔35_windows環境debug Nginx 源碼-CLion配置CMake和啟動生成 CMakeLists.txt 組態檔 修改auto目錄configure文件,在 . auto/make 上邊增加 . auto/cmake, 大概在 106 行。在 auto 目錄下創建cmake 文件其內容如下: #!/usr/bin/env bash NG…

從外部訪問K8s中Pod的五種方式

hostNetwork、 hostPort、 NodePort、 LoadBalancer、 Ingress 暴露Pod與Service一樣&#xff0c;因為Pod就是Service的backend 1、hostNetwork&#xff1a;true 這是一種直接定義 Pod 網絡的方式。 如果在 Pod 中使用 hostNetwork:true 配置&#xff0c; pod 中運行的應用程序…

C++頭文件

C頭文件 一般頭文件特殊頭文件windows.hbits/stdc.h 一般頭文件 C頭文件是一種包含預定義函數、類和變量聲明的文件。它們通常用于在源代碼文件中引入外部庫或模塊的功能。 頭文件的作用是提供程序所需的聲明信息&#xff0c;以便在源代碼文件中使用這些聲明。當你在源代碼文…