微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Flutter setState 不更新 RichText 小部件

如何解决Flutter setState 不更新 RichText 小部件

我正在尝试使用异步接收的消息填充 RichText 小部件。消息存储在 TextSpan 对象列表中。在附加的示例中,我将对象从计时器添加到列表中,但 RichText 小部件未显示新消息。有人能看到我做错了什么吗?

import 'package:Flutter/material.dart';
import 'dart:async';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',theme: ThemeData(
        primarySwatch: Colors.blue,),home: TstWidget(name: 'tst'),);
  }
}

class TstWidget extends StatefulWidget {
  final String name;
  TstWidget({this.name});

  @override
  _TstWidgetState createState() => _TstWidgetState();
}

class _TstWidgetState extends State<TstWidget> {
  Timer _timer;
  int _idx = 10;
  List<TextSpan> _messages = [];

  @override
  void initState() {
    super.initState();

    // Create a couple of initial messages just so see something in the RichText widget.
    _messages.add(TextSpan(text: 'pre set message 1\n'));
    _messages.add(TextSpan(text: 'pre set message 2\n'));

    _timer = new Timer.periodic(new Duration(seconds: 1),(time) {
      print('adding message $_idx');
      setState(() {
        _messages.add(TextSpan(text: 'this is message $_idx\n'));
      });

      if (--_idx == 0) {
        print('timer complete');
        _timer.cancel();
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.name),body: Container(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,children: [
            Expanded(
              child: Container(
                decoration: Boxdecoration(border: Border.all(width: 1)),margin: EdgeInsets.all(4.0),padding: EdgeInsets.all(4.0),child: RichText(
                  text: TextSpan(
                    children: _messages,],);
  }
}

解决方法

Flutter 不支持这种情况。 TextSpan 的 children 属性文档:

不支持在创建 TextSpan 后修改列表,可能会产生意外结果。

https://api.flutter.dev/flutter/painting/TextSpan/children.html

简单的解决方案是在每次渲染时复制列表:

child: RichText(
  text: TextSpan(
    children: List.from(_messages),),

不同的,可能更好的解决方案是为每个消息使用单独的 TextSpans(将消息映射到 TextSpan),如示例中所示:

RichText(
  text: TextSpan(
    text: 'Hello ',style: DefaultTextStyle.of(context).style,children: <TextSpan>[
      TextSpan(text: 'bold',style: TextStyle(fontWeight: FontWeight.bold)),TextSpan(text: ' world!'),],)
,

小部件没有更新,因为您没有更改实际的 List 对象,而只是向现有的 List 对象添加更多项目。

还有setState

通知框架该对象的内部状态已经改变。

因此,要实际更改 _messages 对象的状态,您需要使用更新的项目创建一个新的 List 并将其分配给您的 _messages 变量,以便框架可以检测到状态发生了变化。

为此,您应该更新:

setState(() {
        _messages.add(TextSpan(text: 'this is message $_idx\n'));
      });

到:

setState(() {
        _messages  = [..._messages,TextSpan(text: 'this is message $_idx\n'))];
      });

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。