#!if 넘어옴1 != null
'''플러터'''{{{#!if 넘어옴2 == null
{{{#!if 넘어옴1[넘어옴1.length - 1] >= 0xAC00 && 넘어옴1[넘어옴1.length - 1] <= 0xD7A3
{{{#!if ((넘어옴1[넘어옴1.length - 1] - 0xAC00) % 28) == 0
는}}}{{{#!if ((넘어옴1[넘어옴1.length - 1] - 0xAC00) % 28) != 0
은}}}}}}{{{#!if 넘어옴1[넘어옴1.length - 1] < 0xAC00 || 넘어옴1[넘어옴1.length - 1] > 0xD7A3
은(는)}}}}}}{{{#!if 넘어옴2 != null
, '''플루터'''{{{#!if 넘어옴3 == null
{{{#!if 넘어옴2[넘어옴2.length - 1] >= 0xAC00 && 넘어옴2[넘어옴2.length - 1] <= 0xD7A3
{{{#!if ((넘어옴2[넘어옴2.length - 1] - 0xAC00) % 28) == 0
는}}}{{{#!if ((넘어옴2[넘어옴2.length - 1] - 0xAC00) % 28) != 0
은}}}}}}{{{#!if 넘어옴2[넘어옴2.length - 1] < 0xAC00 || 넘어옴2[넘어옴2.length - 1] > 0xD7A3
은(는)}}}}}}}}}{{{#!if 넘어옴3 != null
, ''''''{{{#!if 넘어옴4 == null
{{{#!if 넘어옴3[넘어옴3.length - 1] >= 0xAC00 && 넘어옴3[넘어옴3.length - 1] <= 0xD7A3
{{{#!if ((넘어옴3[넘어옴3.length - 1] - 0xAC00) % 28) == 0
는}}}{{{#!if ((넘어옴3[넘어옴3.length - 1] - 0xAC00) % 28) != 0
은}}}}}}{{{#!if 넘어옴3[넘어옴3.length - 1] < 0xAC00 || 넘어옴3[넘어옴3.length - 1] > 0xD7A3
은(는)}}}}}}}}}{{{#!if 넘어옴4 != null
, ''''''{{{#!if 넘어옴5 == null
{{{#!if 넘어옴4[넘어옴4.length - 1] >= 0xAC00 && 넘어옴4[넘어옴4.length - 1] <= 0xD7A3
{{{#!if ((넘어옴4[넘어옴4.length - 1] - 0xAC00) % 28) == 0
는}}}{{{#!if ((넘어옴4[넘어옴4.length - 1] - 0xAC00) % 28) != 0
은}}}}}}{{{#!if 넘어옴4[넘어옴4.length - 1] < 0xAC00 || 넘어옴4[넘어옴4.length - 1] > 0xD7A3
은(는)}}}}}}}}}{{{#!if 넘어옴5 != null
, ''''''{{{#!if 넘어옴6 == null
{{{#!if 넘어옴5[넘어옴5.length - 1] >= 0xAC00 && 넘어옴5[넘어옴5.length - 1] <= 0xD7A3
{{{#!if ((넘어옴5[넘어옴5.length - 1] - 0xAC00) % 28) == 0
는}}}{{{#!if ((넘어옴5[넘어옴5.length - 1] - 0xAC00) % 28) != 0
은}}}}}}{{{#!if 넘어옴5[넘어옴5.length - 1] < 0xAC00 || 넘어옴5[넘어옴5.length - 1] > 0xD7A3
은(는)}}}}}}}}}{{{#!if 넘어옴6 != null
, ''''''{{{#!if 넘어옴7 == null
{{{#!if 넘어옴6[넘어옴6.length - 1] >= 0xAC00 && 넘어옴6[넘어옴6.length - 1] <= 0xD7A3
{{{#!if ((넘어옴6[넘어옴6.length - 1] - 0xAC00) % 28) == 0
는}}}{{{#!if ((넘어옴6[넘어옴6.length - 1] - 0xAC00) % 28) != 0
은}}}}}}{{{#!if 넘어옴6[넘어옴6.length - 1] < 0xAC00 || 넘어옴6[넘어옴6.length - 1] > 0xD7A3
은(는)}}}}}}}}}{{{#!if 넘어옴7 != null
, ''''''{{{#!if 넘어옴8 == null
{{{#!if 넘어옴7[넘어옴7.length - 1] >= 0xAC00 && 넘어옴7[넘어옴7.length - 1] <= 0xD7A3
{{{#!if ((넘어옴7[넘어옴7.length - 1] - 0xAC00) % 28) == 0
는}}}{{{#!if ((넘어옴7[넘어옴7.length - 1] - 0xAC00) % 28) != 0
은}}}}}}{{{#!if 넘어옴7[넘어옴7.length - 1] < 0xAC00 || 넘어옴7[넘어옴7.length - 1] > 0xD7A3
은(는)}}}}}}}}}{{{#!if 넘어옴8 != null
, ''''''{{{#!if 넘어옴9 == null
{{{#!if 넘어옴8[넘어옴8.length - 1] >= 0xAC00 && 넘어옴8[넘어옴8.length - 1] <= 0xD7A3
{{{#!if ((넘어옴8[넘어옴8.length - 1] - 0xAC00) % 28) == 0
는}}}{{{#!if ((넘어옴8[넘어옴8.length - 1] - 0xAC00) % 28) != 0
은}}}}}}{{{#!if 넘어옴8[넘어옴8.length - 1] < 0xAC00 || 넘어옴8[넘어옴8.length - 1] > 0xD7A3
은(는)}}}}}}}}}{{{#!if 넘어옴9 != null
, ''''''{{{#!if 넘어옴10 == null
{{{#!if 넘어옴9[넘어옴9.length - 1] >= 0xAC00 && 넘어옴9[넘어옴9.length - 1] <= 0xD7A3
{{{#!if ((넘어옴9[넘어옴9.length - 1] - 0xAC00) % 28) == 0
는}}}{{{#!if ((넘어옴9[넘어옴9.length - 1] - 0xAC00) % 28) != 0
은}}}}}}{{{#!if 넘어옴9[넘어옴9.length - 1] < 0xAC00 || 넘어옴9[넘어옴9.length - 1] > 0xD7A3
은(는)}}}}}}}}}{{{#!if 넘어옴10 != null
, ''''''{{{#!if 넘어옴10[넘어옴10.length - 1] >= 0xAC00 && 넘어옴10[넘어옴10.length - 1] <= 0xD7A3
{{{#!if ((넘어옴10[넘어옴10.length - 1] - 0xAC00) % 28) == 0
는}}}{{{#!if ((넘어옴10[넘어옴10.length - 1] - 0xAC00) % 28) != 0
은}}}}}}{{{#!if 넘어옴10[넘어옴10.length - 1] < 0xAC00 || 넘어옴10[넘어옴10.length - 1] > 0xD7A3
은(는)}}}}}} 여기로 연결됩니다.
#!if 설명 == null && 리스트 == null
{{{#!if 설명1 == null
다른 뜻에 대한 내용은 아래 문서를}}}{{{#!if 설명1 != null
{{{#!html BEMANI 시리즈의 수록곡}}}에 대한 내용은 [[Flutter(BEMANI)]] 문서{{{#!if (문단1 == null) == (앵커1 == null)
를}}}{{{#!if 문단1 != null & 앵커1 == null
의 [[Flutter(BEMANI)#s-|]]번 문단을}}}{{{#!if 문단1 == null & 앵커1 != null
의 [[Flutter(BEMANI)#|]] 부분을}}}}}}{{{#!if 설명2 != null
, {{{#!html 만화 '헌터×헌터'의 등장인물}}}에 대한 내용은 [[후라타]] 문서{{{#!if (문단2 == null) == (앵커2 == null)
를}}}{{{#!if 문단2 != null & 앵커2 == null
의 [[후라타#s-|]]번 문단을}}}{{{#!if 문단2 == null & 앵커2 != null
의 [[후라타#|]] 부분을}}}}}}{{{#!if 설명3 != null
, {{{#!html }}}에 대한 내용은 [[]] 문서{{{#!if (문단3 == null) == (앵커3 == null)
를}}}{{{#!if 문단3 != null & 앵커3 == null
의 [[#s-|]]번 문단을}}}{{{#!if 문단3 == null & 앵커3 != null
의 [[#|]] 부분을}}}}}}{{{#!if 설명4 != null
, {{{#!html }}}에 대한 내용은 [[]] 문서{{{#!if (문단4 == null) == (앵커4 == null)
를}}}{{{#!if 문단4 != null & 앵커4 == null
의 [[#s-|]]번 문단을}}}{{{#!if 문단4 == null & 앵커4 != null
의 [[#|]] 부분을}}}}}}{{{#!if 설명5 != null
, {{{#!html }}}에 대한 내용은 [[]] 문서{{{#!if (문단5 == null) == (앵커5 == null)
를}}}{{{#!if 문단5 != null & 앵커5 == null
의 [[#s-|]]번 문단을}}}{{{#!if 문단5 == null & 앵커5 != null
의 [[#|]] 부분을}}}}}}{{{#!if 설명6 != null
, {{{#!html }}}에 대한 내용은 [[]] 문서{{{#!if (문단6 == null) == (앵커6 == null)
를}}}{{{#!if 문단6 != null & 앵커6 == null
의 [[#s-|]]번 문단을}}}{{{#!if 문단6 == null & 앵커6 != null
의 [[#|]] 부분을}}}}}}{{{#!if 설명7 != null
, {{{#!html }}}에 대한 내용은 [[]] 문서{{{#!if (문단7 == null) == (앵커7 == null)
를}}}{{{#!if 문단7 != null & 앵커7 == null
의 [[#s-|]]번 문단을}}}{{{#!if 문단7 == null & 앵커7 != null
의 [[#|]] 부분을}}}}}}{{{#!if 설명8 != null
, {{{#!html }}}에 대한 내용은 [[]] 문서{{{#!if (문단8 == null) == (앵커8 == null)
를}}}{{{#!if 문단8 != null & 앵커8 == null
의 [[#s-|]]번 문단을}}}{{{#!if 문단8 == null & 앵커8 != null
의 [[#|]] 부분을}}}}}}{{{#!if 설명9 != null
, {{{#!html }}}에 대한 내용은 [[]] 문서{{{#!if (문단9 == null) == (앵커9 == null)
를}}}{{{#!if 문단9 != null & 앵커9 == null
의 [[#s-|]]번 문단을}}}{{{#!if 문단9 == null & 앵커9 != null
의 [[#|]] 부분을}}}}}}{{{#!if 설명10 != null
, {{{#!html }}}에 대한 내용은 [[]] 문서{{{#!if (문단10 == null) == (앵커10 == null)
를}}}{{{#!if 문단10 != null & 앵커10 == null
의 [[#s-|]]번 문단을}}}{{{#!if 문단10 == null & 앵커10 != null
의 [[#|]] 부분을}}}}}}
#!if 설명 == null
{{{#!if 리스트 != null
다른 뜻에 대한 내용은 아래 문서를}}} 참고하십시오.
#!if 리스트 != null
{{{#!if 문서명1 != null
* {{{#!if 설명1 != null
BEMANI 시리즈의 수록곡: }}}[[Flutter(BEMANI)]] {{{#!if 문단1 != null & 앵커1 == null
문서의 [[Flutter(BEMANI)#s-|]]번 문단}}}{{{#!if 문단1 == null & 앵커1 != null
문서의 [[Flutter(BEMANI)#|]] 부분}}}}}}{{{#!if 문서명2 != null
* {{{#!if 설명2 != null
만화 '헌터×헌터'의 등장인물: }}}[[후라타]] {{{#!if 문단2 != null & 앵커2 == null
문서의 [[후라타#s-|]]번 문단}}}{{{#!if 문단2 == null & 앵커2 != null
문서의 [[후라타#|]] 부분}}}}}}{{{#!if 문서명3 != null
* {{{#!if 설명3 != null
: }}}[[]] {{{#!if 문단3 != null & 앵커3 == null
문서의 [[#s-|]]번 문단}}}{{{#!if 문단3 == null & 앵커3 != null
문서의 [[#|]] 부분}}}}}}{{{#!if 문서명4 != null
* {{{#!if 설명4 != null
: }}}[[]] {{{#!if 문단4 != null & 앵커4 == null
문서의 [[#s-|]]번 문단}}}{{{#!if 문단4 == null & 앵커4 != null
문서의 [[#|]] 부분}}}}}}{{{#!if 문서명5 != null
* {{{#!if 설명5 != null
: }}}[[]] {{{#!if 문단5 != null & 앵커5 == null
문서의 [[#s-|]]번 문단}}}{{{#!if 문단5 == null & 앵커5 != null
문서의 [[#|]] 부분}}}}}}{{{#!if 문서명6 != null
* {{{#!if 설명6 != null
: }}}[[]] {{{#!if 문단6 != null & 앵커6 == null
문서의 [[#s-|]]번 문단}}}{{{#!if 문단6 == null & 앵커6 != null
문서의 [[#|]] 부분}}}}}}{{{#!if 문서명7 != null
* {{{#!if 설명7 != null
: }}}[[]] {{{#!if 문단7 != null & 앵커7 == null
문서의 [[#s-|]]번 문단}}}{{{#!if 문단7 == null & 앵커7 != null
문서의 [[#|]] 부분}}}}}}{{{#!if 문서명8 != null
* {{{#!if 설명8 != null
: }}}[[]] {{{#!if 문단8 != null & 앵커8 == null
문서의 [[#s-|]]번 문단}}}{{{#!if 문단8 == null & 앵커8 != null
문서의 [[#|]] 부분}}}}}}{{{#!if 문서명9 != null
* {{{#!if 설명9 != null
: }}}[[]] {{{#!if 문단9 != null & 앵커9 == null
문서의 [[#s-|]]번 문단}}}{{{#!if 문단9 == null & 앵커9 != null
문서의 [[#|]] 부분}}}}}}{{{#!if 문서명10 != null
* {{{#!if 설명10 != null
: }}}[[]] {{{#!if 문단10 != null & 앵커10 == null
문서의 [[#s-|]]번 문단}}}{{{#!if 문단10 == null & 앵커10 != null
문서의 [[#|]] 부분}}}}}}
#!if this['this'] == null
'''이 문서는'''
#!if this['this'] != null
'''이 문단은'''
#!if 토론주소2 == null
''' [[https://namu.wiki/thread/NullSubsequentTangyPosition|토론]]을 통해 다음의 합의사항으로 합의되었습니다.''' 합의된 부분을 토론 없이 수정할 시 [[나무위키:기본방침/이용자 관리 방침#편집권 남용|편집권 남용]]으로 간주되어 제재될 수 있습니다.
#!if 토론주소2 != null
''' 아래 토론들로 합의된 편집방침이 적용됩니다.''' 합의된 부분을 토론 없이 수정할 시 [[나무위키:기본방침/이용자 관리 방침#편집권 남용|편집권 남용]]으로 간주되어 제재될 수 있습니다.{{{#!wiki style="text-align:center;"
{{{#!folding [ 내용 펼치기 · 접기 ]
{{{#!wiki style="margin: 0px -5px;"
||<table width=100%><tablebordercolor=transparent><tablebgcolor=transparent><(>'''[[https://namu.wiki/thread/NullSubsequentTangyPosition|토론]] - 합의사항1'''
----
'''[[https://namu.wiki/thread/|토론]] - 합의사항2'''
----
{{{#!if 토론주소3 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항3'''
----
}}}{{{#!if 토론주소4 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항4'''
----
}}}{{{#!if 토론주소5 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항5'''
----
}}}{{{#!if 토론주소6 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항6'''
----
}}}{{{#!if 토론주소7 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항7'''
----
}}}{{{#!if 토론주소8 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항8'''
----
}}}{{{#!if 토론주소9 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항9'''
----
}}}{{{#!if 토론주소10 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항10'''
----
}}}{{{#!if 토론주소11 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항11'''
----
}}}{{{#!if 토론주소12 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항12'''
----
}}}{{{#!if 토론주소13 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항13'''
----
}}}{{{#!if 토론주소14 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항14'''
----
}}}{{{#!if 토론주소15 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항15'''
----
}}}{{{#!if 토론주소16 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항16'''
----
}}}{{{#!if 토론주소17 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항17'''
----
}}}{{{#!if 토론주소18 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항18'''
----
}}}{{{#!if 토론주소19 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항19'''
----
}}}{{{#!if 토론주소20 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항20'''
----
}}}{{{#!if 토론주소21 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항21'''
----
}}}{{{#!if 토론주소22 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항22'''
----
}}}{{{#!if 토론주소23 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항23'''
----
}}}{{{#!if 토론주소24 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항24'''
----
}}}{{{#!if 토론주소25 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항25'''
----
}}}{{{#!if 토론주소26 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항26'''
----
}}}{{{#!if 토론주소27 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항27'''
----
}}}{{{#!if 토론주소28 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항28'''
----
}}}{{{#!if 토론주소29 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항29'''
----
}}}{{{#!if 토론주소30 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항30'''
----
}}}{{{#!if 토론주소31 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항31'''
----
}}}{{{#!if 토론주소32 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항32'''
----
}}}{{{#!if 토론주소33 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항33'''
----
}}}{{{#!if 토론주소34 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항34'''
----
}}}{{{#!if 토론주소35 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항35'''
----
}}}{{{#!if 토론주소36 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항36'''
----
}}}{{{#!if 토론주소37 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항37'''
----
}}}{{{#!if 토론주소38 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항38'''
----
}}}{{{#!if 토론주소39 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항39'''
----
}}}{{{#!if 토론주소40 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항40'''
----
}}}{{{#!if 토론주소41 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항41'''
----
}}}{{{#!if 토론주소42 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항42'''
----
}}}{{{#!if 토론주소43 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항43'''
----
}}}{{{#!if 토론주소44 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항44'''
----
}}}{{{#!if 토론주소45 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항45'''
----
}}}{{{#!if 토론주소46 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항46'''
----
}}}{{{#!if 토론주소47 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항47'''
----
}}}{{{#!if 토론주소48 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항48'''
----
}}}{{{#!if 토론주소49 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항49'''
----
}}}{{{#!if 토론주소50 != null
'''[[https://namu.wiki/thread/|토론]] - 합의사항50'''
----
}}}||}}}}}}}}}
토론 합의사항 | ||
{{{#!wiki style="margin:0 -10px -5px; min-height:calc(1.5em + 5px); word-break: keep-all" {{{#!folding [ 펼치기 · 접기 ] {{{#!wiki style="margin:-5px -1px -11px" |
| }}}}}}}}} |
<colcolor=#075B9D,#60C9F8> 플러터 Flutter | |
| |
종류 | GUI 프레임워크 |
개발 | 구글 및 커뮤니티 |
안정화 최신 버전 | 3.35.3 - 2025년 9월 5일 # |
|
1. 개요
구글에서 2017년 5월 출시된 Skia 및 Impeller를 렌더링 엔진으로 사용하는 모바일/웹/데스크톱 크로스 플랫폼 GUI 프레임워크. 하나의 코드베이스로 Android, Linux, Windows, macOS, iOS 및 Web 브라우저에서 모두 동작되는 앱을 위해 출시되었다. 구글의 또 다른 운영 체제인 퓨시아의 UI 및 퓨시아 애플리케이션 역시 플러터로 작성된다. 사용되는 언어는 구글의 Dart이다.2. 특징
플러터 프레임워크는 소스 코드를 네이티브 CPU 머신 코드로 직접 컴파일하며 GUI를 위해 각 OS의 네이티브 UI 컴포넌트로 변환하지 않고 렌더링 엔진을 통해 GPU를 이용해 직접 드로잉하기 때문에 성능이 뛰어나다. OS에 관계없이 안드로이드의 Material 디자인, macOS/iOS의 Cupertino, 윈도우 Fluent UI 등등의 테마 적용이 가능하며 테마(Theme) 커스텀도 가능하다. CustomPaint등으로 픽셀 단위의 커스텀 위젯 작성도 가능하다.Flutter는 리액티브 프레임워크 프로그래밍 기법으로 제작되었다. 선언형(declarative) UI와 상태 관리(state management)가 특징이다. State 클래스에는 화면 표시와 관련된 build() 메서드외에 특정 조건에서만 실행되는 라이프 사이클 매서드가 있다. UI 디자인을 Dart 언어의 Widget을 사용하면 되므로 UI 언어가 분리된 Java, C#, Javascript 계열의 개발과는 생산성 측면에서 비교 자체를 불허한다.
상태 관리는 기본적으로 StatefulWidget/setState 사용 및 생성자(constructor)/콜백(callback)을 통한 데이터 up/down 전달이 가능하다. 본격적인 상태 관리 및 MVC/MVVM를 위해서는 GetX, Provider, Riverpod, BLoC(Cubit), MobX, get_it 등의 패키지들이 있으며 자신의 취향에 따라 선택하면 된다.
const 외에 final이라는 개념도 존재한다. const는 컴파일 단계에서 결정되며 final은 런타임에서 결정된다. 차이점은 인스턴스의 필드값 변경 가능 여부다. 상태 관리 시 필드값 변경이 필요하기 때문에 final이 많이 사용된다.
주요 IDE로는 Android Studio와 VS Code가 있다. VSC 만으로도 개발이 아예 불가능한 건 아니지만, JDK 번들이나 Android SDK, Android 에뮬레이터 등을 수작업으로 설치해야 하기에 VSC로 개발하려는 사람도 사실상 Android Studio는 반필수적으로 같이 설치하는 실정이다. 또한 디버깅 측면에서도 Android Studio 쪽이 뛰어나기 때문에 같이 쓰면 썼지 굳이 VSC로만 개발할 이유는 거의 없다.
3. 지원 플랫폼
3.1. 모바일
기존에는 Android와 iOS 모두 Skia 2D 렌더러를 이용하였으나 Flutter 3.0 부터 iOS에 Metal API를 활용한 Impeller 엔진이 실험적으로 적용되었고, 3.10부터 안정화되고 기본값이 되었으며 3.29부터는 아예 Skia 렌더러가 iOS에서 빠지고 Impeller만 사용할 수 있게 되었다.Android의 경우 3.10부터 Vulkan 및 OpenGL ES를 활용한 Impeller 엔진이 실험적으로 적용되었으며, 3.27부터는 Android에서도 Impeller가 기본값이 되었다. 다만 Android의 경우 AP 파편화가 심하여 Google에서 모든 AP 드라이버에 맞춰 안정화하는데 애를 먹고 있으며, 그나마 퀄컴 스냅드래곤의 경우 상황이 낫지만 나머지 미디어텍 등의 AP에서는 화면 깨짐이나 앱 강제 종료[1] 등의 오류가 자주 발생한다. 특정 AP를 사용하는 기기들에서 유난히 크래시가 자주 일어난다면 공식 문서를 참고하여 Impeller를 비활성화해볼 수 있다.
Flutter 버전 및 플랫폼별 Impeller 지원상황
3.2. 데스크톱
데스크톱 상에서의 플러터 앱 실행은 2021년 3월부터 공식적으로 지원되고 있다. 윈도우에서는 OpenGL API 호출을 DirectX 11로 변환하는 ANGLE 라이브러리가 사용되어 렌더링되고 있으며, macOS의 경우 iOS의 것과 대부분을 공유하며 Metal API를 통한 Impeller 엔진으로 구동된다. 리눅스의 경우 GTK 임베더 위에서 Skia 엔진을 띄우고 있다. 윈도우 및 리눅스도 Impeller를 도입할 예정에 있으며 현재 실험 단계에 있다.Google이 발표한 공식 2025년 로드맵에 따르면 당분간 Google은 Flutter 모바일과 웹에만 집중할 것이며, 대신 Ubuntu 개발사인 Canonical에서 Flutter 데스크톱에 대한 기여를 이어나갈 것이라고 밝혔다.
3.3. 웹
| |
default 모드: 자바스크립트로 컴파일 및 canvaskit.wasm 렌더러 사용 | WebAssembly 모드: 웹어셈블리로 컴파일 및 skwasm 렌더러 사용 |
빌드된 플러터 웹 앱을 기존 HTML 페이지의 풀 윈도우, iframe, 또는 임의의 html element에 임베딩을 지원하고 있다. package:web을 사용하여 레거시 HTML/CSS와 연동하여 사용도 가능하다.
Flutter 3.32부터 웹에도 Hot Reload 기능이 실험적으로 도입되었으며, 3.35부터는 별도의 실험 플래그 필요없이 자동적으로 Hot Reload를 지원하여 개발 시간이 크게 단축되었다.
4. 백엔드
구글이 운영하고 있는 Firebase 백엔드 서버의 서비스를 사용하는 대신 셀프 호스팅을 지원하는 백엔드 플랫폼인 AppWrite 또는 Supabase등의 사용도 가능하다. 커스텀 서버 사이드 앱 작성도 가능하며 shelf, dart_frog, serverpod, grpc등의 라이브러리를 이용하면 쉽다.5. 상태 관리 패키지
5.1. GetX
상태 관리, 종속성 주입(Dependency Injection), 라우팅 및 기타 강력한 유틸리티들을 한 패키지 안에 탑재하고 있으며 code bloat가 없다. 또한 Flutter에서 어려운 개념 중 하나인 BuildContext를 사용하지 않아 사용 난이도 및 MVC/MVVM 아키텍처 구현이 가장 쉽다는 장점이 있다. 그로 인해 2024년 pub.dev 라이크 순위 1위를 차지하기도 하였다.하지만 이 BuildContext를 사용하지 않는다는 점이 오히려 비판을 받기도 한다. BuildContext는 위젯 트리에서 현재 위젯의 위치와 레이아웃 정보 등을 포함하는 객체로 Flutter에서 중요한 개념인데, 이를 사용하지 않다 보니 사용 중 위젯 트리가 꼬여도 디버깅이 어려워진다. 일각에서는 이러한 점 때문에 GetX를 이용하여 개발하는 것은 Flutter 앱이 아닌 GetX 앱으로 만드는 것이라고 꼬집기도 하였으며#, LINE 개발진도 Flutter 상태 관리 패키지를 비교 탐구하면서 GetX의 이러한 점 때문에 사용을 포기했을 정도라고 언급하기도 하였다.#
또한, 업데이트 주기가 매우 불규칙적이고 텀도 긴 편이라서 사용자의 불안을 일으키고 있다. 실제로도 Github Issue는 1000개가 넘어감에도 불구하고 GetX 4 및 GetX 5 RC 모두 2025년 9월 기준 6개월 넘게 업데이트되고 있지 않으며, GetX 5 RC의 경우 첫 RC 버전이 2023년 2월에 출시되었으니 2년 반이 넘도록 stable 버전으로 업그레이드되고 있지 않다. 최근 이슈란을 보면 이러한 점에 대해 걱정하는 글들이 많이 올라오고 있다.[2]
현재 stable 최신 버전인 GetX 4의 경우 웹에서의 라우팅 관련 버그가 존재한다. 이는 GetX 5 RC 버전을 사용하면 완화된다. GetX 5로의 마이그레이션 가이드(초안)
5.2. BLoC
BLoC의 핵심 개념은 UI 영역과 로직 영역의 직접 연결을 끊고 그 사이를 BLoC이 들어가 이벤트 기반으로 바꿔버리는 것이다. 산하 라이브러리로 Cubit이라는 경량화된 버전도 있다.BLoC은 학습 난이도가 가장 어려운 편이며 보일러플레이트도 가장 많이 발생하기에 처음 입문하기엔 다소 어려울 수 있고 규모가 작은 앱에 도입하기엔 오히려 불필요한 코드가 많아 적합하지 않을 수 있다.
반대로, 규모가 큰 앱에서는 각 코드별로 역할이 명확하게 나눠져 있어 유지보수하기가 가장 편리하다는 점 때문에 국내에서 Flutter를 사용하는 대기업들은 대부분 BLoC을 사용한다. 대표적인 예시로는 삼성의 Dropship, 쿠팡의 쿠팡이츠 사장님/배달 파트너 앱, 라인의 일본 음식 배달 데마에칸 앱, GS의 GS SHOP 및 우리동네GS, SPC의 배스킨라빈스 앱, 신세계그룹의 이마트24 앱 등이 있다.
5.3. Provider
InheritedWidget을 기반으로 만든 상태 관리 패키지. 그래서 Flutter의 보일러플레이트 코드 형태를 답습하고 있는 단점이 있다. 학습 난이도 자체는 쉬운 편이나, Provider 개발자가 후술할 Riverpod을 만들면서 Provider는 유지보수 정도만 진행한다고 하였기에 지금 새로 배운다면 Riverpod을 배우는 것이 좋다.5.4. Riverpod
Provider의 제작자가 만든 Provider의 문제점을 개선한 상태 관리 라이브러리.Provider의 가장 큰 문제였던 BuildContext를 참조할 수 없는 스코프에선 상태 관리가 불가능하다는 문제와 Provider 사용자들을 항상 괴롭혀 왔던 위젯 트리 탐색 문제를 해결한 라이브러리이다.
그러나 학습 곡선과 코드는 Provider에 비해 더 복잡하다. 모든 프로젝트에 Riverpod가 필요한 것은 아니기에 간단한 프로젝트라면 Provider로 구현해도 큰 문제는 없다. 많은 유저들이 권장하는 방법은 아니지만, 리액트의 hooks를 Flutter에서 구현한 flutter_hooks와 같이 쓰기도 한다.
여담으로 Riverpod라는 이름은 Provider의 애너그램이다.
5.5. 기타
그 밖의 상태 관리 라이브러리로는 MobX, get_it 등이 있으며 플러터 공식 문서의 옵션을 참고하자.6. 플러터 사용 예시
Official showcaseIt's all widgets, 2022년초 이후 다수의 웹 앱 예제가 제시되고 있다.
7. 외부 링크
유튜브 공식 Flutter 영상Flutter 공식 문서
Flutter SDK 다운로드
Flutter Packages - 플러터 라이브러리 패키지
공식 트위터 - '금주의 위젯' 등 유용한 소식이 업데이트된다.
Reddit FlutterDev
8. 도서
도서관 자료 목록 - 플러터 링크9. 웹 IDE
Flutter SDK를 로컬 PC에 별도 설치하지 않고도 간단한 예제를 웹 브라우저에서 코딩하고 바로 실행시킬 수 있다.DartPad와 Firebase Studio가 있다.
|