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

Flutter中如何动态更新列表内元素样式,实现TextSpan透明度定时更新

解决方案

问题根因

  • TextSpan 是不可变对象,无法直接修改已创建实例的透明度属性,必须创建新的TextSpan实例替换列表中的旧元素
  • 修改完列表数据后未调用setState(),Flutter框架无法感知状态变更,不会触发页面重绘

具体修改步骤

  1. 在State类中新增定时器、当前高亮单词索引两个状态变量
  2. 初始化TextSpan时设置默认透明度
  3. 编写定时器触发的更新逻辑,每次修改完列表后调用setState()触发重绘
  4. 页面销毁时取消定时器避免内存泄漏

修改后完整可运行代码

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

class HomePeageReaderWidget extends StatefulWidget {
  const HomePeageReaderWidget({Key? key}) : super(key: key);

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

class _HomePageReaderWidgetState extends State<HomePeageReaderWidget> {
  var text =
      """Gaziantep şehri Türkiye'nin güneydoğusundadır. Çok güzel bir şehirdir. Belki İstanbul ve Ankara kadar büyük değildir, ama yine de burada bir milyonun üstünde insan yaşıyor. Gaziantep'te iş alanları çok geniştir, herkes çalışıyor. Birçok fabrikalar ve atölyeler vardır. Ayrıca bir de Gaziantep Üniversitesi vardır. Üniversite şehirden biraz uzak, ama yurt da üniversitenin yanında. Bunun için ulaşım sorunu yok.Gaziantep'te yazın hava çok sıcak oluyor, insanlar evlerinden ve iş yerlerinden dışarıya çıkmıyorlar. Gece balkonlarda yatıyorlar. Kışın ise havalar sert, sokaklar ise her zaman ıslaktır.""";

  var textList = <TextSpan>[];
  Timer? _readingTimer;
  int currentHighlightIndex = 0;

  @override
  void initState() {
    super.initState();
    // 初始化时所有单词默认透明度为0.3
    text.split(' ').forEach((element) {      
      textList.add(TextSpan(text: "$element ", style: const TextStyle(color: Colors.black, opacity: 0.3)));
    });
  }

  // 启动快速阅读练习
  void startReadingExercise() {
    // 先停止之前的定时器避免重复启动
    if(_readingTimer != null) _readingTimer!.cancel();
    currentHighlightIndex = 0;
    // 每秒更新一次高亮单词
    _readingTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if(currentHighlightIndex >= textList.length) {
        // 所有单词遍历完成停止定时器
        timer.cancel();
        return;
      }
      // 替换对应位置的TextSpan为新的透明度实例
      for(int i=0; i<textList.length; i++) {
        textList[i] = TextSpan(
          text: textList[i].text,
          style: TextStyle(
            color: Colors.black,
            // 当前索引的单词透明度为1,其余为0.3
            opacity: i == currentHighlightIndex ? 1.0 : 0.3
          )
        );
      }
      currentHighlightIndex++;
      // 通知框架状态变更,触发重绘
      setState(() {});
    });
  }

  @override
  void dispose() {
    // 页面销毁时取消定时器
    if(_readingTimer != null) _readingTimer!.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    Widget _controlRow() {
      return Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Column(
            children: [
              IconButton(
                onPressed: () {},
                icon: const Icon(Icons.skip_previous),
                iconSize: 50,
                color: const Color(0xFFffcd05),
              )
            ],
          ),
          Column(
            children: [
              IconButton(
                onPressed: () {
                  if(_readingTimer != null && _readingTimer!.isActive) {
                    _readingTimer!.pause();
                  }
                },
                icon: const Icon(Icons.pause_circle),
                iconSize: 50,
                color: const Color(0xFF91268e),
              )
            ],
          ),
          Column(
            children: [
              IconButton(
                onPressed: () {},
                icon: const Icon(Icons.skip_next),
                iconSize: 50,
                color: const Color(0xFFffcd05),
              )
            ],
          ),
        ],
      );
    }

    Widget _textArea() {
      return FractionallySizedBox(
          widthFactor: 0.9,
          heightFactor: 0.9,
          child: DecoratedBox(
              decoration: BoxDecoration(
                border: Border.all(
                  color: Colors.grey,
                  width: 1,
                ),
              ),
              child: RichText(
                text: TextSpan(children: textList),
              )));
    }

    return Column(
      children: [
        Expanded(
          flex: 6,
          child: _textArea(),
        ),
        Expanded(
          child: _controlRow(),
        ),
        Container(
          width: 300,
          child: ElevatedButton.icon(
            onPressed: startReadingExercise,
            icon: const Icon(Icons.play_arrow, size: 18),
            label: const Text("Hızlı Egzersiz"),
            style: ButtonStyle(
              backgroundColor:
                  MaterialStateProperty.all(const Color(0xFF075098)),
            ),
          ),
        )
      ],
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 20:06:00