Flutter - 从 websocket 消息动态创建小部件 - 未处理的异常:在构造函数中调用 setState()

Posted

技术标签:

【中文标题】Flutter - 从 websocket 消息动态创建小部件 - 未处理的异常:在构造函数中调用 setState()【英文标题】:Flutter - Create Widget dynamically from websocket message - Unhandled Exception: setState() called in constructor 【发布时间】:2021-07-07 16:37:50 【问题描述】:

当我从 websocket 接收数据时,我想动态添加一个 ListTile。 当我将新数据添加到全局列表时,ListView 似乎还没有准备好。

我收到了这个错误

[ERROR:flutter/shell/common/shell.cc(242)] Dart 未处理异常:setState() 在构造函数中调用:_MyHomePageState#4c6c6(生命周期状态:已创建,无小部件,未安装)

处理这段代码时发生的错误

  void addItem(Item item) 
    setState(() 
      items.add(item);
    );
  

我尝试应用这些解决方案,但没有一个适合我:

Flutter Dart setState not working: Unhandled Exception: setState() called in constructor: ...(lifecycle state: created, no widget, not mounted) Flutter Unhandled Exception: setState() called in constructor:

这是我的测试应用:

依赖

dependencies:
  flutter:
    sdk: flutter
  web_socket_channel: ^2.0.0
  intl: ^0.17.0

main.dart

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:test_add_from_websocket/Item.dart';
import 'package:test_add_from_websocket/websocket.dart';

void main() 
  runApp(MyApp());


Future _init() async 
  final WebSocket _socket = WebSocket();
  _socket.connect('wss://echo.websocket.org', connectionListener, messageListener);


void connectionListener(WebSocket socket, bool connected) 
  print('Status : ' + (connected ? 'Connected' : 'Failed to connect'));


void messageListener(WebSocket socket, String message) 
  print('websocket received: $message');
  Map itemMap = jsonDecode(message);
  var _item = Item.fromJson(itemMap);
  MyHomePage().addItem(_item);


class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'Test',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: FutureBuilder(
        future: _init(),
        builder: (context, snapshot) 
          if (snapshot.connectionState == ConnectionState.done) 
            return MyHomePage();
           else 
            return Text('Loading');
          
        ,
      ),
    );
  


class MyHomePage extends StatefulWidget 
  MyHomePage(Key key) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();

  void addItem(Item item) 
    _MyHomePageState().addItem(item);
  


class _MyHomePageState extends State<MyHomePage> 
  void addItem(Item item) 
    setState(() 
      items.add(item);
    );
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        title: Text('Add from websocket event'),
      ),
      body: ListView.builder(
          itemCount: items.length,
          itemBuilder: (context, index) 
            return ListTile(
              leading: Icon(Icons.access_time),
              title: Text(items[index].time),
            );
          ),
      floatingActionButton: FloatingActionButton(
        onPressed: () 
          DateTime now = DateTime.now();
          Item _item = Item(
            time: DateFormat.Hm().format(now),
          );
          print(_item.time);
          final WebSocket _socket = WebSocket();
          _socket.send(jsonEncode(_item));
        ,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  

我的 WebSocket 类

import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:web_socket_channel/status.dart' as status;

class WebSocket 
  static final WebSocket _instance = WebSocket._internal();

  factory WebSocket() => _instance;

  WebSocket._internal();

  String _url;
  bool _connected;
  WebSocketChannel _channel;

  connect(String url, Function connectionListener, Function messageListener) 
    try 
      _url = url;
      _channel = IOWebSocketChannel.connect(_url);
      _connected = true;
      connectionListener(_instance, _connected);
      print('websocket connected to  $_url');
      _channel.stream.listen(
        (message) 
          print('websocket receive $message');
          messageListener(_instance, message);
        ,
        onDone: () 
          _connected = false;
          connectionListener(_instance, _connected);
          print('websocket closed');
        ,
        onError: (error) 
          print('websocket error $error');
        ,
      );
     catch (e) 
      print('websocket exception $e');
      _connected = false;
      connectionListener(_connected);
    
  

  close() 
    if (_connected) 
      _channel.sink.close(status.normalClosure);
    
  

  bool send(String message) 
    try 
      if (_channel == null) throw ('_channel undefined');
      _channel.sink.add(message);
      return true;
     catch (e) 
      return false;
    
  

还有我的 Item 类

class Item 
  final String time;

  Item(this.time);

  Item.fromJson(Map<String, dynamic> json) : time = json['time'];
  Map<String, dynamic> toJson() => 'time': time;


List<Item> items = [
  Item(time: '10:22'),
  Item(time: '11:22'),
];

谢谢

【问题讨论】:

【参考方案1】:

问题是您正在调用 addItem,而后者又调用了 setState,但是当您调用它时还没有状态

尝试将您的全局函数移动到状态类并从 initState 调用它们

【讨论】:

以上是关于Flutter - 从 websocket 消息动态创建小部件 - 未处理的异常:在构造函数中调用 setState()的主要内容,如果未能解决你的问题,请参考以下文章

websocket协议

如何从 websocket 端点外部发出 websocket 消息?

带有 Spring-boot 后端的 Flutter websocket

从网络选项卡捕获 Websocket 消息

从路由中发出 websocket 消息

如何使用 STOMP 从 Spring WebSocket 服务器向 WebSocket 客户端发送消息?