Weekly Flutter

[Flutter 주석 파헤치기] 02. Inherited Widget

davidchoi0531 2023. 2. 22. 11:26

Flutter Framework 내부의 위젯 종류에는 크게 3가지 정도가 있다.

StatefulWidget, StatelessWidget, InheritedWidget

StatefulWidget과 statelessWidget은 지난 글에서 내용을 확인 할 수 있고, 그 다음 InheritedWidget에 대하여 알아보고자 한다.

 

InheritedWidget

  • InheritedWidget은 정보를 트리 아래로 효율적으로 전달하기 위한 위젯의 기본 클래스다.
class FrogColor extends InheritedWidget {
  const FrogColor({
		super.key,
		required this.color,
		required super.child,
  });

  final Color color;

  static FrogColor? maybeOf(BuildContext context) {
	  return context.dependOnInheritedWidgetOfExactType<FrogColor>();
  }

	static FrogColor of(BuildContext context) {
		final FrogColor? result = maybeOf(context);
		assert(result != null, 'No FrogColor found in context');
	return result!;
	}

  @override
  bool updateShouldNotify(FrogColor oldWidget) => color != oldWidget.color;
}
class MyPage extends StatelessWidget {
	const MyPage({super.key});

	@override
	Widget build(BuildContext context) {
		return Scaffold(
	   body: FrogColor(
	     color: Colors.green,
		     child: Builder(
		       builder: (BuildContext innerContext) {
		         return Text(
		           'Hello Frog',
		           style: TextStyle(color: FrogColor.of(innerContext).color),
		         );
		       },
		     ),
		   ),
		 );
	}
}
BuildContext.dependOnInheritedWidgetOfExactType<T>

해당 메서드는 트리에서 가장 가까운 T 타입의 위젯을 리턴하여 준다.

bool updateShouldNotify(FrogColor oldWidget) => color != oldWidget.color;

해당 메서드는 상태변화가 적용되어야 하는지에 대한 boolean 값을 리턴하도록 구현하여야 한다.

Flutter framework 내부의 InheritedWidget에 대한 주석을 살펴 보면, 코드상에서 볼 수 있듯이 ‘maybeOf’ method는 InheritedWidget을 상속받은 nullable T를 리턴하도록 하고, ‘of’ method는 null이 리턴될 때 오류를 발생시킨다고 되어있다.

또한 주석 상의 용례를 보면 builder를 사용하여 BuildContext를 parameter로 받아 ‘of’ method를 사용하여 FrogColor를 찾아내어 ‘Hello Frog’ String의 스타일에 적용한다.

class MyPage extends StatelessWidget {
	const MyPage({super.key});

	@override
	Widget build(BuildContext context) {
		return Scaffold(
	   body: FrogColor(
	     color: Colors.green,
		     child: Text(
		           'Hello Frog',
		           style: TextStyle(color: FrogColor.of(context).color),
		         );
		   ),
		 );
	}
}

다음과 같은 예시를 확인 해 보자. 해당 코드가 실행 될 경우, FrogColor.of(… method에서 오류가 발생한다. 해당 ‘of’ method에 주어진 context는 FrogColor를 찾을 수 없기 때문이다.

BuildContext와 Widget Tree 에 관한 내용은 다음에 추가적으로 확인 해 보도록 하자.

InheritedWidget 사용시 rebuild 에 비용이 많이 소모가 될 경우 InheritedModel을 사용 할 수 있다. InheritedModel의 주석을 확인 해 보면,

InheritedWidget의 종속 위젯들은, InheritedWidget.updateShouldNotify를 통하여 InheritedWidget이 바뀔 때 무조건 rebuild된다. InheritedModel은 무조건 rebuild되는 것을 제외하면 InheritedWidget과 유사하다. 라고 되어있다.

class ABModel extends InheritedModel<String> {
   const ABModel({
    super.key,
    this.colorA,
    this.colorB,
    required super.child,
   });

   final Color? colorA;
   final Color? colorB;

   @override
   bool updateShouldNotify(ABModel oldWidget) {
     return colorA != oldWidget.colorA || colorB != oldWidget.colorB;
   }

   @override
   bool updateShouldNotifyDependent(ABModel oldWidget, Set<String> dependencies) {
     return (colorA != oldWidget.colorA && dependencies.contains('colorA'))
       || (colorB != oldWidget.colorB && dependencies.contains('colorB'));
   }

   // ...
 }
class MyPage extends StatelessWidget {
	const MyPage({super.key});

	@override
	Widget build(BuildContext context) {
		return Scaffold(
	   body: ABModel(
	    colorA: Colors.green,
			colorB: Colors.blue,
		     child: Builder(
		       builder: (BuildContext innerContext) {
									final model = InheritedModel.inheritFrom<ABModel>(
										innerContext,
										aspect: 'colorA',
									);
		         return Text(
		           'Hello Frog',
		           style: TextStyle(color: model.colorA),
		         );
		       },
		     ),
		   ),
		 );
	}
}

InheritedModel.inheritFrom<T> method를 사용하여 InheritedModel를 찾을 수 있다.

만약 aspect 인자가 null 이라면 연관된 모든 항목에 대하여 업데이트를 시키게 된다.

이와같이 InheritedWidget, InheritedModel을 간단하게 예시를 통하여 알아보았다.

여러가지 상황에서 유용하게 쓸 수 있을 것이라 생각이 된다. 하지만 위젯의 공통 데이터 상태 관리는 provider라는 라이브러리가 존재 한다. 또한 provider는 InheritedWidget을 기반으로 한 상태관리 라이브러리 이기 때문에 해당 위젯을 직접적으로 사용하기 보다는 provider를 사용 하는것이 더 편리하다.

provider 이외에도 riverpod, getx, rxdart, bloc 등이 존재 하는데, 단순히 상태관리뿐 아니라 비동기 프로그래밍, 반응형 프로그래밍을 위한 라이브러리이기 때문에 다르다고 할 수 있다.

inheritedWidget을 기반으로 한 라이브러리 - provider, riverpod

Stream을 기반으로 한 비동기 처리 및 상태관리 - bloc, getx, rxdart

오늘은 inheritedWidget에 대하여 알아보았다. 실제 업무상 사용 예 대신에 주석의 몇가지 예시를 통해 알아보았으나 개념적 부분들은 충분히 이해 할 수 있을 것이라 생각한다.