코드를 짤 때 코드 분리를 잘 하지 않는 습관이 있는데, 고쳐야지,,하다가도 막상 고치지는 못했었는데, 이번에 프로젝트를 하면서 코드 길이가 너무 길어지는데다가 코드를 재사용해야하는 경우가 생겨서 위젯 파일 분리를 해야했다. 다른 것들은 다 할만 했으나 자식 위젯에서 부모 위젯의 상태를 변경할 수 있는 방법이 헷갈렸었는데, 두가지 방법을 정리해보고자 한다.
1. findAncestorStateOfType() 함수 사용하기
상대적으로 간단한 방법이다. 공식 문서를 보면 이 함수는 BuildContext 하에 있는 함수로, T타입의 StatefulWidget의 가장 가까운 ancestor의 State 객체를 리턴한다. 따라서 findAncestorStateOfType 함수는 자식위젯의 build 함수 내에서 사용될 수 있다. 또한 parent 위젯의 값을 변경할 때는 부모 위젯의 setstate를 호출하여 변경한다. null safety 처리 해주는 것도 한번 확인해 보는 것이 좋다.
실제 코드로 예시를 확인해보자. 자식 위젯은 tap할 때마다 icon 색상이 변하는 위젯이고, 부모 위젯의 hasColor 변수의 값을 변경시키도록 역할을 한다. 먼저 Parent 위젯의 코드이다.
import 'package:find_ancestor_example/Child.dart';
import 'package:flutter/material.dart';
class Parent extends StatefulWidget {
const Parent({Key? key}) : super(key: key);
@override
State<Parent> createState() => ParentState();
}
class ParentState extends State<Parent> {
bool hasColor = false;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.only(top: 15, left: 15),
child: Column(
children: [
Child(hasColor: hasColor),
SizedBox(height: 7,),
Text('light on : ${hasColor}', style: TextStyle(color: Colors.black38, fontWeight: FontWeight.w500))
],
),
)
);
}
}
다음으로 Child 위젯의 코드이다.
import 'package:flutter/material.dart';
import 'package:find_ancestor_example/Parent.dart';
class Child extends StatefulWidget {
const Child({Key? key, required this.hasColor}) : super(key: key);
final bool hasColor;
@override
State<Child> createState() => _ChildState();
}
class _ChildState extends State<Child> {
@override
Widget build(BuildContext context) {
ParentState? parent = context.findAncestorStateOfType<ParentState>();
return GestureDetector(
onTap: () {
parent!.setState(() {
parent.hasColor = !parent.hasColor;
});
},
child: Icon(
Icons.light_mode_rounded,
color: widget.hasColor ? Colors.yellowAccent : Colors.black38,
size: 60,
),
);
}
}
2. 부모 위젯에서 함수를 만들어 자식 위젯에 전달하기
위 방법보다는 아주 조금 더 복잡하기는 한데, 크게 불편하지는 않아서 이 방법 또한 쉽게 사용할 수 있다. 먼저 child 위젯은 parent 위젯으로부터 부모 위젯의 상태를 변경하는 함수를 parameter로 받고 이 함수를 호출할 때 부모 위젯의 state가 변경되도록 하는 것이다. 그리고 변경되기를 원하는 부분을 부모 위젯에서 상태를 변경하는 함수를 만들 때 setstate안에 넣어서 바꾸어 주면 된다.
위에서 본 것과 똑같은 기능을 하는 코드이다. 먼저 부모 위젯의 코드이다.
import 'package:find_ancestor_example/Child.dart';
import 'package:flutter/material.dart';
class Parent extends StatefulWidget {
const Parent({Key? key}) : super(key: key);
@override
State<Parent> createState() => ParentState();
}
class ParentState extends State<Parent> {
bool hasColor = false;
void changeHasColor() {
setState(() {
hasColor = !hasColor;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.only(top: 15, left: 15),
child: Column(
children: [
Child(hasColor: hasColor, notifyParent: changeHasColor,),
SizedBox(height: 7,),
Text('light on : ${hasColor}', style: TextStyle(color: Colors.black38, fontWeight: FontWeight.w500))
],
),
)
);
}
}
다음은 자식 위젯의 코드이다.
import 'package:flutter/material.dart';
import 'package:find_ancestor_example/Parent.dart';
class Child extends StatelessWidget {
const Child({Key? key, required this.hasColor, required this.notifyParent}) : super(key: key);
final bool hasColor;
final Function() notifyParent;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: notifyParent,
child: Icon(
Icons.light_mode_rounded,
color: hasColor ? Colors.yellowAccent : Colors.black38,
size: 60,
),
);
}
}
보면 위 코드는 tap이 될 때마다 상태가 변하므로 GestureDetector 내의 ontap에 notifyParent 함수를 호출해 부모 위젯의 상태를 변경하고 있는 것을 확인할 수 있다.
두 방법 모두 크게 불편하지 않아서 각자 선호하는 것으로 사용하면 좋을 듯 하다.
'Flutter' 카테고리의 다른 글
[Flutter] 플러터에서 Isolate & 비동기 (0) | 2023.10.30 |
---|---|
[Flutter] 카카오로그인, iOS 설정 에러 해결법 정리 (0) | 2022.12.02 |
[Flutter] Null Safety (1) | 2022.11.06 |
[Flutter] Flutter CLI (Command-Line Interface) (0) | 2022.10.06 |
[Flutter] FVM (Flutter Version Management) 사용 방법 (Windows) (1) | 2022.10.01 |