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

Flutter InAppWebView在Android TV上DPad与Tab键失效问题求助

解决方案:Android TV上flutter_inappwebview加载GitHub的焦点切换问题

问题根源

  1. Android TV环境下,InAppWebView默认未获取焦点,导致键盘事件无法传递到网页,JS焦点控制失效。
  2. GitHub页面的可聚焦元素范围较广,原选择器仅覆盖带tabindex的元素,漏掉了按钮、链接等默认可聚焦元素。
  3. GitHub页面存在自定义键盘事件监听,可能拦截了默认的焦点切换行为。

具体实现步骤

1. 确保WebView获取初始焦点

在onWebViewCreated回调中主动请求WebView获取焦点,确保键盘事件能正常传递:

onWebViewCreated: (controller) {
  _controller = controller;
  // Android TV上强制WebView获取焦点
  controller.android?.requestFocus();
},

2. 完善焦点切换逻辑

  • 扩大可聚焦元素选择器,覆盖所有HTML默认可交互元素
  • 动态统计页面可聚焦元素数量,处理GitHub动态加载内容
  • 统一DPad方向键和Tab键的焦点切换行为

完整代码示例:

import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const WebViewPage(),
    ),
  );
}

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

  @override
  State<WebViewPage> createState() => _WebViewPageState();
}

class _WebViewPageState extends State<WebViewPage> {
  InAppWebViewController? _controller;
  int _focusIndex = 0;
  int _maxFocusIndex = 0;

  @override
  void initState() {
    super.initState();
    RawKeyboard.instance.addListener(_handleKeyEvent);
  }

  @override
  void dispose() {
    RawKeyboard.instance.removeListener(_handleKeyEvent);
    super.dispose();
  }

  void _handleKeyEvent(RawKeyEvent event) {
    if (event is! RawKeyDownEvent) return;

    // 处理方向键和Tab键
    if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
      _focusIndex = (_focusIndex - 1).clamp(0, _maxFocusIndex);
      _focusTargetElement();
    } else if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
      _focusIndex = (_focusIndex + 1).clamp(0, _maxFocusIndex);
      _focusTargetElement();
    } else if (event.logicalKey == LogicalKeyboardKey.tab) {
      // 拦截默认Tab行为,统一用自定义逻辑
      event.handled = true;
      _focusIndex = (_focusIndex + 1).clamp(0, _maxFocusIndex);
      _focusTargetElement();
    }
  }

  void _focusTargetElement() {
    _controller?.evaluateJavascript(source: '''
      // 选择所有可聚焦元素:按钮、链接、表单控件、非-1的tabindex元素
      const focusableElements = document.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
      if (focusableElements.length > 0) {
        const target = focusableElements[$_focusIndex];
        target.focus();
        // 滚动到元素可见区域,适配TV大屏
        target.scrollIntoView({behavior: 'smooth', block: 'center'});
      }
    ''');
  }

  void _updateFocusableElementCount() {
    _controller?.evaluateJavascript(source: '''
      const focusableElements = document.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
      focusableElements.length;
    ''').then((value) {
      if (value != null) {
        setState(() {
          _maxFocusIndex = int.parse(value.toString()) - 1;
          _focusIndex = _focusIndex.clamp(0, _maxFocusIndex);
        });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: InAppWebView(
        initialUrlRequest: URLRequest(url: Uri.parse('https://github.com')),
        onWebViewCreated: (controller) {
          _controller = controller;
          controller.android?.requestFocus();
        },
        onLoadStop: (controller, url) {
          // 页面加载完成后统计可聚焦元素
          _updateFocusableElementCount();
          // 注入JS阻止页面拦截键盘事件
          controller.evaluateJavascript(source: '''
            document.addEventListener('keydown', function(e) {
              // 放行方向键和Tab键的默认行为
              if ([37,38,39,40,9].includes(e.keyCode)) {
                e.stopPropagation();
              }
            }, true);
          ''');
        },
        onPageCommitVisible: (controller, url) {
          // 页面可见时再次统计,处理动态加载内容
          _updateFocusableElementCount();
        },
      ),
    );
  }
}

3. 额外优化:阻止页面自定义键盘拦截

在onLoadStop中注入JS,阻止GitHub页面的自定义事件拦截方向键和Tab键,确保焦点切换逻辑正常执行。


关键说明

  • 初始焦点:Android TV上WebView默认不会自动获取焦点,必须主动调用requestFocus()才能让键盘事件传递到网页。
  • 动态统计元素:GitHub页面存在大量动态加载内容,必须在页面加载完成和可见时重新统计可聚焦元素数量,避免索引越界。
  • 滚动适配:添加scrollIntoView确保聚焦元素在屏幕中央,符合Android TV的用户交互习惯。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 16:40:09