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

Flutter无障碍开发:加载新题目后让屏幕阅读器自动聚焦题目

Flutter测验项目无障碍优化:切换题目时自动聚焦到题干

要实现选择答案后加载下一题时,屏幕阅读器自动聚焦到题干,需要结合FocusNode和UI更新后的回调来完成,以下是修改后的完整代码及关键说明:

import 'package:flutter/material.dart';

class QuizPage extends StatefulWidget {
  const QuizPage({super.key});

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

class _QuizPageState extends State<QuizPage> {
  int _currentQuestionIndex = 0;
  late FocusNode _questionFocusNode;

  final List<Map<String, Object>> _questions = [
    {
      'question': '天空是什么颜色?',
      'answers': ['蓝色', '绿色', '黄色'],
      'correctAnswer': 0,
    },
    {
      'question': '太阳系有多少颗行星?',
      'answers': ['7', '8', '9'],
      'correctAnswer': 1,
    },
    {
      'question': '世界上最大的海洋是哪个?',
      'answers': ['大西洋', '印度洋', '太平洋'],
      'correctAnswer': 2,
    },
    {
      'question': '法国的首都是哪里?',
      'answers': ['柏林', '马德里', '巴黎'],
      'correctAnswer': 2,
    },
  ];

  @override
  void initState() {
    super.initState();
    // 初始化聚焦节点
    _questionFocusNode = FocusNode();
  }

  @override
  void dispose() {
    // 销毁聚焦节点,避免内存泄漏
    _questionFocusNode.dispose();
    super.dispose();
  }

  void _answerQuestion(int selectedAnswerIndex) {
    if (selectedAnswerIndex ==
        _questions[_currentQuestionIndex]['correctAnswer']) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('回答正确!')),
      );
    } else {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('回答错误!')),
      );
    }

    setState(() {
      if (_currentQuestionIndex < _questions.length - 1) {
        _currentQuestionIndex++;
        // UI更新完成后,自动聚焦到题干
        WidgetBinding.instance.addPostFrameCallback((_) {
          _questionFocusNode.requestFocus();
        });
      } else {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('测验已完成!')),
        );
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('测验'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // 将题干包装在Focus组件中,关联聚焦节点
            Focus(
              focusNode: _questionFocusNode,
              child: Semantics(
                label: '第${_currentQuestionIndex + 1}题',
                child: Text(
                  _questions[_currentQuestionIndex]['question'] as String,
                  style: const TextStyle(fontSize: 24),
                ),
              ),
            ),
            const SizedBox(height: 20),
            ...(_questions[_currentQuestionIndex]['answers'] as List<String>)
                .asMap()
                .entries
                .map((entry) {
              int index = entry.key;
              String answer = entry.value;
              return Semantics(
                label: '选项${entry.key + 1}:$answer',
                child: ElevatedButton(
                  onPressed: () => _answerQuestion(index),
                  child: Text(answer),
                ),
              );
            }).toList(),
          ],
        ),
      ),
    );
  }
}

关键修改说明

  • 初始化与销毁FocusNode:在initState中创建_questionFocusNode,dispose中销毁,防止内存泄漏。
  • UI更新后触发聚焦:在setState更新题目索引后,通过WidgetBinding.instance.addPostFrameCallback确保UI完全更新后,再调用_questionFocusNode.requestFocus()发起聚焦,避免因UI未更新导致聚焦失效。
  • 关联聚焦节点到题干:用Focus组件包裹题干的Semantics和Text,将focusNode绑定到该组件,让屏幕阅读器能定位并读取题干内容。
  • 语义化优化:更新了Semantics的中文标签,确保屏幕阅读器能清晰告知用户当前是第几题、选项内容等信息。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 16:03:13