flutter

框架

flutter/flutter

谷歌的开源UI框架,用于构建跨平台应用。

项目简介

Flutter是谷歌开发的开源UI框架,用于从单一代码构建美观、高性能的移动、Web和桌面应用。它提供丰富的可定制组件和硬件加速图形,确保原生般性能。Flutter还拥有热重载等提升开发效率的特性。

README 预览

\n  \n    \n      \n      \n    \n  \n\n\n[](https://flutter-dashboard.appspot.com/#/build?repo=flutter)\n[![Discord badge][]][Discord instructions]\n[![Twitter handle][]][Twitter badge]\n[![BlueSky badge][]][BlueSky handle]\n[](https://codecov.io/gh/flutter/flutter)\n[](https://insights.linuxfoundation.org/project/flutter)\n[](https://bestpractices.coreinfrastructure.org/projects/5631)\n[](https://slsa.dev)\n\nFlutter is Google's SDK for crafting beautiful, fast user experiences for\nmobile, web, and desktop from a single codebase. Flutter works with existing\ncode, is used by developers and organizations around the world, and is free and\nopen source.\n\n## Documentation\n\n* [Install Flutter](https://docs.flutter.dev/get-started)\n* [Flutter documentation](https://docs.flutter.dev)\n* [Development wiki](./docs/README.md)\n* [Contributing to Flutter](https://github.com/flutter/flutter/blob/main/CONTRIBUTING.md)\n\nFor release and other announcements, join the\n[flutter-announce](https://groups.google.com/g/flutter-announce)\nmailing list. Our documentation also tracks [breaking\nchanges](https://docs.flutter.dev/release/breaking-changes) across releases.\n\n## Terms of service\n\nThe Flutter tool may occasionally download resources from Google servers. By\ndownloading or using the Flutter SDK, you agree to the Google Terms of Service:\nhttps://policies.google.com/terms\n\nFor example, when installed from GitHub (as opposed to from a prepackaged\narchive), the Flutter tool will download the Dart SDK from Google servers\nimmediately when first run, as it is used to execute the `flutter` tool itself.\nThis will also occur when Flutter is upgraded (e.g. by running the `flutter\nupgrade` command).\n\n## About Flutter\n\nWe think Flutter will help you create beautiful, fast apps, with a productive,\nextensible and open development model, whether you're targeting iOS or Android,\nweb, Windows, macOS, Linux or embedding it as the UI toolkit for a platform of\nyour choice.

常见问题 (5)

故障排除
如何修复在动画过程中选择器被关闭时,CupertinoPicker 点按选择功能引发的 "StateError: Bad state: No element" 崩溃问题?

这是 Flutter 框架中的一个竞态条件(flutter/flutter#192135),位于 _CupertinoPickerState._handleChildTap:在 300ms 的点按选中动画(_kCupertinoPickerTapToScrollDuration)结束后,代码在未检查 widget 是否仍然 mounted 的情况下读取 FixedExtentScrollController.selectedItem。如果 picker 在偏离中心的点按发生后 300ms 内被销毁(例如父组件在选中时将其隐藏,或带有较短退出动画的模态框被 pop),selectedItem 将没有附加的 positions,从而抛出 Bad state: No element。该问题影响 Flutter 3.27.0 到至少 3.47.2 的版本。

在修复合入之前(PR flutter/packages#13012),可采用以下临时解决方案:

1. 延迟销毁,使 picker 的存活时间长于动画——在选中变化后等待超过 300ms 再隐藏/pop 它:

Timer(const Duration(milliseconds: 350), () {
  if (mounted) Navigator.of(context).pop();
});

注意:默认情况下,点按 showCupertinoModalPopup 的 barrier 是安全的,因为其 335ms 的退出过渡动画长于 300ms 的动画。

2. 在本地给框架打补丁——在 packages/flutter/lib/src/cupertino/picker.dart(或 flutter/packages 中的 cupertino_ui)中为 await 之后的代码添加保护:

await _effectiveController.animateToItem(
  index,
  duration: _kCupertinoPickerTapToScrollDuration,
  curve: _kCupertinoPickerTapToScrollCurve,
);
if (!mounted) return;          // 添加此保护
_enableHapticFeedback = true;
_lastHapticIndex = _effectiveController.selectedItem;

3. 作为最后的手段,通过 PlatformDispatcher.instance.onError 全局捕获该错误,并忽略来自 _handleChildTap 的 StateError。

来源 Issue #192135
故障排除
为什么在 Flutter vector_math 中,Matrix4 透视变换(setEntry(3, 2, ...))没有应用于 X/Y 旋转?

这是预期的矩阵行为,而不是 bug。平面 widget 各处的 z = 0,因此仅靠底行中的透视项会得到 w = 1,没有可用来相除的值,而且 setRotation() 会替换旋转块,不会将透视折叠进去。有两种可行的修复方法:

1. 先应用透视,再旋转,这样旋转会将透视项折叠进 w 中:

Matrix4.identity()
  ..setEntry(3, 2, 0.001)
  ..rotateX(x)
  ..rotateY(y);

2. 直接构建 Matrix4,将透视强度乘入旋转的第三列(已由 Flutter 团队确认有效):

return Matrix4(
  r00, r10, r20, perspectiveStrength * r20,
  r01, r11, r21, perspectiveStrength * r21,
  r02, r12, r22, perspectiveStrength * r22,
  xTranslation,
  yTranslation,
  zTranslation,
  perspectiveStrength * zTranslation + 1,
);

注意:Matrix4.compose() 以及在 setEntry(3, 2, ...) 之后调用 setRotation() 都不会在 X/Y 旋转上产生透视效果,因此请使用上述方法之一。

来源 Issue #194632
故障排除
为什么在 Flutter web 上启用无障碍功能(semantics)后 TextField 会失去焦点,以及如何解决?

原因:如果某个祖先 Theme 重建时使用的 ThemeData 与之前的实例永不相等(例如,你在 build 中内联创建 WidgetStateProperty.resolveWith —— 它没有重写 ==),ScrollableState.didChangeDependencies 会重新创建滚动位置。这会导致语义角色在 scrollable 和 generic 之间切换(因为 hasImplicitScrolling 只有在 position.haveDimensions 为 true 时才会被设置),而 web 引擎会重新追加 DOM 元素,将 document.activeElement 移动到 <body> 上,使得键入的内容不再进入 <textarea>。当 TextField 位于 SingleChildScrollView 内部,并且触发了任何重建时(例如,焦点/文本监听器中的 setState),就会出现这种情况。

解决方法:只构建一次 ThemeData,并在每次构建时复用同一个实例 —— 将其存储在 final 字段、initState 或顶层/静态常量中,而不是在 build 内部构造。避免在 build 中内联分配 WidgetStateProperty.resolveWith(...) 或任何没有重写 == 的属性。报告者已验证:跨构建复用同一个 ThemeData 对象可以使键入正常生效;移除滚动视图也可以避免此问题。由于这是一个已确认的 Flutter web 引擎/框架 bug(角色切换会移动获得焦点的节点),请在修复合入 stable 渠道后升级 Flutter。

来源 Issue #192792
故障排除
为什么 `flutter test --platform chrome` 会一直卡在 "loading" 界面,我该如何解决?

这是 Flutter 的 Web 测试服务器中的一个 bug(已在 Flutter 3.41.7 上确认)。服务器通过对 host.dart.js 进行子字符串匹配来处理请求,因此任何文件名以 host.dart 结尾的库(例如 foo_host.dart、ghost.dart)都会编译为 foo_host.dart.js,同样会命中匹配。服务器会返回测试宿主包而不是真正的模块,模块加载器抛出 TypeError: Cannot read properties of undefined,而运行器则无限期等待:没有错误、没有超时,CPU 占用接近零。

修复方法:
1. 升级 Flutter:该挂起问题在 Flutter 3.47.4 stable 上不再复现。运行 flutter upgrade,然后重新运行 flutter test --platform chrome。
2. 如果无法升级,可采用变通方法:重命名库文件,使其基本文件名不以 host.dart 结尾(例如 foo_host.dart -> foo_host_api.dart),并更新所有 import。注意,只要有一个这样的测试套件就会导致整个测试运行停滞,因此请检查项目中的每个库文件。

诊断提示:在 Chrome DevTools 中打开停滞的测试页面。如果对 /<path>/foo_host.dart.js 的请求返回的字节与 /static/host.dart.js(约 638 KB 的 dart2js 宿主包)完全相同,则说明遇到了此 bug。

来源 Issue #192760
故障排除
为什么在 iOS Safari 上,Flutter web 中过长的 Flutter Semantics label 会拦截对其他按钮的点击?

这是一个 Flutter web 引擎的 bug:一个不可见的 semantics 节点的长换行标签溢出到其元素框下方很远处,并参与了 Safari 的原生命中测试,因此点击视觉上位于下方的 widget(例如后面某个 sliver 中的按钮)反而会命中上方的 semantics 节点并触发其回调。修复在 PR #194011 中;请升级到包含该修复的 Flutter 版本(查看该 PR 了解对应的 milestone,因为在 Flutter 3.47.1 上报告此问题时它仍处于审查中,尚未进入稳定版本)。在此之前,复现过程中观察到的缓解措施:在 web 上保持无障碍标签简短(不换行的标签可避免溢出),或者重构布局——用 Column 替换多个独立的 SliverToBoxAdapter 可以得到正确的堆叠(z-index)和命中测试。禁用 semantics 也能避免此问题,但会失去无障碍功能,因此不是可行的变通方案。

来源 Issue #192466