ข้ามไปยังเนื้อหา

Layout: Row & Column

โมเดล layout ของ Flutter ก็คือ flexbox เพียงแต่สะกดด้วยคำต่างกันเท่านั้น Row คือ display: flex; flex-direction: row Column คือ display: flex; flex-direction: column Expanded คือ flex: 1 MainAxisAlignment แมปไปยัง justify-content ส่วน CrossAxisAlignment แมปไปยัง align-items

พอเห็นการแมปนี้แล้ว layout ของ Flutter จะคุ้นเคยขึ้นมาทันที จุดที่ต้องระวังคือ main axis จะสลับ กันระหว่าง Row กับ Column ส่วนใน CSS นั้น justify-content หมายถึง axis ในทิศที่คุณตั้งไว้เสมอ

React
/* React / CSS flexbox */
/* Row layout */
.row {
display: flex;
flex-direction: row;
justify-content: space-between; /* main axis */
align-items: center; /* cross axis */
gap: 8px;
}
/* Column layout */
.col {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: 12px;
}
/* Flex child that grows */
.grow { flex: 1; }
// JSX
function NavBar() {
return (
<div className="row">
<span className="grow">My App</span>
<button>Login</button>
<button>Sign Up</button>
</div>
);
}
Flutter
// Flutter Row / Column
// Row layout
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
spacing: 8, // Flutter 3.27+ gap shorthand
children: [/* ... */],
)
// Column layout
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 12,
children: [/* ... */],
)
// Flex child that grows (flex: 1)
Expanded(child: someWidget)
// Widget
class NavBar extends StatelessWidget {
const NavBar({super.key});
@override
Widget build(BuildContext context) {
return Row(
children: [
const Expanded(child: Text('My App',
style: TextStyle(fontWeight: FontWeight.bold))),
TextButton(onPressed: () {}, child: const Text('Login')),
ElevatedButton(onPressed: () {}, child: const Text('Sign Up')),
],
);
}
}
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(
      home: Scaffold(
        appBar: AppBar(title: const Text('Row & Column Demo')),
        body: const Padding(
          padding: EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            spacing: 16,
            children: [
              _SectionLabel('Row — spaceAround'),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  _Chip(label: 'Flutter', color: Colors.blue),
                  _Chip(label: 'Dart', color: Colors.teal),
                  _Chip(label: 'Material', color: Colors.purple),
                ],
              ),
              _SectionLabel('Row with Expanded'),
              Row(
                spacing: 8,
                children: [
                  Expanded(child: _Chip(label: 'Grows to fill', color: Colors.orange)),
                  _Chip(label: 'Fixed', color: Colors.red),
                ],
              ),
              _SectionLabel('Column — center + start'),
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                spacing: 6,
                children: [
                  _Chip(label: 'Item 1', color: Colors.indigo),
                  _Chip(label: 'Item 2', color: Colors.indigo),
                  _Chip(label: 'Item 3', color: Colors.indigo),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _SectionLabel extends StatelessWidget {
  final String text;
  const _SectionLabel(this.text);

  @override
  Widget build(BuildContext context) => Text(text,
      style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13,
          color: Colors.grey));
}

class _Chip extends StatelessWidget {
  final String label;
  final Color color;
  const _Chip({required this.label, required this.color});

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
      decoration: BoxDecoration(color: color.withOpacity(0.15),
          border: Border.all(color: color),
          borderRadius: BorderRadius.circular(20)),
      child: Text(label, style: TextStyle(color: color, fontWeight: FontWeight.w600)),
    );
  }
}
อะไรคือสิ่งที่เทียบเท่ากับ `justify-content` ของ CSS flexbox ใน Flutter?
สำหรับ `Column` นั้น `mainAxisAlignment` ควบคุมการจัดวางตาม axis ไหน?
Flutter widget ตัวไหนที่เทียบเท่ากับ `flex: 1` บน flex child?
อะไรคือวิธีที่ง่ายที่สุดใน Flutter ที่เทียบเท่ากับ `gap: 8px` ของ CSS ระหว่าง child ของ Row (Flutter 3.27+)?