本人剛開始學習Flutter,對Flutter還不是很瞭解。這個例子 首先是日後項目有可能會用到,其次是加深學習。
文中的說明中難免有不合理的地方,詳細文檔請參閱官方文檔:
Flutter https://flutter.dev/docs https://flutterchina.club
Flutter signalr_client https://pub.dev/packages/signalr_client#-readme-tab-
客戶端 Android Flutter
服務端 Windows Asp.Net Core 注意本文只針對服務端是 Asp.Net Core 而不是 Farmework
服務端Demo
- 首先創建Asp.Net Core 項目。
- 通過Nuget 安裝 Microsoft.AspNetCore.SignalR。
- 在項目上右鍵-添加-添加客戶端
如圖所示 提供程序選中 unpkg,文件只選着 signalr.js signalr.js.min ,目標位置 wwwroot/js/signalr/ 然後點擊安裝。
- 在 SignalRChat 項目文件夾中,創建 Hubs 文件夾 。
- 在 Hubs 文件夾中,使用以下代碼創建 ChatHub.cs 文件
public class ChatHub : Hub
{
public override Task OnConnectedAsync()
{
return base.OnConnectedAsync();
}
public async Task SendMessage(string name,string message)
{
await Clients.All.SendAsync("NewMessage", message);
}
}
- 修改Startup.cs 文件
在 ConfigureServices 中 添加 services.AddSignalR();
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSignalR();
}
在Configure中 添加 endpoints.MapHub<ChatHub>("/chatHub");
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapHub<ChatHub>("/chatHub");
});
}
- 修改Index.cshtml 爲如下所示
@page
<div class="container">
<div class="row"> </div>
<div class="row">
<div class="col-2">User</div>
<div class="col-4"><input type="text" id="userInput" /></div>
</div>
<div class="row">
<div class="col-2">Message</div>
<div class="col-4"><input type="text" id="messageInput" /></div>
</div>
<div class="row"> </div>
<div class="row">
<div class="col-6">
<input type="button" id="sendButton" value="Send Message" />
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<hr />
</div>
</div>
<div class="row">
<div class="col-6">
<ul id="messagesList"></ul>
</div>
</div>
<script src="~/js/signalr/dist/browser/signalr.js"></script>
<script src="~/js/chat.js"></script>
- 在 wwwroot -> js 文件夾中新建 chat.js 文件
"use strict";
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();
//Disable send button until connection is established
document.getElementById("sendButton").disabled = true;
connection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
var encodedMsg = user + " says " + msg;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
});
connection.start().then(function () {
document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
return console.error(err.toString());
});
document.getElementById("sendButton").addEventListener("click", function (event) {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
connection.invoke("SendMessage", user, message).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
});
- F5 運行項目
- 在任務欄右下角找到 IIS Express 圖表並右鍵點擊,選着顯示所有應用程序,選着你的網站名稱並點擊下面的配置。
- 找到你的網站的配置節 在 bindings 中添加一個指定你電腦ip的配置節。並在你的電腦防火牆中允許你設置的網站端口的入站規則。現在你需要關閉 VisualStudio 後用管理員權限重新打開並允許項目。
客戶端 Demo
- 首先使用 AndroidStudio 新建一個 Fultter的項目。
- 編輯 pubspec.yaml 在 dependencies 中 添加
signalr_client: ^0.1.6
- 在Fultter中引入
import 'package:signalr_client/signalr_client.dart';
主要代碼如下
// SignalR地址
static final serverUrl = "http://192.168.1.7:24361/chatHub";
// SignalR Connection
final hubConnection = HubConnectionBuilder().withUrl(serverUrl).build();
//初始化連接
void _initSignalR() async {
print("Begin Connection");
print(serverUrl);
try {
hubConnection.onclose((error) => print("Connection Closed"));
await hubConnection.start();
hubConnection.on("ReceiveMessage", (e) => {receiveMessage(e[0], e[1])});
_conState = "連接成功";
} catch (e) {
_conState = "連接失敗";
}
}
// 接受消息
void receiveMessage(name, msg) {
setState(() {
ltMsg.add("來自[" + name + "]的即時消息:" + msg);
});
}
// 發送消息
void sendMsg() async {
final result = await hubConnection.invoke("SendMessage",
args: <Object>[_controllerName.text, _controllerMsg.text]);
}
- 完整代碼如下
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:signalr_client/signalr_client.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// 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: 'Flutter SignalR 首頁'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
var _msg = "等待消息";
var _conState = "等待連接";
_MyHomePageState() {
_initSignalR();
}
// The location of the SignalR Server.
static final serverUrl = "http://192.168.1.7:24361/chatHub";
// SignalR Connection
final hubConnection = HubConnectionBuilder().withUrl(serverUrl).build();
// Msg List
List<String> ltMsg = ["測試1", "測試2"];
// Msg
final TextEditingController _controllerMsg = new TextEditingController();
// Name
final TextEditingController _controllerName = new TextEditingController();
//初始化連接
void _initSignalR() async {
print("Begin Connection");
print(serverUrl);
try {
hubConnection.onclose((error) => print("Connection Closed"));
await hubConnection.start();
hubConnection.on("ReceiveMessage", (e) => {receiveMessage(e[0], e[1])});
_conState = "連接成功";
} catch (e) {
_conState = "連接失敗";
}
}
// 接受消息
void receiveMessage(name, msg) {
setState(() {
ltMsg.add("來自[" + name + "]的即時消息:" + msg);
});
}
// 發送消息
void sendMsg() async {
final result = await hubConnection.invoke("SendMessage",
args: <Object>[_controllerName.text, _controllerMsg.text]);
}
// 計數器
void _incrementCounter() {
setState(() {
_counter++;
//ltMsg.add("Test");
});
}
// 獲取列表數據
_getListData() {
List<Widget> widgets = [];
for (int i = 0; i < ltMsg.length; i++) {
widgets.add(new Padding(
padding: new EdgeInsets.all(10.0), child: new Text(ltMsg[i])));
}
return widgets;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SingleChildScrollView(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
textDirection: TextDirection.ltr,
children: <Widget>[
RaisedButton(
color: Colors.red,
child: Text("重新連接SignalR 服務器"),
textColor: Colors.white,
onPressed: _initSignalR,
),
Spacer(
flex: 1,
),
Text(
"SignalR狀態:",
style: TextStyle(fontSize: 20),
),
Text(
_conState,
style: TextStyle(fontSize: 20),
),
],
),
Container(
height: 400,
child: ListView(children: _getListData()),
),
Text(
'點擊計數:',
style: TextStyle(fontSize: 30),
),
Text(
'當前點擊數量:$_counter',
style: TextStyle(fontSize: 20),
),
Column(
children: <Widget>[
new TextField(
controller: _controllerName,
decoration: new InputDecoration(
hintText: '暱稱',
),
),
new TextField(
controller: _controllerMsg,
decoration: new InputDecoration(
hintText: '消息',
),
),
new MaterialButton(
color: Colors.blueAccent,
minWidth: double.infinity,
height: 50,
onPressed: () {
sendMsg();
_incrementCounter();
// showDialog(
// context: context,
// child: new AlertDialog(
// title: new Text('What you typed'),
// content: new Text(_controller.text),
// ),
// );
},
child: new Text('發送'),
),
],
)
],
),
),
),
);
}
}
- 頁面效果
- 移動端效果