반응형
하나의 StatefulWidget에서 상태 값을 관리하는 방법은 setState()를 사용하는 것입니다. setState() 없이 상태 값을 관리할 수도 있는데, ValueNotifier를 사용하는 것입니다. ValueNotifier가 좋은 것은 다른 위젯에서 변경한 상태값을 적용할 수 있습니다.
setState() 없이 상태값을 변경하는 예제
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final ValueNotifier<int> _counter = ValueNotifier<int>(0); // ValueNotifier 변수 선언
final Widget goodJob = const Text('Good job!');
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title)
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
ValueListenableBuilder(
valueListenable: _counter, // 사용할 변수를 지정. _counter가 변경 되면 자동 호출
builder: (BuildContext context, int value, Widget? child) {
// value = _counter 로 적용
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text('$value'),
child!, // child는 아래 지정된 위젯으로 치환됨
],
);
},
child: goodJob,
)
],
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.plus_one),
onPressed: () => _counter.value += 1, // _counter.value 값이 수정되면 자동 호출
),
);
}
}
ValueNotifier로 수정한 값을 다른 위젯에서도 사용하고 싶을때
ValueNotifier가 영향을 미치는 범위는 ValueListenableBuilder() 내 입니다. 전체 위젯에 값을 적용하기 위해서는 setState()를 사용해야 하나 그럴 경우 오류가 발생합니다. build가 완료되기 전에 setState()를 호출하게 되면 빨간 화면을 마주하게 됩니다.
이 오류를 피하면서 setState()를 사용하는 방법은 WidgetsBinding.instance.addPostFrameCallback() 입니다.
addPostFrameCallback()는 페이지 빌드 후에 비동기로 콜백함수를 호출하기 때문에 빨간 화면을 피할 수 없습니다.
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final ValueNotifier<int> _counter = ValueNotifier<int>(0); // ValueNotifier 변수 선언
final Widget goodJob = const Text('Good job!');
int _count = 0;
void setCount(int counter) {
setState(() {
_count = counter + 1;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title)
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
ValueListenableBuilder(
valueListenable: _counter, // 사용할 변수를 지정. _counter가 변경 되면 자동 호출
builder: (BuildContext context, int value, Widget? child) {
// setState() 가 있는 함수를 호출
WidgetsBinding.instance.addPostFrameCallback((_) {
setCount(value);
});
// value = _counter 로 적용
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text('$value'),
child!, // child는 아래 지정된 위젯으로 치환됨
],
);
},
child: goodJob,
),
Container(
child: Text('$_count')
),
],
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.plus_one),
onPressed: () => _counter.value += 1, // _counter.value 값이 수정되면 자동 호출
),
);
}
}
반응형