You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中通过WebSocket实现点击按钮刷新位置详情UI的方案

Dart/Flutter 实现共享WebSocket连接的位置详情更新功能

实现步骤

1. 全局WebSocket连接管理(单例模式)

维护唯一的WebSocket连接,统一处理连接建立、消息收发、异常重连,避免多页面重复创建连接消耗资源。

2. 定义位置数据模型

标准化WebSocket消息的序列化/反序列化格式,确保前后端数据交互一致。

3. 位置列表页

展示7个预加载的位置项,点击条目携带位置ID跳转至详情页。

4. 位置详情页

通过全局WebSocket发送指定位置的更新请求,监听对应ID的返回消息并刷新UI。

5. 异常处理

处理WebSocket断开、重连及消息解析错误,保障功能稳定性。


代码实现

1. WebSocket单例管理类

import 'dart:async';
import 'dart:convert';
import 'package:web_socket_channel/web_socket_channel.dart';

class WebSocketManager {
  // 单例实例
  static final WebSocketManager _instance = WebSocketManager._internal();
  factory WebSocketManager() => _instance;
  WebSocketManager._internal();

  WebSocketChannel? _channel;
  final StreamController<Map<String, dynamic>> _messageController = StreamController.broadcast();
  Stream<Map<String, dynamic>> get messageStream => _messageController.stream;

  // 建立WebSocket连接
  Future<void> connect(String url) async {
    try {
      _channel = WebSocketChannel.connect(Uri.parse(url));
      _channel?.stream.listen(
        (message) {
          try {
            final data = jsonDecode(message) as Map<String, dynamic>;
            _messageController.add(data);
          } catch (e) {
            print('消息解析失败: $e');
          }
        },
        onError: (error) {
          print('WebSocket错误: $error');
          _reconnect(url);
        },
        onDone: () {
          print('WebSocket连接断开');
          _reconnect(url);
        },
      );
    } catch (e) {
      print('连接服务器失败: $e');
      await Future.delayed(const Duration(seconds: 3));
      await connect(url);
    }
  }

  // 发送消息到服务器
  void sendMessage(Map<String, dynamic> message) {
    if (_channel?.closeCode == null) {
      _channel?.sink.add(jsonEncode(message));
    } else {
      print('WebSocket未连接,无法发送请求');
    }
  }

  // 自动重连逻辑
  Future<void> _reconnect(String url) async {
    await Future.delayed(const Duration(seconds: 3));
    await connect(url);
  }

  // 关闭连接(仅在App退出时调用)
  void disconnect() {
    _channel?.sink.close();
    _messageController.close();
  }
}

2. 位置数据模型

class LocationData {
  final String id;
  final String name;
  final double latitude;
  final double longitude;
  final String updateTime;

  LocationData({
    required this.id,
    required this.name,
    required this.latitude,
    required this.longitude,
    required this.updateTime,
  });

  // 从JSON解析数据
  factory LocationData.fromJson(Map<String, dynamic> json) {
    return LocationData(
      id: json['id'],
      name: json['name'],
      latitude: json['latitude'].toDouble(),
      longitude: json['longitude'].toDouble(),
      updateTime: json['updateTime'],
    );
  }

  // 转为JSON格式(发送请求时使用)
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'latitude': latitude,
      'longitude': longitude,
      'updateTime': updateTime,
    };
  }
}

3. 位置列表页

import 'package:flutter/material.dart';

class LocationListPage extends StatelessWidget {
  // 模拟7个初始位置数据
  final List<LocationData> locations = [
    LocationData(id: 'loc1', name: '位置1', latitude: 39.9042, longitude: 116.4074, updateTime: '2024-05-20 10:00'),
    LocationData(id: 'loc2', name: '位置2', latitude: 31.2304, longitude: 121.4737, updateTime: '2024-05-20 10:01'),
    LocationData(id: 'loc3', name: '位置3', latitude: 23.1291, longitude: 113.2644, updateTime: '2024-05-20 10:02'),
    LocationData(id: 'loc4', name: '位置4', latitude: 30.2741, longitude: 120.1551, updateTime: '2024-05-20 10:03'),
    LocationData(id: 'loc5', name: '位置5', latitude: 30.6638, longitude: 104.0668, updateTime: '2024-05-20 10:04'),
    LocationData(id: 'loc6', name: '位置6', latitude: 22.5431, longitude: 114.0579, updateTime: '2024-05-20 10:05'),
    LocationData(id: 'loc7', name: '位置7', latitude: 36.6769, longitude: 117.0378, updateTime: '2024-05-20 10:06'),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('位置列表')),
      body: ListView.builder(
        itemCount: locations.length,
        itemBuilder: (context, index) {
          final location = locations[index];
          return ListTile(
            title: Text(location.name),
            subtitle: Text('最后更新: ${location.updateTime}'),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => LocationDetailPage(locationId: location.id, initialData: location),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

4. 位置详情页

import 'package:flutter/material.dart';

class LocationDetailPage extends StatefulWidget {
  final String locationId;
  final LocationData initialData;

  const LocationDetailPage({
    super.key,
    required this.locationId,
    required this.initialData,
  });

  @override
  State<LocationDetailPage> createState() => _LocationDetailPageState();
}

class _LocationDetailPageState extends State<LocationDetailPage> {
  late LocationData _currentData;
  final WebSocketManager _wsManager = WebSocketManager();

  @override
  void initState() {
    super.initState();
    _currentData = widget.initialData;
    // 监听当前位置的更新消息
    _wsManager.messageStream.listen((message) {
      if (message['id'] == widget.locationId) {
        setState(() {
          _currentData = LocationData.fromJson(message);
        });
      }
    });
  }

  // 发送更新请求
  void _requestLatestData() {
    _wsManager.sendMessage({
      'action': 'refresh_location',
      'location_id': widget.locationId,
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(_currentData.name)),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('位置ID: ${_currentData.id}'),
            const SizedBox(height: 8),
            Text('纬度: ${_currentData.latitude.toStringAsFixed(4)}'),
            const SizedBox(height: 8),
            Text('经度: ${_currentData.longitude.toStringAsFixed(4)}'),
            const SizedBox(height: 8),
            Text('最新更新时间: ${_currentData.updateTime}'),
            const SizedBox(height: 24),
            Center(
              child: ElevatedButton(
                onPressed: _requestLatestData,
                child: const Text('获取最新位置数据'),
              ),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    // 全局WebSocket连接不在这里关闭,避免影响其他页面
    super.dispose();
  }
}

5. App初始化WebSocket连接

在main.dart中启动时建立连接:

import 'package:flutter/material.dart';

void main() async {
  // 初始化全局WebSocket连接
  await WebSocketManager().connect('ws://your-websocket-server-url');
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '位置更新Demo',
      home: LocationListPage(),
    );
  }
}

关键注意事项

  • 单例生命周期:在App退出时调用WebSocketManager().disconnect(),可通过WidgetsBindingObserver监听App状态实现。
  • 消息过滤:详情页仅处理对应位置ID的消息,避免无关消息触发UI刷新。
  • 依赖配置:在pubspec.yaml中添加依赖:
dependencies:
  flutter:
    sdk: flutter
  web_socket_channel: ^2.4.0

内容的提问来源于stack exchange,提问作者Jason_dd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 20:43:15