跳到主要内容

เนื้อหาภาษาไทย / 泰语内容

系统指南 内容已审核

การใช้ WebSockets ใน Flutter

บทความนี้แนะนำการใช้ WebSockets ใน Flutter เพื่อสร้างแอปพลิเคชันที่สื่อสารแบบ Real-time โดยใช้ package `web_socket_channel` และอธิบายถึงข้อดีของ WebSockets เหนื...

การใช้ WebSockets ใน Flutter: สร้าง Realtime Communication กับ Server

ในยุคปัจจุบันที่ข้อมูลเปลี่ยนแปลงอยู่ตลอดเวลา การสร้างแอปพลิเคชันที่สามารถสื่อสารแบบ Real-time ได้กลายเป็นสิ่งสำคัญอย่างยิ่ง WebSockets คือเทคโนโลยีที่ตอบโจทย์นี้ได้เป็นอย่างดี โดยเปิดช่องทางการสื่อสารแบบสองทาง (bidirectional) ระหว่าง client และ server ทำให้ข้อมูลสามารถถูกส่งไปมาได้ทันทีโดยไม่ต้องมีการร้องขอซ้ำๆ เหมือน HTTP polling

บทความนี้จะพาคุณไปทำความรู้จักกับการใช้งาน WebSockets ใน Flutter เพื่อสร้างแอปพลิเคชันที่สามารถรับส่งข้อมูลแบบ Real-time ได้อย่างมีประสิทธิภาพ โดยเราจะเน้นไปที่การใช้งาน WebSocket client และการจัดการข้อมูลผ่าน Streams

ทำไมต้อง WebSockets?

ก่อนที่จะลงมือเขียนโค้ด เรามาทำความเข้าใจกันก่อนว่าทำไม WebSockets ถึงมีความสำคัญและแตกต่างจาก HTTP polling อย่างไร:

  • Real-time Communication: WebSockets ช่วยให้ข้อมูลถูกส่งไปมาได้ทันทีโดยไม่ต้องรอการร้องขอจาก client ทำให้แอปพลิเคชันสามารถตอบสนองต่อการเปลี่ยนแปลงของข้อมูลได้อย่างรวดเร็ว
  • Bidirectional Communication: WebSockets เปิดช่องทางการสื่อสารแบบสองทาง ทำให้ทั้ง client และ server สามารถส่งข้อมูลให้กันและกันได้ตลอดเวลา
  • Reduced Latency: เนื่องจากไม่มี overhead จากการสร้าง connection ใหม่ทุกครั้งเหมือน HTTP polling, WebSockets จึงมี latency ที่ต่ำกว่ามาก
  • Efficiency: WebSockets ใช้ resource น้อยกว่า HTTP polling เพราะไม่ต้องมีการสร้าง connection ใหม่ทุกครั้ง

WebSockets เหมาะสำหรับแอปพลิเคชันที่ต้องการการอัปเดตข้อมูลแบบ Real-time เช่น:

  • แอปพลิเคชันแชท
  • แอปพลิเคชันติดตามหุ้น
  • เกมออนไลน์
  • แอปพลิเคชัน IoT

WebSocket Client ใน Flutter

Flutter มี package ที่ชื่อว่า web_socket_channel ที่ช่วยให้เราสามารถใช้งาน WebSockets ได้อย่างง่ายดาย ในการเริ่มต้นใช้งาน ให้เพิ่ม dependency นี้ลงใน pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  web_socket_channel: ^2.0.0

หลังจากนั้น ให้ทำการ flutter pub get เพื่อติดตั้ง package

ต่อไป เราจะสร้าง WebSocket client เพื่อเชื่อมต่อกับ WebSocket server:

import 'package:flutter/material.dart';
import 'package:web_socket_channel/web_socket_channel.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'WebSocket Demo',
      home: MyHomePage(
        title: 'WebSocket Demo Home Page',
        channel: WebSocketChannel.connect(
          Uri.parse('wss://echo.websocket.events'), // Replace with your WebSocket server URL
        ),
      ),
    );
  }
}

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() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Form(
              child: TextFormField(
                controller: _controller,
                decoration: InputDecoration(labelText: 'Send a message:'),
              ),
            ),
            SizedBox(height: 24),
            StreamBuilder(
              stream: widget.channel.stream,
              builder: (context, snapshot) {
                return Text(snapshot.hasData ? '${snapshot.data}' : '');
              },
            )
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _sendMessage,
        tooltip: 'Send message',
        child: 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);
      _controller.clear();
    }
  }

  @override
  void dispose() {
    widget.channel.sink.close();
    super.dispose();
  }
}

โค้ดนี้สร้างแอปพลิเคชันง่ายๆ ที่เชื่อมต่อกับ WebSocket server ที่ wss://echo.websocket.events (ซึ่งเป็น server ที่ใช้สำหรับทดสอบ WebSocket) และแสดงข้อความที่ได้รับจาก server ใน StreamBuilder

คำอธิบายโค้ด:

  • WebSocketChannel.connect(Uri.parse('wss://echo.websocket.events')): สร้าง WebSocket channel เพื่อเชื่อมต่อกับ server
  • widget.channel.stream: เป็น Stream ที่รับข้อมูลจาก WebSocket server
  • widget.channel.sink.add(_controller.text): ส่งข้อความไปยัง WebSocket server
  • widget.channel.sink.close(): ปิด WebSocket connection เมื่อ Widget ถูก dispose
  • StreamBuilder: Widget ที่ใช้สำหรับรับข้อมูลจาก Stream และแสดงผล

การจัดการ State และ Streams อย่างมีประสิทธิภาพ

ในตัวอย่างข้างต้น เราใช้ StreamBuilder เพื่อรับข้อมูลจาก WebSocket stream โดยตรง แต่ในแอปพลิเคชันที่ซับซ้อนมากขึ้น การจัดการ state และ streams อย่างมีประสิทธิภาพเป็นสิ่งสำคัญ เราสามารถใช้ Provider หรือ BLoC pattern เพื่อจัดการ state และ streams ได้อย่างมีโครงสร้าง

ตัวอย่างการใช้ Provider:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:web_socket_channel/web_socket_channel.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => WebSocketProvider(
        WebSocketChannel.connect(
          Uri.parse('wss://echo.websocket.events'),
        ),
      ),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'WebSocket Demo',
      home: MyHomePage(title: 'WebSocket Demo Home Page'),
    );
  }
}

class WebSocketProvider extends ChangeNotifier {
  final WebSocketChannel channel;
  String _message = '';

  WebSocketProvider(this.channel) {
    channel.stream.listen((data) {
      _message = data;
      notifyListeners();
    });
  }

  String get message => _message;

  void sendMessage(String message) {
    channel.sink.add(message);
  }

  @override
  void dispose() {
    channel.sink.close();
    super.dispose();
  }
}

class MyHomePage extends StatelessWidget {
  final String title;

  MyHomePage({Key? key, required this.title}) : super(key: key);

  final TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    final webSocketProvider = Provider.of<WebSocketProvider>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Form(
              child: TextFormField(
                controller: _controller,
                decoration: InputDecoration(labelText: 'Send a message:'),
              ),
            ),
            SizedBox(height: 24),
            Text(webSocketProvider.message),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          webSocketProvider.sendMessage(_controller.text);
          _controller.clear();
        },
        tooltip: 'Send message',
        child: Icon(Icons.send),
      ),
    );
  }
}

ในตัวอย่างนี้ เราสร้าง WebSocketProvider ที่จัดการ WebSocket channel และ state ของข้อความที่ได้รับ เมื่อมีข้อความใหม่เข้ามา _message จะถูกอัปเดตและ notifyListeners() จะถูกเรียกเพื่อแจ้งให้ Widget ที่ listen อยู่ทราบถึงการเปลี่ยนแปลง

เหตุผลเชิงออกแบบ: การใช้ Provider ช่วยให้เราแยก logic ของ WebSocket ออกจาก UI ทำให้โค้ดมีความสะอาดและ maintainable มากขึ้น นอกจากนี้ Provider ยังช่วยให้เราสามารถเข้าถึง WebSocket channel และ state ได้จากทุกที่ใน Widget tree

ข้อควรระวังในการใช้งาน WebSockets

ถึงแม้ว่า WebSockets จะมีประโยชน์อย่างมาก แต่ก็มีข้อควรระวังบางประการที่ต้องคำนึงถึง:

  • Error Handling: ควรมีการจัดการ error ที่อาจเกิดขึ้นระหว่างการเชื่อมต่อ WebSocket เช่น connection timeout หรือ server error
  • Reconnection: ในกรณีที่ connection ถูกตัดขาด ควรมีการ implement logic สำหรับการ reconnect อัตโนมัติ
  • Security: ควรใช้ WSS (WebSocket Secure) แทน WS เพื่อเข้ารหัสข้อมูลที่ส่งผ่าน WebSocket
  • Scalability: เมื่อมีจำนวน client เพิ่มขึ้น ควรมีการออกแบบ server ให้สามารถรองรับ load ได้อย่างมีประสิทธิภาพ

สรุป

WebSockets เป็นเครื่องมือที่มีประสิทธิภาพในการสร้างแอปพลิเคชันที่ต้องการการสื่อสารแบบ Real-time ใน Flutter เราสามารถใช้ package web_socket_channel เพื่อเชื่อมต่อกับ WebSocket server และใช้ Streams เพื่อจัดการข้อมูลที่ได้รับ การจัดการ state และ streams อย่างมีประสิทธิภาพด้วย Provider หรือ BLoC pattern จะช่วยให้โค้ดมีความสะอาดและ maintainable มากขึ้น

ในบทความถัดไป เราจะไปเรียนรู้เกี่ยวกับการใช้ Isolate ใน Flutter เพื่อให้แอปพลิเคชันสามารถทำงาน background task ได้โดยไม่กระทบต่อ UI thread

Office.in.th Custom Service

想基于此结构开发或定制系统?

Office.in.th 承接企业级 Web Application、API Integration 以及政府/企业管理系统的设计与开发,并提供长期支持。

查看软件开发服务