[Flutter] Theme와 세 가지 트리
Theme 속성을 사용하면 애플리케이션 전체에서 사용될 테마를 지정할 수 있다.
import 'package:flutter/material.dart';
import 'package:expense_tracker/widgets/expenses.dart';
// 공통으로 사용할 디자인 설정을 k로 시작하도록..
var kColorScheme = ColorScheme.fromSeed(seedColor: const Color.fromARGB(255, 96, 49, 181));
void main() {
runApp(
MaterialApp(
theme: ThemeData().copyWith(
useMaterial3: true,
scaffoldBackgroundColor: Colors.deepPurpleAccent,
colorScheme: kColorScheme,
appBarTheme: const AppBarTheme().copyWith(
backgroundColor: kColorScheme.onPrimaryContainer,
foregroundColor: kColorScheme.primaryContainer,
),
cardTheme: CardTheme().copyWith(
color: kColorScheme.secondaryContainer,
margin: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: kColorScheme.primaryContainer,
),
),
textTheme: ThemeData().textTheme.copyWith(
titleLarge: TextStyle(
fontWeight: FontWeight.normal,
color: kColorScheme.onSecondaryContainer,
fontSize: 14,
),
),
),
home: const Expenses(),
),
);
}
ColorScheme 클래스는 MaterialDesign의 색상 테마를 나타내는 클래스로 ThemaData.colorScheme 에 적용돼 애플리케이션의 테마 색상을 제공한다.
ColorSchme의 fromSeed 메서드를 사용하면 seedColor를 기준으로 색상 스키마를 자동으로 생성한다.
k 로 시작하는 변수는 보통 상수를 의미하고 애플리케이션 전체에서 공통적으로 사용될 색상을 정의한다.
ColorScheme 내부 속성을 사용해 일관성있게 디자인 할 수 있다.
primary : 기본 색상. 앱에서 주로 사용되는 색상이다.
secondary : 보조 색상. 기본 색상과 구분되는 색상이다.
error : 오류 상황에서 사용되는 색상이다.
primaryContainer : 주요 색상을 사용하는 컨테이너 요소의 배경색을 지정할 때 사용한다.
on + a : 특정 색상 위에 그려질 때 사용한다.
...
이 외에도 다양한 속성을 제공하니 필요할 때 찾아서 사용하자.
MaterialApp의 theme 속성은 애플리케이션 전체에 적용되는 테마를 정의한다.
ThemeData에는 여러 요소에 대해 미리 정의된 스타일이 포함되어있다.
앱 전체에서 일관된 디자인을 제공하기 위해서 사용되고, 위의 예시에서는 copyWith 메서드를 사용해 기존 ThemaData를 기반으로 원하는 속성만 추가해 새로운 ThemaData를 만드는 데 사용하고 있다.
background: Container(
color: Theme.of(context).colorScheme.error,
margin: EdgeInsets.symmetric(horizontal: Theme.of(context).cardTheme.margin!.horizontal),
),
이렇게 main.dart의 runApp 메서드에서 theme 속성을 지정해두면 runApp을 필두로 렌더링되는 다른 위젯들의 디자인을 변경할 때 Theme.of(context) 메서드로 현재 context에서 가장 근접한 ThemeData에 액세스 할 수 있다.
그냥 color : Colors.red 로 지정하는 것 보다 color : Theme.of(context) 방식을 사용하는 편이 일관성, 재사용성, 유연성, 접근성 측면에서 훨씬 효과적이다. (가능한 한 테마를 사용하자! 다크 모드를 구현할 때도 효과적이다)
final isDarkMode = MediaQuery.of(context).platformBrightness == Brightness.dark;
플러터가 제공하는 위젯인 MediaQuery는 현재 애플리케이션이 실행되고 있는 화면의 사이즈, 방향, 밝기 등의 정보를 얻어올 수 있다.
역시 MaterialApp 같은 애플리케이션의 시작이 되는 위젯이 MediaQuery를 자동으로 생성해준다.
플러터로 위젯을 통해 UI를 구성하는데 각 위젯마다 차지하게 되는 화면의 부분이 다르고 가질 수 있는 자식의 수도 다르다.
Row와 Column 위젯은 각각 가로 방향과 세로 방향으로 자식 위젯을 배열하고, 가로 방향과 세로 방향으로 확장돼 가능한 모든 공간을 차지하려 한다.
Expanded 위젯은 Row나 Column의 여분 공간을 채우기 위해 사용된다.
Row 위젯의 자식으로 Expanded 위젯을 배치하면 해당 위젯은 Row가 가진 여분 공간을 모두 차지하게 된다.
기본적으로 위젯은 부모의 제약사항과 위젯 자체의 속성에 따라 차지하는 크기가 결정된다.
Column 위젯의 높이는 최대한 많이 차지하고, 너비는 자식 요소에 따라 결정되는 속성을 가지지만 위젯의 부모가 있다면 부모 위젯이 높이 속성을 제한한다.
Row 위젯의 자식으로 Row를 사용하는 경우 위젯은 부모의 여유 공간이 얼마나 되는지 알 수 없어 레이아웃 오류가 발생할 수 있다. (공간이 모두 Infinity 로 설정된다)
따라서 Expanded 위젯으로 여유 공간을 Infinitiy 대신 정확한 수치로 제한해 둬서 문제를 해결한다.
위젯이 화면을 어떻게 차지하는지를 잘 고려해서 UI를 구성하자.
LayoutBuilder(builder: (ctx, constraints) {
//...
}
애플리케이션을 가로 모드로 전환했을 때 위젯을 이전과는 다르게 배치한다던가 애플리케이션이 실행되는 환경이 IOS 일 때는 안드로이드 환경과는 다르게 위젯을 배치한다던가... 이런 식으로 반응형 애플리케이션을 제작할 때는 LayoutBuilder 위젯을 사용한다.
builder 메서드의 파라미터로 BuilderContext 객체와 BoxConstraints 객체를 받는다.
constraints 객체로 부모 위젯이 현재 위젯에게 부과하는 제약 조건을 알 수 있고, 해당 정보를 통해 너비 또는 높이가 특정 수치 이상인 경우 위젯을 다른 방식으로 배치하도록 설정할 수 있다.
build 메서드가 호출됐을 때 위젯 트리를 처음부터 생성하고 교체하는 건 아니다.
먼저 위젯 트리를 검사하고 재사용할 수 있는 위젯은 재사용해서 새로운 위젯을 만든다.
이후 기존의 위젯 트리와 새로 만든 위젯 트리를 비교해 업데이트가 필요한 부분이 있다면 그 부분만 새로 렌더링한다.
이 부분을 이해하기 위해 세 가지 트리 구조에 대해 알아보자.
Widget Tree는 플러터 애플리케이션의 구조를 나타내고, 위젯이 계층을 이뤄 위젯 트리를 형성한다.
Element Tree는 Widget Tree를 인스턴스화 할 때 사용된다.
위젯에 대응하는 요소가 하나씩 있고, 각 요소들은 한 번만 생성되고 위젯이 여러 번 빌드될 수 있도록 한다.
Render Tree는 실제로 화면에 그려지는 객체를 포함한다.
Element와 관련된 렌더링 정보를 포함하고 실제로 화면에 UI를 그리는 역할을 한다.
위젯 트리로 애플리케이션의 틀을 제공하고, 엘리먼트 트리로 실제로 메모리에 저장되는 위젯 인스턴스의 상태를 유지한다.
플러터 엔진은 렌더 트리로 UI를 그리고, 렌더 트리는 엘리먼트 트리를 통해 재사용 할 수 있는 부분을 재사용한다.
위젯 트리의 내부 요소가 변경되면 새로운 위젯 트리가 생성된다.
새로운 위젯 트리는 엘리먼트 트리와 매칭되고, 기존 엘리먼트 트리의 각 엘리먼트들은 새로운 위젯과 같은 위치에 있는 위젯을 참조하게 된다.
플러터에서 위젯은 불변하고 위젯 트리는 상태가 변경될 때 마다 새로 생성되며, 엘리먼트 트리는 한 번 생성된 후 계속해서 재사용되고 업데이트 될 수 있다.
플러터의 엘리먼트가 위젯을 재사용 할 수 있다고 판단하는 기준은 위치, 타입, 키 이다.
키를 제공하지 않으면 위치와 타입을 기준을 재사용 할 수 있다고 판단하기에 위젯의 상태를 올바르게 연결할 수 없는 경우가 발생한다.
엘리먼트가 위젯의 상태를 제대로 관리하기 위해 위젯을 구분하는 키가 도입됐다.
'Mobile > Flutter' 카테고리의 다른 글
[Flutter] 애니메이션 (1) | 2023.06.25 |
---|---|
[Flutter] Riverpod 상태 관리 (0) | 2023.06.24 |
[Flutter] 여러 가지 화면과 사용자 입력 관리 (0) | 2023.06.17 |
[Flutter] 위젯 렌더링과 다트 문법 (0) | 2023.06.16 |
[Flutter] Dart 언어와 StatefulWidget (0) | 2023.06.14 |
댓글
이 글 공유하기
다른 글
-
[Flutter] 애니메이션
[Flutter] 애니메이션
2023.06.25 -
[Flutter] Riverpod 상태 관리
[Flutter] Riverpod 상태 관리
2023.06.24 -
[Flutter] 여러 가지 화면과 사용자 입력 관리
[Flutter] 여러 가지 화면과 사용자 입력 관리
2023.06.17 -
[Flutter] 위젯 렌더링과 다트 문법
[Flutter] 위젯 렌더링과 다트 문법
2023.06.16