1. Widget의 부모 컨테이너 설정 오류
모든 위젯은 Container로 감싸는 것이 좋습니다. 특히 Row 또는 Column 내부에 TextField를 직접 넣는 경우, 반드시 Expanded으로 감싸야 합니다.
Row(
children: [
Expanded(
child: TextField(
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.orange),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.red),
),
),
),
),
],
)
2. ListView 내부에서 크기 조절 문제
ListView가 Column 안에 있을 때는 shrinkWrap: true를 반드시 설정해야 합니다. 이 값이 없으면 리스트 뷰가 화면 전체 높이를 차지하므로 레이아웃이 깨집니다.
Column(
children: [
Text("상단 텍스트"),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: 10,
itemBuilder: (context, index) {
return ListTile(title: Text("항목 $index"));
},
),
),
],
)
3. 화면 리사이징 및 디바이스 대응
다양한 해상도와 화면 크기에 대응하기 위해 ScreenUtil이나 MediaQuery를 활용하세요.
- 화면 하단 여백 계산:
MediaQuery.of(context).viewInsets.bottom - 상단 상태바 높이:
MediaQuery.of(context).padding.top
4. 이미지 로딩 및 예외 처리
네트워크 이미지 로드 실패 시, 로컬 이미지를 대체하여 표시할 수 있습니다.
Image.network(
'https://example.com/image.jpg',
height: double.infinity,
width: double.infinity,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) => Image.asset(
'assets/images/placeholder.png',
height: double.infinity,
width: double.infinity,
fit: BoxFit.cover,
),
)
5. State 관리 및 비동기 처리
setState()는 현재 위젯 트리를 다시 빌드합니다. 비동기 작업 후 결과를 사용할 땐 FutureBuilder를 사용하고, 복잡한 위젯은 별도 클래스로 분리하세요.
FutureBuilder<Response>(
future: fetchData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return DetailWidget(data: snapshot.data!.data);
}
return CircularProgressIndicator();
},
)
6. null-safe 연산자 활용
??와 ?.는 필수적인 연산자입니다.
a ?? b: a가null이면b반환, 아니면a반환a?.b: a가null이면null반환, 아니면a.b호출
7. RichText로 다양한 스타일 텍스트 표현
여러 스타일을 결합해 출력할 때는 RichText와 TextSpan을 사용하세요.
RichText(
text: TextSpan(
children: [
TextSpan(
text: "전문가 소개: ",
style: TextStyle(fontSize: 18, color: Colors.blue),
),
TextSpan(
text: "10년 이상 경력, 전문성 높은 진료 제공",
style: TextStyle(fontSize: 16, color: Colors.black),
),
],
),
)
8. 클립 및 그림자 충돌 문제
ClipRRect를 사용할 경우, BoxShadow가 제대로 작동하지 않을 수 있습니다. 이는 레이아웃 순서나 컨테이너의 배경 설정에 영향을 받을 수 있으므로, Container 내부의 decoration과 clipBehavior를 주의 깊게 설정하세요.
9. Flexible vs Expanded 차이
Expanded은 기본적으로 fit: FlexFit.tight이며, 가능한 한 공간을 채웁니다. 최대 높이 제한을 원한다면 Flexible로 변경하고 fit: FlexFit.loose를 지정하세요.
Flexible(
fit: FlexFit.loose,
flex: 1,
child: Container(
height: 100, // 최대 높이 제약
color: Colors.grey[200],
),
)
10. 데이터 전송 시 형식 변환 주의
Flutter에서 List 또는 Map을 서버로 전송할 때는 직렬화가 필요합니다.
List→toString()또는jsonEncode()Map→json.encode(mapData)
final data = {
'images': json.encode(signImages), // Map을 문자열로 변환
};
11. Navigation UI 제어
상태바와 하단 바를 숨기거나 보이게 할 수 있습니다.
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [SystemUiOverlay.bottom], // 상태바만 숨기고 하단 바 유지
);
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(statusBarColor: Colors.transparent),
);
12. 복잡한 비동기 처리: compute 사용
메인 스레드에서 무거운 작업을 수행하면 레이아웃이 멈춥니다. compute()를 통해 새로운 isolate를 생성하여 처리하세요.
final result = await compute(longRunningTask, input);