Flutter 공식 유튜브에 Flutter Technique of the Week라는 재생목록이 있습니다. 플러터에서 제공하는 여러 기술적인 부분들을 알려주는 영상들을 모아둔 것이죠. 이 재생목록의 영상들을 하나씩 보면서, 해당 내용과 관련한 부분들을 묶어서 정리해 보았습니다.
위 영상은 Flutter 공식 유튜브에서 올린 MediaQuery 소개 영상입니다. 간단하게 MediaQuery에 대해 살펴볼 수 있으니, 미리 보는 것을 추천합니다.
Flutter 공식문서의 설명을 보면 아래와 같습니다.
MediaQuery
: child Widget에게 MediaQueryData를 제공하는 WidgetMediaQueryData
: 미디어에 대한 정보. 좀 더 상세히 설명하면, 디바이스 관련 정보 및 유저가 설정한 레이아웃 관련 값들(기본 폰트 사이즈 등)을 의미합니다.
사실 Flutter 유튜브 영상도 그렇고, 대다수의 블로그 글이나 책에서는 MediaQuery가 디바이스 관련 정보를 제공한다고 설명하는 경우가 많습니다. MediaQuery가 디바이스 관련 정보를 제공하는 것은 맞습니다. 그러나, 이것이 MediaQuery가 디바이스 관련 정보를 의미하는 클래스라는 뜻은 아닙니다. 위에서 설명한 것처럼, MediaQueryData
가 디바이스 및 시스템 레이아웃 관련 정보를 가진 클래스이고, MediaQueryData
를 제공하는 Widget이 MediaQuery
입니다.
(다만 사실상 MediaQuery의 용도가 MediaQueryData를 얻기 위한 것이기 때문에, 둘의 용도가 동일하다고 봐도 무방합니다.)
MediaQuery의 주요 속성은 아래와 같습니다. 위에서 언급한 것처럼 디바이스 관련 정보 및 유저가 설정한 UI 관련 옵션 정보를 가지고 있으며, 사실상 MediaQueryData의 속성값이라고 봐도 무방합니다.
참고로 GetX에서는 아래 속성들에 접근할 수 있는 별도의 메서드를 제공하고 있으니, MediaQuery 대신 GetX에서 제공하는 메서드를 사용하는 것이 좋습니다.
예를 들어, 기존 MediaQuery의devicePixelRatio
값은 GetX에선Get.context.devicePixelRatio
를 통해 context 없이 접근할 수 있습니다.
논리 픽셀 단위의 화면 크기를 나타내는 속성입니다.
Size
MediaQuery.sizeOf(context)
혹은 MediaQuery.of(context).size
size
를 호출할 경우, 정상적인 크기가 아닌 Size.zero
가 리턴될 수 있습니다.사실 셋은 비슷한 듯 다른 값입니다. 각각을 여기서 상세하게 다루고 싶지만, 그러기엔 양이 너무 많은 관계로 여기선 셋의 차이 위주로 짚겠습니다.
viewPadding
이나 viewInsets
를 사용해야 합니다.)MediaQuery.paddingOf(context)
혹은 MediaQuery.of(context).padding
padding
은 변화하지만, viewPadding
은 변하지 않습니다.MediaQuery.viewPaddingOf(context)
혹은 MediaQuery.of(context).viewPadding
viewInsets
값을 통해 키보드가 표시되었는지 여부를 감지할 때 유용합니다. 혹은 키보드가 열릴 때 한정으로, 화면 하단에 여백을 추가하여 텍스트 필드나 버튼이 키보드에 의해 가려지지 않도록 처리할 때 유용합니다.MediaQuery.viewInsetsOf(context)
혹은 MediaQuery.of(context).viewInsets
현재 화면의 방향을 나타내는 속성입니다.
Orientation
Orientation.portrait
: 세로 방향Orientation.landscape
: 가로 방향MediaQuery.orientationOf(context)
혹은 MediaQuery.of(context).orientation
디바이스의 물리적 픽셀과 논리적 픽셀의 비율을 나타내는 값입니다. 간단히 말해, 디바이스가 얼마나 많은 물리적 픽셀을 사용해 하나의 논리적 픽셀을 렌더링하는지를 의미합니다.
double
MediaQuery.devicePixelRatioOf(context)
혹은 MediaQuery.of(context).devicePixelRatio
flutter:
assets:
- assets/images/my_image.png
- assets/images/2.0x/my_image.png
- assets/images/3.0x/my_image.png
디바이스 화면의 특정 기능이 있는 영역들에 대한 정보를 제공하는 속성입니다. 여기서 말하는 특정 기능은 접히는 화면, 노치(notch), 카메라 컷아웃, 힌지와 같은 디스플레이 영역을 의미합니다.
List<DisplayFeature>
MediaQuery.displayFeaturesOf(context)
혹은 MediaQuery.of(context).displayFeatures
플랫폼이 사용하는 입력 방식을 나타내는 속성입니다. 키보드+마우스 입력 방식을 사용하는지, 터치 입력 방식을 사용하는지를 나타냅니다.
NavigationMode
NavigationMode.traditional
: 키보드+마우스 입력 방식NavigationMode.directional
: 터치 기반 입력 방식MediaQuery.navigationModeOf(context)
혹은 MediaQuery.of(context).navigationMode
가독성을 높이기 위해 애플리케이션 내 텍스트 크기를 사용자의 접근성 설정에 따라 유연하게 조정할 수 있도록 도와주는 속성입니다.
TextScaler
MediaQuery.textScalerOf(context)
혹은 MediaQuery.of(context).textScaler
Text(
'Hello, World!',
style: Theme.of(context).textTheme.bodyText1?.copyWith(
fontSize: MediaQuery.textScalerOf(context).scale(16),
),
);
textScaleFactor
를 대체하는 속성입니다. (textScaleFactor
는 deprecated됨)textScaleFactor
는 앱 전체의 텍스트 크기를 한꺼번에 변경하는 반면, textScaler
는 각 텍스트별로 크기를 조정할 수 있기 때문에 더 유용합니다.사용자의 접근성 설정 중 "글자 굵게(안드로이드)/볼드체 텍스트(iOS)" 옵션이 활성화되어 있는지를 나타내는 속성입니다.
bool
MediaQuery.boldTextOf(context)
혹은 MediaQuery.of(context).boldText
시각 관련 값을 24시 단위로 포맷팅할지 12시간 단위로 포맷팅할지를 결정하는 속성입니다.
bool
MediaQuery.alwaysUse24HourFormatOf(context)
혹은 MediaQuery.of(context).alwaysUse24HourFormat
플랫폼에서 애니메이션을 비활성화하거나 단순화할 것을 요청하는지 여부를 나타내는 속성입니다. 사용자의 접근성 설정 중 "애니메이션 삭제(안드로이드)/동작 줄이기(iOS)"가 활성화되어 있으면 true
를 리턴합니다.
bool
MediaQuery.disableAnimationsOf(context)
혹은 MediaQuery.of(context).disableAnimations
true
라면 앱의 애니메이션을 비활성화하거나, 최소화함으로써 더 나은 사용자 접근성을 제공할 수 있다.사용자의 터치 및 제스처와 관련된 설정 정보를 제공하는 속성입니다.
GestureSettings
MediaQuery.gestureSettingsOf(context)
혹은 MediaQuery.of(context).gestureSettings
사용자의 접근성 설정 중 고대비 옵션이 켜져있는지를 나타내는 속성입니다.
bool
MediaQuery.highContrastOf(context)
혹은 MediaQuery.of(context).highContrast
사용자의 접근성 설정 중 '색상 반전'이 켜져있는지를 나타내는 속성입니다.
bool
MediaQuery.invertColorsOf(context)
혹은 MediaQuery.of(context).invertColors
사용자가 TalkBack 혹은 VoiceOver와 같은 접근성 서비스를 사용하고 있는지 여부를 나타내는 속성입니다.
bool
MediaQuery.accessibleNavigationOf(context)
혹은 MediaQuery.of(context).accessibleNavigation
accessibleNavigation
를 통해 접근성 서비스를 사용 중인지 확인하고, 이에 알맞는 로직을 짤 수 있습니다.사용자가 iOS의 스위치 내부에 라벨을 표시하도록 설정했는지 여부를 나타내는 속성입니다. iOS에서 '설정 > 손쉬운 사용 > 디스플레이 및 텍스트 크기 > 켬/끔 레이블'에 해당하는 값입니다.
bool
MediaQuery.onOffSwitchLabelsOf(context)
혹은 MediaQuery.of(context).onOffSwitchLabels
true
라면 스위치 내부에 'O'나 '|'와 같은 표시가 나옵니다.플랫폼의 현재 테마입니다. 정확히는, 현재 플랫폼이 라이트모드(라이트 테마)를 사용하는지, 다크모드(다크 테마)를 사용하는지를 나타내는 속성입니다.
Brightness
Brightness.dark
: 다크모드Brightness.light
: 라이트모드MediaQuery.platformBrightnessOf(context)
혹은 MediaQuery.of(context).platformBrightness
platformBrightness
값을 통해 현재 플랫폼의 테마를 확인할 수 있습니다.Brightness.light
를 리턴합니다.디바이스가 시스템 컨텍스트 메뉴(복사, 붙여넣기 등)를 표시할 수 있는지를 나타내는 속성입니다. 이 속성은 특히 모바일과 데스크톱 간의 차이를 처리하는 데 유용합니다.
bool
MediaQuery.supportsShowingSystemContextMenuOf(context)
혹은 MediaQuery.of(context).supportsShowingSystemContextMenu
시스템 제스처가 차지하는 영역에 대한 여백을 나타내는 속성입니다. 이 여백은 보통 시스템 제스처(예: 스와이프 제스처)가 차지하는 화면의 경계 부분을 의미합니다.
EdgeInsets
MediaQuery.systemGestureInsetsOf(context)
혹은 MediaQuery.of(context).systemGestureInsets
MediaQuery.of
VS MediaQuery.propertyOf
이제부턴, 'Flutter Technique of the Week' 영상에 나온 이야기를 해 보겠습니다.
Flutter에서는 MediaQuery를 사용할 때MediaQuery.propertyOf(context)
를 사용할 것을 권장하고 있습니다. 왜 그럴까요? 무슨 차이가 있는지 자세히 알아봅시다.
MediaQuery.of(context)
가 비효율적인 이유: MediaQuery의 하위 속성 중 하나라도 변경된다면 MediaQuery 자체가 rebuild되기 때문에 지나친 rebuild로 인한 리소스 낭비가 발생할 수 있습니다. (심지어 현재 클래스에서 사용하지 않는 속성값이 변경되더라도 rebuild가 발생합니다.)
(참고 : 정확히는, MediaQuery의 속성 중 하나인 MediaQueryData
의 필드 값 중 하나라도 변하면 rebuild가 발생합니다.)
위에서 살펴본 것처럼, MediaQuery의 속성은 상당히 많습니다. 20개가 넘어가죠. 이 중 실제로 우리가 사용하는 값은 많아봐야 1~2개입니다. 그렇기에, 우리가 사용하는 이 1~2개의 값이 변할 때만 rebuild가 일어나면 되는 거죠. 하지만, MediaQuery.of(context)
는 현재 코드에서 사용하지 않는 하위 속성이 변경되더라도 rebuild가 일어납니다.
예시를 통해 좀 더 자세히 알아보죠.
MediaQuery.of(context).size
로 사용할 겁니다.MediaQuery.of(context).padding
을 사용하지 않았음에도 불구하고, 해당 값이 변할 때마다 MediaQuery가 rebuild되는 비효율적인 상황이 발생합니다.MediaQuery.propertyOf(context)
는 다르다!: 코드에서 사용하는 'property'에 해당하는 값이 변할 때만 MediaQuery를 rebuild하기 때문에, MediaQuery의 rebuild 횟수를 크게 줄일 수 있습니다.
결론 : MediaQuery를 사용한다면
MediaQuery.of(context)
대신MediaQuery.propertyOf(context)
를 사용하자!
수몽 나 윤광