Flutter 使用 SignalR Asp.Net Core 搭建即時通訊 DEMO

本人剛開始學習Flutter,對Flutter還不是很瞭解。這個例子 首先是日後項目有可能會用到,其次是加深學習。

文中的說明中難免有不合理的地方,詳細文檔請參閱官方文檔:

SIgnalR    https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/signalr?view=aspnetcore-3.0&tabs=visual-studio

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">&nbsp;</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">&nbsp;</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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
    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('發送'),
                  ),
                ],
              )
            ],
          ),
        ),
      ),
    );
  }
}
  • 頁面效果

      

  • 移動端效果

              

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章