강의중 더 자세하고 예제까지 넣기위해 글에서 따로 빼서 작성한다.

깊게 볼필요는 없을것 같아서 짧게만 적자면
변경이 불가한 위젯.
위젯 트리에 필요한 buildContext외에는 의존이 필요없는곳에서 사용하는 위젯.

이 글을 적기위한 내용이라고 볼수도 있다.
_MyWidgetState createState() => _MyWidgetState();
한번만 호출이 된다는 특징이 있다.
함수 특성대로 초기화 시에 필요한 내용은 이곳에작성하면 되지만 아직 context(위젯트리)에 접근이 불가 하다.
void initState() {
super.initState();
// 초기화 작업
}
이유는 호출이 되는 가지수가 두가지인데
첫번째는 당연히 initstate가 호출될때, 그리고 두번째는 InheritedWidget가 변경이 될때이다.
InheritedWidget라는 개념에 대해 잠깐알아보자.
InheritedWidget
/// InheritedWidget 정의 class MyInheritedWidget extends InheritedWidget { final int value; const MyInheritedWidget({ super.key, required this.value, required super.child, }); ㅤ static MyInheritedWidget? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>(); } ㅤ bool updateShouldNotify(MyInheritedWidget oldWidget) { return oldWidget.value != value; } }우리가 알던 위젯들과 비슷하면서도 조금 다른점이 있다.
of 함수와 updateShouldNotify 함수가 있으며 여기서 value값은 임의로 넣은값이다.
of를 먼저 보자면 건내받은 context의 .dependOnInheritedWidgetOfExactType 값을 전달해 주는 함수이다.
dependOnInheritedWidgetOfExactType<T>는 현재 context에서 가장 가까운 T를 반환하고 없으면 null을 띄운다는것이다.
즉 만약 위젯트리가
이렇게 있고 CardBoard가 위의 예시처럼 MyInheritedWidget와 동일하다는 가정으로.
CardWidget에서 CardBoard.of(context)?.value를 사용한다를 풀어쓰면
CardWidget에서 context로 위젯트리를 거슬러 올라가 가장 가까운 CardBord로 접근을 해서 그곳의 value를 호출한다 보면 된다.
of는 이쯤으로 하고
updateShouldNotify 함수는 위젯의 value 값이 변경이 됐는지에 대한 bool값을 리턴해준다.
이때 updateShouldNotify 리턴값이 true가 되면 드디어 우리가 기다리던 didChangeDependencies가 호출이 되는것이다.
코드를 보면서 좀더 설명하자면
void didChangeDependencies() {
super.didChangeDependencies();
print("📢 didChangeDependencies 호출됨!");
// InheritedWidget에서 새로운 값을 가져오기
value = MyInheritedWidget.of(context)?.value ?? 0;
}
최초 CardWidget이 실행시 didChangeDependencies가 호출이 되고 이때 MyInheritedWidget.of를 사용하고있음에 of의 내부 dependOnInheritedWidgetOfExactType 함수가 호출됨으로 구독이 진행이된다.
구독이라함은 InheritedWidget로 선언된 MyInheritedWidget를 element 트리에 저장을 하며 하위 위젯에 접근이 가능하게 한다.
참고로 구독이되지 않고 접근하고싶다면 getElementForInheritedWidgetOfExactType로 사용하면된다.
구독이 되었기 때문에 MyInheritedWidget의 값 value 가 변경이되면 MyInheritedWidget의함수 updateShouldNotify 가 true로 리턴이 되고 didChangeDependencies가 호출이 되면서 값이 새로고침이 된다고 보면된다.
처음 생성 될때의 로직은 이대로 끝이고 두가지의 다른 케이스에 대해 알아보자.
예를 들어 상위 위젯에서 다음에 나올 ui새로고침인 setstate가 호출되었을 경우이다. 이 경우 State는 유지가 되지만 didUpdateWidget가 호출이되고 속성이 변하지 않았더라도 didUpdateWidget와 build가 호출이 된다.
예제로 드디어 떠나보자
현제 위젯 트리와 화면 구성

구성은 간단하다
메인에서 MyInheritedWidget에 value가 있으며 하위 위젯으로 MyWidget이 있고 MyInheritedWidget의 value를 사용중에있다.
카운터 증가를 누르면 value가 변경이 되며 그 값이 MyWidget로 전달되는 과정에서 어떤 함수들이 호출이 되는지 알아 볼것이다.

코드 보단 흐름에 주목을 하려해서 코드는 맨 마지막에 작성하겠다. 함수명도 최대한 생략.
맨 상위 parent가 생성이되고 init-다음 didchange-, build가 호출된다.
후에 하위 위젯인 myWIdget의 init-과 didchange-가 호출되고 didchange에서 선언중인 of 함수로 인해 상위 위젯인 MyInheritatedWidget이 호출된다.
그 후 build가 호출이 된다.
이때 카운터의 값을 증가하는 버튼을 누르면

parent-위젯에서의 버튼 setstate에 의해 build가 호출되고.
counter의 값이 바뀜에 따라 myInherited에서 감지가 되어 updateShould-에서 true를 반환 element값 수정
parent-위젯이 리빌드 됨에따라 didupdateWidget 호출.
바뀐element 값으로 인해 myWidget에서 didupdated-가 호출, myInherited-위젯의 of를 호출, count값 최신화.
myWidget build 완료.
순으로 된다.
말로는 복잡하지만 아래 코드와 함꼐 실행하면서 보면 이해가 될것이다.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: ParentWidget(),
);
}
}
/// InheritedWidget 정의
class MyInheritedWidget extends InheritedWidget {
final int value;
const MyInheritedWidget({
super.key,
required this.value,
required super.child,
});
static MyInheritedWidget? of(BuildContext context) {
print("📢 MyInheritedWidget of 호출됨!");
return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
}
bool updateShouldNotify(MyInheritedWidget oldWidget) {
print("📢 MyInheritedWidget updateShouldNotify 호출됨!");
return oldWidget.value != value;
}
}
/// 부모 위젯 - 상태를 관리하고 MyInheritedWidget을 감싸는 역할
class ParentWidget extends StatefulWidget {
const ParentWidget({super.key});
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
int _counter = 0;
void initState() {
// TODO: implement initState
super.initState();
print("📢 ParentWidget initState 호출됨!");
}
void didUpdateWidget(covariant ParentWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print("📢 ParentWidget didUpdateWidget 호출됨!");
}
void didChangeDependencies() {
super.didChangeDependencies();
print("📢 ParentWidget didChangeDependencies 호출됨!");
}
void _increment() {
setState(() {
print("버튼 클릭");
_counter++;
});
}
Widget build(BuildContext context) {
print("📢 ParentWidget build 호출됨!");
return Scaffold(
appBar: AppBar(title: const Text("InheritedWidget 예제")),
body: MyInheritedWidget(
value: _counter,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MyWidget(),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _increment,
child: const Text("카운터 증가"),
),
],
),
),
);
}
}
/// MyWidget - InheritedWidget의 데이터를 읽고 반응하는 위젯
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
int value = 0;
void initState() {
// TODO: implement initState
super.initState();
print("📢 MyWidget initState 호출됨!");
}
void didUpdateWidget(covariant MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print("📢 MyWidget didUpdateWidget 호출됨!");
}
void didChangeDependencies() {
super.didChangeDependencies();
print("📢 MyWidget didChangeDependencies 호출됨!");
// InheritedWidget에서 새로운 값을 가져오기
value = MyInheritedWidget.of(context)?.value ?? 0;
}
Widget build(BuildContext context) {
print("📢 MyWidget build 호출됨!");
return Center(
child: Text(
"현재 값: $value",
style: const TextStyle(fontSize: 24),
),
);
}
}
다음은 비교적 간단하다 생각되는 setState를 해보겟다.


이전 코드에서 Inherited가 사라졋으며 count값을 파라미터로 넘겨받고있다.
실행하면 
우리가 알고있는대로 init - didchange - build 순으로 호출이 된다.
카운터 증가 버튼을 누르면

parent의 setstate가 호출되어 build가 호출되고 상위 위젯이 호출됨에 따라 myWidget의 didupdate-가 호출되며 다시 build가 됨을 알수있다.
아래는 코드다.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: ParentWidget(),
);
}
}
class ParentWidget extends StatefulWidget {
const ParentWidget({super.key});
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
int _count = 0;
void initState() {
// TODO: implement initState
super.initState();
print("📢 ParentWidget initState 호출됨!");
}
void didUpdateWidget(covariant ParentWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print("📢 ParentWidget didUpdateWidget 호출됨!");
}
void didChangeDependencies() {
super.didChangeDependencies();
print("📢 ParentWidget didChangeDependencies 호출됨!");
}
void _increment() {
setState(() {
print("버튼 클릭");
_count++;
});
}
Widget build(BuildContext context) {
print("📢 ParentWidget build 호출됨!");
return Scaffold(
appBar: AppBar(title: const Text("setState 예제")),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MyWidget(count: _count),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _increment,
child: const Text("카운터 증가"),
),
],
),
);
}
}
class MyWidget extends StatefulWidget {
const MyWidget({super.key, required this.count});
final int count;
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
void initState() {
// TODO: implement initState
super.initState();
print("📢 MyWidget initState 호출됨!");
}
void didUpdateWidget(covariant MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print("📢 MyWidget didUpdateWidget 호출됨!");
}
void didChangeDependencies() {
super.didChangeDependencies();
print("📢 MyWidget didChangeDependencies 호출됨!");
}
Widget build(BuildContext context) {
print("📢 MyWidget build 호출됨!");
return Center(
child: Text(
"현재 값: ${widget.count}",
style: const TextStyle(fontSize: 24),
),
);
}
}