OpenSource-Hub

flutter

프레임워크

flutter/flutter

크로스 플랫폼 애플리케이션을 빌드하기 위한 Google의 오픈 소스 UI 프레임워크.

개요

Flutter는 Google이 개발한 오픈 소스 UI 프레임워크로, 단일 코드베이스로 아름답고 고성능의 모바일, Web 및 데스크톱 애플리케이션을 구축하는 데 사용됩니다. 풍부한 사용자 정의 가능한 컴포넌트와 하드웨어 가속 그래픽을 제공하여 네이티브 수준의 성능을 보장합니다. Flutter는 또한 핫 리로드(Hot Reload)와 같이 개발 효율성을 높여주는 기능도 갖추고 있습니다.

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.

FAQ (5)

문제 해결
애니메이션 도중 피커가 닫힐 때 CupertinoPicker의 tap-to-select에서 발생하는 "StateError: Bad state: No element" 크래시를 해결하는 방법은?

이것은 _CupertinoPickerState._handleChildTap에서 발생하는 Flutter 프레임워크 경쟁 상태(race condition)(flutter/flutter#192135)입니다: 300ms 탭 선택 애니메이션(_kCupertinoPickerTapToScrollDuration)이 끝난 후, 위젯이 여전히 마운트되어 있는지 확인하지 않고 FixedExtentScrollController.selectedItem을 읽습니다. 중앙에서 벗어난 위치를 탭한 후 300ms 이내에 피커가 dispose되면(예: 부모가 선택 시 피커를 숨기거나, 짧은 종료 애니메이션을 가진 모달이 pop되는 경우), selectedItem에 연결된 position이 없어 Bad state: No element 예외가 발생합니다. 이 문제는 Flutter 3.27.0부터 최소 3.47.2까지 영향을 미칩니다.

수정 사항이 적용될 때까지의 해결 방법(PR flutter/packages#13012):

1. 피커가 애니메이션보다 오래 유지되도록 dispose를 지연시키세요 — 선택이 변경된 후 300ms 이상 기다린 다음에 숨기거나 pop하세요:

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

참고: showCupertinoModalPopup의 배리어를 탭하는 것은 기본적으로 안전합니다. 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 회전에 적용되지 않는 이유는 무엇인가요?

이는 버그가 아니라 예상되는 행렬 동작입니다. 플랫 위젯은 모든 위치에서 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가 된 후에만 설정되기 때문), 웹 엔진이 DOM 요소를 다시 추가하면서 document.activeElement가 <body>로 이동하여 타이핑이 더 이상 <textarea>에 입력되지 않습니다. 이 문제는 TextField가 SingleChildScrollView 안에 있고 어떤 리빌드든 트리거될 때 (예: 포커스/텍스트 리스너에서의 setState) 발생합니다.

해결 방법: ThemeData를 한 번만 생성하고 매 빌드마다 동일한 인스턴스를 재사용하세요. build 안에서 생성하는 대신 final 필드, initState, 또는 최상위/static 상수에 저장하세요. build 안에서 WidgetStateProperty.resolveWith(...) 또는 == 오버라이드가 없는 프로퍼티를 인라인으로 할당하지 마세요. 리포터가 확인한 바에 따르면: 여러 빌드에 걸쳐 하나의 ThemeData 객체를 재사용하면 타이핑이 정상적으로 입력되며, 스크롤 뷰를 제거해도 문제를 피할 수 있습니다. 이는 확인된 Flutter 웹 엔진/프레임워크 버그이므로 (역할 교환으로 인해 포커스된 노드가 이동함), 수정이 stable 채널에 반영되면 Flutter도 업그레이드하세요.

원본 Issue #192792
문제 해결
`flutter test --platform chrome`이 "loading" 상태에서 계속 멈춰 있는 이유는 무엇이며, 어떻게 해결할 수 있나요?

이것은 Flutter 웹 테스트 서버의 버그입니다(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 사용량이 거의 0인 상태로 무한정 대기합니다.

해결 방법:
1. Flutter 업그레이드: Flutter 3.47.4 stable에서는 더 이상 이 멈춤 현상이 재현되지 않습니다. flutter upgrade를 실행한 후 flutter test --platform chrome을 다시 실행하세요.
2. 업그레이드할 수 없는 경우의 우회 방법: 라이브러리 파일의 basename이 host.dart로 끝나지 않도록 이름을 변경하고(예: foo_host.dart -> foo_host_api.dart) 모든 import를 업데이트하세요. 이러한 스위트 하나가 전체 테스트 실행을 멈추게 하므로 프로젝트의 모든 라이브러리 파일을 확인하세요.

진단 팁: 멈춘 테스트 페이지를 Chrome DevTools에서 여세요. /<path>/foo_host.dart.js에 대한 요청이 /static/host.dart.js(약 638 KB dart2js 호스트 번들)와 정확히 동일한 바이트를 반환하면 이 버그에 해당하는 것입니다.

원본 Issue #192760
문제 해결
Flutter web의 iOS Safari에서 긴 Flutter Semantics 레이블이 다른 버튼의 탭을 가로채는 이유는 무엇인가요?

이것은 Flutter web engine 버그입니다: 보이지 않는 semantics 노드의 길게 줄바꿈된 레이블이 해당 요소의 박스 훨씬 아래까지 오버플로되어 Safari의 네이티브 히트 테스트에 참여하므로, 시각적으로 더 아래에 있는 위젯(예: 이후 sliver에 있는 버튼)을 탭하면 상위 semantic 노드가 타겟이 되어 대신 해당 콜백이 실행됩니다. 수정 사항은 PR #194011에 있습니다; 이를 포함하는 Flutter 릴리스로 업그레이드하세요(Flutter 3.47.1에서 보고된 시점에는 아직 리뷰 중이었고 stable 릴리스에 포함되지 않았으므로 마일스톤은 PR에서 확인하세요). 그 전까지 재현에서 관찰된 완화 방법은 다음과 같습니다: 웹에서는 접근성 레이블을 짧게 유지하세요(줄바꿈되지 않는 레이블은 오버플로를 피합니다). 또는 레이아웃을 재구성하세요 — 별도의 SliverToBoxAdapters를 Column으로 교체하면 올바른 스태킹(z-index)과 히트 테스트가 이루어졌습니다. semantics를 비활성화해도 이 문제를 피할 수 있지만 접근성이 제거되므로 실행 가능한 해결 방법은 아닙니다.

원본 Issue #192466