Flutter Layouts
Layout Widgets ក្នុង Flutter
Layout widgets ប្រើសម្រាប់រៀបចំ widgets ផ្សេងទៀត។ ពួកវាកំណត់ទីតាំង និង ទំហំ របស់ children widgets។
📚 Layout Theory
Flutter Layout Algorithm (3 Steps):
1. Constraints go DOWN
Parent → Child: "You must be THIS size"
2. Sizes go UP
Child → Parent: "I chose THIS size"
3. Parent sets position
Parent positions child based on alignment
Box Constraints:
Flutter ប្រើ Box Constraints ដើម្បីកំណត់ទំហំ widget:
BoxConstraints(
minWidth: 0, // ទំហំតូចបំផុត
maxWidth: 100, // ទំហំធំបំផុត
minHeight: 0,
maxHeight: 200,
)
// Types of constraints:
// 1. Tight: minWidth == maxWidth (fixed size)
// 2. Loose: minWidth < maxWidth (flexible size)
// 3. Unbounded: maxWidth = infinity
Layout Rules:
- Rule 1: Widget មិនអាចធំជាង parent constraints
- Rule 2: Widget អាចតូចជាង parent constraints (ប្រសិនបើ parent អនុញ្ញាត)
- Rule 3: Widget ត្រូវដឹងទីតាំងរបស់វា (x, y)
- Rule 4: Parent កំណត់ទីតាំង child មិនមែន child កំណត់ខ្លួនឯងទេ
⚠️ សំខាន់: "Constraints go down. Sizes go up. Parent sets position." - នេះគឺជា golden rule នៃ Flutter layout!
📐 Main Layout Widgets
1. Container
Container គឺជា widget ពេញនិយម និងអាចប្រើបានច្រើន។
Container(
width: 200,
height: 100,
padding: EdgeInsets.all(16),
margin: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(12),
gradient: LinearGradient(
colors: [Colors.blue, Colors.purple],
),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
blurRadius: 10,
offset: Offset(0, 5),
),
],
border: Border.all(color: Colors.white, width: 2),
),
child: Text(
'Container',
style: TextStyle(color: Colors.white),
),
)
2. Row - តម្រៀបផ្ដេក (Horizontal)
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, // ចន្លោះស្មើគ្នា
crossAxisAlignment: CrossAxisAlignment.center, // កណ្តាលបញ្ឈរ
children: [
Icon(Icons.star, color: Colors.yellow),
Icon(Icons.star, color: Colors.yellow),
Icon(Icons.star, color: Colors.yellow),
Icon(Icons.star_border),
Icon(Icons.star_border),
],
)
// MainAxisAlignment values:
// - start: ខាងឆ្វេង
// - end: ខាងស្តាំ
// - center: កណ្តាល
// - spaceBetween: ចន្លោះរវាង (ធំបំផុត)
// - spaceAround: ចន្លោះជុំវិញ (កណ្តាល)
// - spaceEvenly: ចន្លោះស្មើគ្នា
3. Column - តម្រៀបបញ្ឈរ (Vertical)
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start, // ខាងឆ្វេង
children: [
Text('ឈ្មោះ៖ សុខា'),
SizedBox(height: 8),
Text('អាយុ៖ 20 ឆ្នាំ'),
SizedBox(height: 8),
Text('ទីក្រុង៖ ភ្នំពេញ'),
],
)
// CrossAxisAlignment values:
// - start: ខាងឆ្វេង/លើ
// - end: ខាងស្តាំ/ក្រោម
// - center: កណ្តាល
// - stretch: រាតត្រដាង
// - baseline: តាមបន្ទាត់មូលដ្ឋាន
4. Stack - ដាក់ជាន់គ្នា
Stack(
children: [
// Background image
Container(
width: 300,
height: 200,
color: Colors.blue,
),
// Overlay text
Positioned(
bottom: 20,
left: 20,
child: Text(
'រូបភាព',
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
),
// Top right badge
Positioned(
top: 10,
right: 10,
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Text(
'5',
style: TextStyle(color: Colors.white),
),
),
),
],
)
5. Expanded & Flexible
Row(
children: [
// Fixed width
Container(
width: 100,
height: 50,
color: Colors.red,
),
// Takes remaining space
Expanded(
child: Container(
height: 50,
color: Colors.blue,
),
),
// Fixed width
Container(
width: 80,
height: 50,
color: Colors.green,
),
],
)
// Expanded with flex ratio
Row(
children: [
Expanded(
flex: 1, // 25% (1/4)
child: Container(color: Colors.red, height: 50),
),
Expanded(
flex: 2, // 50% (2/4)
child: Container(color: Colors.blue, height: 50),
),
Expanded(
flex: 1, // 25% (1/4)
child: Container(color: Colors.green, height: 50),
),
],
)
6. Wrap - តម្រៀបដោយបត់បាន
Wrap(
spacing: 8, // ចន្លោះផ្ដេក
runSpacing: 8, // ចន្លោះបញ្ឈរ
children: [
Chip(label: Text('Flutter')),
Chip(label: Text('Dart')),
Chip(label: Text('Mobile')),
Chip(label: Text('iOS')),
Chip(label: Text('Android')),
Chip(label: Text('Web')),
Chip(label: Text('Desktop')),
],
)
📏 Sizing & Spacing
SizedBox
// Fixed size
SizedBox(
width: 200,
height: 100,
child: ElevatedButton(
onPressed: () {},
child: Text('Button'),
),
)
// Spacing
Column(
children: [
Text('Text 1'),
SizedBox(height: 20), // ចន្លោះ 20px
Text('Text 2'),
],
)
Padding
Padding(
padding: EdgeInsets.all(16), // ទាំងអស់ 16px
child: Text('Padded text'),
)
// Different padding
Padding(
padding: EdgeInsets.only(
left: 20,
top: 10,
right: 20,
bottom: 10,
),
child: Text('Text'),
)
// Symmetric padding
Padding(
padding: EdgeInsets.symmetric(
horizontal: 20, // ឆ្វេង-ស្តាំ
vertical: 10, // លើ-ក្រោម
),
child: Text('Text'),
)
Center & Align
// Center
Center(
child: Text('កណ្តាល'),
)
// Align
Align(
alignment: Alignment.topRight,
child: Icon(Icons.close),
)
// Alignment values:
// topLeft, topCenter, topRight
// centerLeft, center, centerRight
// bottomLeft, bottomCenter, bottomRight
🎨 Real Layout Example
class ProfilePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ប្រវត្តិរូប'),
),
body: SingleChildScrollView(
child: Column(
children: [
// Header with background
Stack(
children: [
Container(
height: 200,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue, Colors.purple],
),
),
),
Positioned(
bottom: 20,
left: 0,
right: 0,
child: Center(
child: CircleAvatar(
radius: 50,
backgroundImage: NetworkImage('https://example.com/avatar.jpg'),
),
),
),
],
),
SizedBox(height: 20),
// Name and bio
Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: [
Text(
'សុខា',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
Text(
'Flutter Developer',
style: TextStyle(
color: Colors.grey,
fontSize: 16,
),
),
],
),
),
SizedBox(height: 20),
// Stats row
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildStat('150', 'Posts'),
_buildStat('1.2K', 'Followers'),
_buildStat('300', 'Following'),
],
),
SizedBox(height: 20),
// Action buttons
Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {},
child: Text('Follow'),
),
),
SizedBox(width: 10),
Expanded(
child: OutlinedButton(
onPressed: () {},
child: Text('Message'),
),
),
],
),
),
SizedBox(height: 20),
// Posts grid
GridView.count(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
crossAxisCount: 3,
crossAxisSpacing: 4,
mainAxisSpacing: 4,
children: List.generate(9, (index) {
return Container(
color: Colors.grey[300],
child: Center(
child: Icon(Icons.image, size: 50),
),
);
}),
),
],
),
),
);
}
Widget _buildStat(String count, String label) {
return Column(
children: [
Text(
count,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4),
Text(
label,
style: TextStyle(color: Colors.grey),
),
],
);
}
}
💡 ជំនួយ: ប្រើ Flutter DevTools Widget Inspector ដើម្បីមើល និង debug layout issues។ វាបង្ហាញ widget tree និង constraints របស់ widget នីមួយៗ។