위젯 트리에서 부분리로딩이 중요한 이유
성능 최적화와 개발 효율성 때문에 위젯 트리에서 부분 리로딩이 중요한 이유이다.
- 성능 최적화: 위젯 트리 전체를 다시 렌더링하는 대신, 변경된 부분만 리로딩하면 불필요한 연산을 줄여 앱의 성능을 크게 향상시킬 수 있습니다. 이렇게 하면 사용자가 느끼는 UI 반응 속도가 빨라지고, 리소스를 절약할 수 있습니다.
- 개발 효율성: 부분 리로딩 덕분에 코드 수정 후 전체 앱을 다시 실행하지 않고, 수정된 부분만 빠르게 확인할 수 있습니다. 이는 개발 속도를 높이고, 실시간으로 변화를 테스트할 수 있게 해줍니다.
따라서, 부분 리로딩은 성능과 개발 생산성을 모두 향상시키는 중요한 기능입니다.
1. 전체 페이지 코드
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: ShopPage(),
);
}
}
class ShopPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("ShopPage 그림 그려짐");
return Scaffold(
appBar: AppBar(title: Text("쇼핑카트")),
body: Column(
children: [
// 1. 이미지, 이미지를 사용해서 사이즈를 맞추러면 AspectRatio 무조건 써야한다.
Header(),
// 2. 동그라미 두 개
_circle(),
// 3. 텍스트 필드 (아이콘 포함)
_field(),
],
),
);
}
//사람아이콘을 특정 위치에 넣고 싶을 때 Stack을 쓴다.
Stack _field() {
return Stack(
children: [
TextFormField(
maxLines: 3,
decoration: InputDecoration(
suffixIcon: Icon(Icons.person),
enabledBorder: OutlineInputBorder(),
),
),
Positioned(
left: 200,
top: 50,
child: Icon(Icons.person),
),
],
);
}
// 동그란 두 개의 컨테이너를 보여주는 위젯
Container _circle() {
return Container(
width: 200,
height: 200,
decoration: BoxDecoration(
color: Colors.white, // 바깥 컨테이너의 배경색
border: Border.all(), // 바깥 컨테이너의 테두리
borderRadius: BorderRadius.circular(100),
),
child: Align(
alignment: Alignment(1.0, 0.0), // -1.0~1.0(가로), -1.0~1.0(세로)
child: Container(
width: 150,
height: 150,
decoration: BoxDecoration(
color: Colors.deepOrange,
borderRadius: BorderRadius.circular(75),// 안쪽 컨테이너를 원형으로 만들기 위해 반지름을 75로 설정
),
),
),
);
}
}
class Header extends StatefulWidget {
@override
State<Header> createState() => _HeaderState();
}
class _HeaderState extends State<Header> {
// 객체의 상태!
List<String> imageList = [
"https://picsum.photos/id/100/200/200",
"https://picsum.photos/id/101/200/200"
];
// 상태 관리
int selectedIndex = 0;
@override
// build 실행하고 페이지를 다시 그리지 않게 하기 위해 Context를 분리해야 한다.
// Context를 분리하려면 위젯을 하나 더 만들면 된다.
// Context를 분리하면 부분 리로딩이 가능하다.
Widget build(BuildContext context) {
print("Header 그림 그려짐");
return Column(
children: [
// AspectRatio를 사용해서 이미지를 비율에 맞춰 조절함
AspectRatio(
aspectRatio: 3 / 2,
child: Image.network(
imageList[selectedIndex],
fit: BoxFit.cover,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
width: 70,
height: 70,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(20),
),
child: IconButton(
onPressed: () {
selectedIndex = 0;
print("selectedIndex : $selectedIndex");
setState(() {});
},
icon: Icon(Icons.account_circle_sharp),
),
),
Container(
width: 70,
height: 70,
decoration: BoxDecoration(
color: Colors.redAccent,
borderRadius: BorderRadius.circular(20),
),
child: IconButton(
onPressed: () {
selectedIndex = 1;
print("selectedIndex : $selectedIndex");
setState(() {});
},
icon: Icon(Icons.access_alarms_sharp),
),
),
],
),
],
);
}
}
2. ★Header★(Context 분리, 부분 리로딩)
class ShopPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("ShopPage 그림 그려짐");
return Scaffold(
appBar: AppBar(title: Text("쇼핑카트")),
body: Column(
children: [
// 1. 이미지, 이미지를 사용해서 사이즈를 맞추러면 AspectRatio 무조건 써야한다.
Header(),
//=====================================================================
class Header extends StatefulWidget {
@override
State<Header> createState() => _HeaderState();
}
class _HeaderState extends State<Header> {
// 객체의 상태!
List<String> imageList = [
"https://picsum.photos/id/100/200/200",
"https://picsum.photos/id/101/200/200"
];
// 상태 관리
int selectedIndex = 0;
@override
// build 실행하고 페이지를 다시 그리지 않게 하기 위해 Context를 분리해야 한다.
// Context를 분리하려면 위젯을 하나 더 만들면 된다.
// Context를 분리하면 부분 리로딩이 가능하다.
Widget build(BuildContext context) {
print("Header 그림 그려짐");
return Column(
children: [
// AspectRatio를 사용해서 이미지를 비율에 맞춰 조절함
AspectRatio(
aspectRatio: 3 / 2,
child: Image.network(
imageList[selectedIndex],
fit: BoxFit.cover,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
width: 70,
height: 70,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(20),
),
child: IconButton(
onPressed: () {
selectedIndex = 0;
print("selectedIndex : $selectedIndex");
setState(() {});
},
icon: Icon(Icons.account_circle_sharp),
),
),
Container(
width: 70,
height: 70,
decoration: BoxDecoration(
color: Colors.redAccent,
borderRadius: BorderRadius.circular(20),
),
child: IconButton(
onPressed: () {
selectedIndex = 1;
print("selectedIndex : $selectedIndex");
setState(() {});
},
icon: Icon(Icons.access_alarms_sharp),
),
),
],
),
],
);
}
}
- 회색 아이콘을 클릭했을때 페이지 전체가 리로딩 되는 게 아니라 사진만 리로딩 된다.


코드설명
주요 개념 설명
1. StatefulWidget
과 setState
Header
는StatefulWidget
을 상속받고 있으며, 이를 통해 화면의 상태를 동적으로 관리할 수 있습니다.
setState
메소드를 호출하면 상태가 변경되었음을 Flutter에 알리고, UI가 다시 렌더링됩니다.
- 여기서는
selectedIndex
가 변경될 때마다 UI를 다시 그려서 다른 이미지를 보여줍니다.
2. imageList
imageList
는 두 개의 이미지 URL을 담고 있는 리스트입니다. 이 리스트는 각각 다른 이미지를 나타내고, 선택된 인덱스에 따라 화면에 표시됩니다.
selectedIndex
값에 따라imageList[selectedIndex]
가 변경되고,AspectRatio
위젯에서 해당 이미지가 표시됩니다.
3. AspectRatio
AspectRatio
는 주어진 비율에 따라 위젯의 크기를 결정하는 데 사용됩니다. 여기서는 3:2 비율로 이미지를 표시하고 있습니다.
- 이미지는
BoxFit.cover
속성을 사용하여 공간을 가득 채우도록 조정됩니다.
4. 버튼 구성
- 두 개의 버튼(
IconButton
)이 있고, 각각 다른 이미지를 선택하는 역할을 합니다.
- 첫 번째 버튼을 누르면
selectedIndex
가0
으로 설정되고, 두 번째 버튼을 누르면selectedIndex
가1
로 설정됩니다.
selectedIndex
가 변경되면setState()
가 호출되어 화면이 다시 그려집니다.
5. setState
의 역할
setState()
는 상태가 변경되었음을 알리고, Flutter가 이 변화를 감지하여 UI를 다시 그립니다.
- 버튼을 클릭할 때마다
selectedIndex
가 변경되고, 이에 맞춰 이미지가 변경됩니다.
6. Container
와 BoxDecoration
- 버튼은
Container
로 감싸져 있으며,BoxDecoration
을 사용해 배경색과 둥근 모서리를 적용하고 있습니다.
- 첫 번째 버튼은 회색, 두 번째 버튼은 빨간색(
redAccent
) 배경을 가지고 있으며, 각각 다른 아이콘을 표시합니다.
동작 흐름
- 처음 화면에
selectedIndex
값이0
으로 설정되어 있어 첫 번째 이미지가 표시됩니다.
- 첫 번째 버튼을 클릭하면
selectedIndex
가0
으로 변경되며 첫 번째 이미지가 계속 표시됩니다.
- 두 번째 버튼을 클릭하면
selectedIndex
가1
로 변경되며, 두 번째 이미지로 변경됩니다.
요약
두 개의 버튼을 사용하여 화면에 표시되는 이미지를 동적으로 변경하는 코드입니다. 각 버튼은 클릭할 때
selectedIndex
값을 변경하고, 이에 따라 이미지가 바뀝니다. 이를 통해 상태 관리와 UI 업데이트 방법을 이해할 수 있습니다.3. _Circle()
// 동그란 두 개의 컨테이너를 보여주는 위젯
Container _circle() {
return Container(
//바깥쪽 큰 원
width: 200,
height: 200,
decoration: BoxDecoration(
color: Colors.white,// 바깥 컨테이너의 배경색
border: Border.all(),// 바깥 컨테이너의 테두리
borderRadius: BorderRadius.circular(100),
),
child: Align(
alignment: Alignment(1.0, 0.0), // -1.0~1.0(가로), -1.0~1.0(세로)
child: Container(
//안쪽 작은 원
width: 150,
height: 150,
decoration: BoxDecoration(
color: Colors.deepOrange,
borderRadius: BorderRadius.circular(75),
),
),
),
);
}
}

4. _field()
//사람아이콘을 특정 위치에 넣고 싶을 때 Stack을 쓴다.
Stack _field() {
return Stack(
children: [
TextFormField(
maxLines: 3,
decoration: InputDecoration(
suffixIcon: Icon(Icons.person),
enabledBorder: OutlineInputBorder(),
),
),
Positioned(
left: 200,
top: 50,
child: Icon(Icons.person),
),
],
);
}

코드설명
Stack
의 주요 속성
Stack
은 Flutter에서 여러 위젯을 겹쳐서 배치할 수 있는 위젯입니다.
children
:Stack
안에 여러 자식 위젯을 배치할 수 있습니다.
alignment
: 자식 위젯들의 기본 배치를 설정할 수 있습니다. 기본값은 좌상단입니다.
fit
:Stack
의 크기와 자식 위젯들의 크기를 조정할 때 사용됩니다.
이 코드는
Stack
을 사용하여 TextFormField
위에 아이콘을 원하는 위치에 정확히 배치하는 방법을 보여줍니다. Stack
과 Positioned
를 활용하면, 복잡한 레이아웃을 자유롭게 조정할 수 있습니다.Stack
위젯
Stack
은 자식 위젯들을 겹치는 방식으로 배치합니다. 각 자식 위젯은 기본적으로 상하로 겹치며, 마지막에 추가된 자식이 위에 그려집니다.Positioned
와 함께 사용하면, 자식 위젯을 특정 위치에 배치할 수 있습니다.
- 사용 목적:
Stack
은 일반적으로 화면에 여러 요소를 겹치거나, 특정 요소를 배경에 고정하고 다른 요소를 그 위에 띄울 때 사용합니다.
Positioned
위젯
- *
Positioned
*는Stack
내에서 사용되며, 자식 위젯을 특정 위치에 배치할 수 있습니다. - 예를 들어, 위의 코드에서는
left: 200
과top: 50
을 통해 아이콘을 텍스트 필드의 위에 좌측에서 200, 상단에서 50 떨어진 위치에 정확히 배치합니다.
Share article