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

Mental Model Quickstart

สี่แนวคิดนี้ unlock Flutter สำหรับ React developer เมื่อเข้าใจแล้ว ส่วนที่เหลือของ framework คือการเรียน widget API เท่านั้น — mental model อยู่ในมือคุณแล้ว

ใน React UI ของคุณมี components (code ของคุณ) และ HTML elements (<div>, <p>, <button>) สองชั้นที่แตกต่างกัน

ใน Flutter มี ชั้นเดียวคือ widget Layout, spacing, color, text, images, gesture detection, แม้แต่ตัว app เอง — ล้วนเป็น widget ไม่มี HTML ข้างใต้ Flutter วาดทุก pixel โดยตรง

flowchart TB
  subgraph React
    direction TB
    RApp["&lt;App&gt;"] --> RDiv["&lt;div className='card'&gt;"]
    RDiv --> RH2["&lt;h2&gt;Hello&lt;/h2&gt;"]
    RDiv --> RBtn["&lt;button&gt;Click&lt;/button&gt;"]
  end
  subgraph Flutter
    direction TB
    FApp["MyApp (StatelessWidget)"] --> FMaterial["MaterialApp"]
    FMaterial --> FScaffold["Scaffold"]
    FScaffold --> FCenter["Center"]
    FCenter --> FColumn["Column"]
    FColumn --> FText["Text('Hello')"]
    FColumn --> FBtn["ElevatedButton(...)"]
  end
React element tree vs Flutter widget tree

ความลึกดูมากกว่าตอนแรก แต่ consistent — ไม่มีสอง mental model ที่ต้องสลับกัน

Component tree ของ React และ widget tree ของ Flutter คือแนวคิดเดียวกัน ทั้งคู่เป็น description ของสิ่งที่ควร render ทั้งคู่ถูก re-evaluate เมื่อ state เปลี่ยน Framework diff description กับ snapshot ก่อนหน้าและอัปเดตเฉพาะที่เปลี่ยน

React
// React component tree
function Card({ title, body }) {
return (
<div className="card">
<h3>{title}</h3>
<p>{body}</p>
</div>
);
}
function App() {
return (
<main>
<Card title="Flutter" body="Everything is a widget" />
<Card title="React" body="Everything is a component" />
</main>
);
}
Flutter
// Flutter widget tree
class InfoCard extends StatelessWidget {
final String title;
final String body;
const InfoCard({super.key, required this.title, required this.body});
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Text(body),
],
),
),
);
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
children: const [
InfoCard(title: 'Flutter', body: 'Everything is a widget'),
InfoCard(title: 'React', body: 'Everything is a component'),
],
),
),
);
}
}

Save file → app ที่รันอยู่ update ภายในไม่ถึงวินาทีโดยไม่ต้อง reset state นี่คือ Hot Reload ของ Flutter และทำงานเหมือน Fast Refresh ของ React ทุกประการ:

  • Edit build() methods ถูก inject ทันที
  • Widget state ถูก preserve (counter ยังคงจำตัวเลขอยู่)
  • การเปลี่ยนแปลงใน initState(), class fields, หรือ main() ต้องใช้ Hot Restart (R) — เหมือน changes ใน module-level code ของ React ที่ต้อง refresh เอง

Flutter เป็น declarative คุณ describe ว่า UI ควรหน้าตาเป็นอย่างไรสำหรับ state ที่ให้มา — ไม่ใช่ imperatively mutate DOM นี่คือ contract เดียวกับที่ React นำมาใช้

// Imperative (jQuery-style thinking — อย่าทำแบบนี้)
// "find the Text widget and update its text to '$_count'"
// Declarative (Flutter way)
// "given _count, build this subtree"
Text('Count: $_count')

เมื่อ setState รัน Flutter เรียก build() อีกครั้งและ Text widget ได้รับค่าใหม่ คุณ describe ผลลัพธ์ Flutter หาว่าต้อง update อะไรบ้าง

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

// 1. ทุกอย่างคือ widget (แม้แต่ app root)
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
        useMaterial3: true,
      ),
      // 2. Widget tree: MyApp > MaterialApp > Scaffold > ...
      home: const WidgetTreeDemo(),
    );
  }
}

// 4. Declarative: build() describe UI สำหรับ state ปัจจุบัน
class WidgetTreeDemo extends StatefulWidget {
  const WidgetTreeDemo({super.key});

  @override
  State<WidgetTreeDemo> createState() => _WidgetTreeDemoState();
}

class _WidgetTreeDemoState extends State<WidgetTreeDemo> {
  int _likes = 0;
  bool _bookmarked = false;

  @override
  Widget build(BuildContext context) {
    final colors = Theme.of(context).colorScheme;
    return Scaffold(
      appBar: AppBar(
        title: const Text('Mental Model Demo'),
        backgroundColor: colors.primaryContainer,
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 3. ลอง edit Text นี้แล้ว hot-reload ดู
            Text(
              'Flutter Mental Model',
              style: Theme.of(context).textTheme.headlineSmall?.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
            ),
            const SizedBox(height: 8),
            const Text(
              'Everything is a widget. The tree is declarative. '
              'Hot Reload keeps your state while updating the UI.',
            ),
            const SizedBox(height: 24),
            Row(
              children: [
                FilledButton.icon(
                  onPressed: () => setState(() => _likes++),
                  icon: const Icon(Icons.favorite),
                  label: Text('$_likes Likes'),
                ),
                const SizedBox(width: 12),
                OutlinedButton.icon(
                  onPressed: () => setState(() => _bookmarked = !_bookmarked),
                  icon: Icon(
                    _bookmarked ? Icons.bookmark : Icons.bookmark_border,
                  ),
                  label: Text(_bookmarked ? 'Saved' : 'Save'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
ใน Flutter มีอะไร "อยู่ใต้" widget ใน rendering stack?
เกิดอะไรขึ้นเมื่อเรียก `setState()` ใน StatefulWidget?
ใน Flutter จะเพิ่ม padding/spacing รอบ widget ได้อย่างไร?
Feature ใดของ Flutter ที่เทียบเท่า Fast Refresh ของ React?