
문제 상황
Flutter는 위젯 트리 구조
부모 → 자식 데이터 전달은 쉬움
조부모 → 깊은 자식 데이터 전달은 불편
A
└ B
└ C
└ D ← A의 데이터를 쓰고 싶다
생성자에 계속 넘기면 불편
➡️ 이걸 해결하기 위해 나온 게 InheritedWidget
위젯 트리를 따라 데이터를 내려보내는 위젯
하위 위젯이 BuildContext로 접근 가능
값이 바뀌면 의존 중인 위젯 rebuild
class MyInheritedWidget extends InheritedWidget {
final int count;
const MyInheritedWidget({
super.key,
required this.count,
required Widget child,
}) : super(child: child);
static MyInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>()!;
}
이걸 호출한 위젯은 의존성 등록
값 변경 시 rebuild 대상
bool updateShouldNotify(MyInheritedWidget oldWidget) {
return count != oldWidget.count;
}
true → 의존 중인 위젯 전부 rebuild
false → 아무도 rebuild 안 됨
class CounterText extends StatelessWidget {
Widget build(BuildContext context) {
final count = MyInheritedWidget.of(context).count;
return Text('$count');
}
}
값이 여러 개일 때
하나만 바뀌어도 전부 rebuild
InheritedWidget의 확장
여러 의존성(aspect)을 분리
필요한 부분만 rebuild
class MyModel extends InheritedModel<MyAspect> { ... }
rebuild 단위
보통 enum 사용
enum MyAspect { color, size }
class MyInheritedModel extends InheritedModel<MyAspect> {
final Color color;
final double size;
const MyInheritedModel({
super.key,
required this.color,
required this.size,
required Widget child,
}) : super(child: child);
전체 변경 여부
bool updateShouldNotify(MyInheritedModel oldWidget) {
return color != oldWidget.color || size != oldWidget.size;
}
bool updateShouldNotifyDependent(
MyInheritedModel oldWidget,
Set<MyAspect> dependencies,
) {
if (dependencies.contains(MyAspect.color) &&
color != oldWidget.color) {
return true;
}
if (dependencies.contains(MyAspect.size) &&
size != oldWidget.size) {
return true;
}
return false;
}
aspect별로 rebuild 판단
InheritedModel.inheritFrom<MyInheritedModel>(
context,
aspect: MyAspect.color,
);
| 항목 | InheritedWidget | InheritedModel |
|---|---|---|
| 의존성 단위 | 하나 | 여러 개 |
| rebuild | 전체 | 부분 |
linter-rules
팀원들끼리 코드 규칙 정할 때
ThemeData는 앱 전체의 색상·글자·버튼 스타일을 한 번에 관리하기 위한 설정 객체
각 위젯에 일일이 스타일을 주지 않기 위해 사용
버튼마다 color, style를 직접 주면 코드가 길어짐
페이지가 많아질수록 디자인 수정이 어려워짐
그래서 MaterialApp 단계에서 ThemeData를 한 번만 설정
👉 “앱 전체의 기본값을 정해두고, 위젯들은 그 기본값을 따라가게 만든다”는 흐름
main.dart → MaterialApp → theme 구조로 사용
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor: Colors.blue,
),
home: const HomePage(),
);
}
}
ThemeData는 항상 MaterialApp 안에 들어감
theme: ThemeData(
primaryColor: Colors.orange,
),
앱의 대표 색상
AppBar, 버튼 등에 기본으로 적용됨
theme: ThemeData(
scaffoldBackgroundColor: Colors.grey[100],
),
모든 Scaffold의 배경색
페이지마다 배경색 안 써도 됨
theme: ThemeData(
textTheme: const TextTheme(
bodyMedium: TextStyle(fontSize: 16),
),
),
Text 위젯의 기본 글자 스타일
TextStyle을 매번 쓰지 않아도 됨
ThemeData(
dividerColor: Colors.grey, // 앱 전체 Divider 색 지정
)
Divider(
color: Theme.of(context).dividerColor, // theme에서 가져와서 적용 가능
thickness: 2,
)
Flutter에서 구분선(Divider) 색상을 지정하는 속성
Divider() 위젯이나 리스트 항목 사이의 선, ListTile 구분선 같은 곳에서 사용
기본적으로 ThemeData 안에서 정의할 수 있어서 앱 전체 Divider 색을 통일할 수 있음
사용자가 값을 드래그해서 선택할 수 있는 위젯
주로 숫자 값 범위를 선택할 때 사용
방향은 기본적으로 수평
최소값(min)과 최대값(max) 사이의 값을 선택
| 속성 | 타입 | 설명 |
|---|---|---|
value | double | 현재 슬라이더 값 |
onChanged | ValueChanged<double> | 값이 바뀔 때 호출되는 콜백 |
min | double | 최소값 (기본 0.0) |
max | double | 최대값 (기본 1.0) |
divisions | int? | 슬라이더를 나눌 구간 수, null이면 연속값 |
label | String? | 드래그할 때 보여줄 값 표시 |
activeColor | Color? | 선택된 영역 색 |
inactiveColor | Color? | 선택되지 않은 영역 색 |
thumbColor | Color? | 슬라이더 원(핸들) 색 |
onChangeStart | ValueChanged<double>? | 드래그 시작 시 호출 |
onChangeEnd | ValueChanged<double>? | 드래그 끝났을 때 호출 |
double _sliderValue = 50;
Slider(
value: _sliderValue,
min: 0,
max: 100,
divisions: 10, // 10단계로 나누기
label: _sliderValue.round().toString(),
onChanged: (double value) {
setState(() {
_sliderValue = value;
});
},
)
_sliderValue를 상태로 관리해야 실시간 값 갱신 가능
divisions를 사용하면 슬라이더가 계단식(step)으로 움직임
label을 설정하면 슬라이더 위에 값 표시
슬라이더를 더 세부적으로 꾸미고 싶으면 SliderTheme 사용 가능
SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: Colors.orange,
inactiveTrackColor: Colors.grey,
thumbColor: Colors.red,
overlayColor: Colors.red.withAlpha(32),
valueIndicatorColor: Colors.blue,
trackHeight: 6,
),
child: Slider(
value: _sliderValue,
min: 0,
max: 100,
divisions: 10,
label: _sliderValue.round().toString(),
onChanged: (value) {
setState(() {
_sliderValue = value;
});
},
),
)
trackHeight → 슬라이더 선 두께
overlayColor → 드래그 시 원 주변 색
valueIndicatorColor → 값 표시 배경 색
여러 스타일을 섞어 한 줄에 표현할 때 사용
Row + Text 3개로 나눠서 구현할 수도 있지만, 글자별 스타일이 다를 경우 RichText가 더 편리
Row(
children: [
Text('Result ', style: TextStyle(fontSize: 18)),
Text('Normal', style: TextStyle(fontSize: 18, color: Colors.pink)),
Text('(BMI 10-30)', style: TextStyle(fontSize: 18)),
],
);
예시: RichText 사용 시
RichText(
text: TextSpan(
style: TextStyle(fontSize: 18),
children: [
TextSpan(text: 'Result: '),
TextSpan(
text: 'Normal',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).highlightColor,
),
),
TextSpan(text: ' (BMI 10-30)'),
],
),
)
TextSpan 클래스는 스타일이 적용된 텍스트 단위
위젯은 아니고, RichText 안에서만 사용
TextStyle 적용 가능
자식 TextSpan도 가질 수 있음 (중첩 가능)
RichText 대신 Text.rich를 사용하면 더 간단하게 작성 가능
Text.rich(
TextSpan(
style: TextStyle(fontSize: 18),
children: [
TextSpan(text: 'Result: '),
TextSpan(
text: 'Normal',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).highlightColor,
),
),
TextSpan(text: ' (BMI 10-30)'),
],
),
)
RichText → 글자별 스타일이 다를 때 사용
TextSpan → 스타일 적용 단위, 위젯 아님
Text.rich → RichText 단축 표현
스타일을 자식에게 상속 가능, 중첩도 가능
Row + Text보다 코드가 간결하고 유지보수가 쉬움


BMI 계산기까지 만들어봤다룡
오늘 목표한 강의 완강까지 완료하고 강의 실습까지 했다룡. 이제 내일부터 과제 시작하고 기본 기능까지 이번주에 완료하는 게 목표다. 빨리 과제 끝내구 위젯 공부 더 해야지~