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

Composition over Configuration

Composition model ของ React — component เล็กๆ ที่รับ children, higher-order components ที่ wrap behavior, และ render props — map ได้อย่างลงตัวกับ widget model ของ Flutter ใน Flutter คุณ compose behavior โดยการ wrap widget: Padding(child: ...), GestureDetector(child: ...), Opacity(child: ...) ไม่มี CSS class inheritance หรือ widget subclassing สำหรับการ customize รูปลักษณ์ ทุกอย่างคือ composition

React
// React — children prop
function Card({ children, elevated }) {
return (
<div
style={{
padding: 16,
borderRadius: 12,
boxShadow: elevated ? '0 4px 12px rgba(0,0,0,0.1)' : 'none',
}}
>
{children}
</div>
);
}
// Usage
<Card elevated>
<h3>Hello</h3>
<p>Composed content</p>
</Card>
Flutter
// Flutter — wrapper widget with child
class AppCard extends StatelessWidget {
final Widget child;
final bool elevated;
const AppCard({super.key, required this.child, this.elevated = false});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: elevated
? [BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 12,
offset: const Offset(0, 4))]
: [],
),
child: child,
);
}
}
// Usage
AppCard(
elevated: true,
child: Column(
children: [
Text('Hello', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
Text('Composed content'),
],
),
)
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(
        backgroundColor: Colors.grey[100],
        body: Padding(
          padding: const EdgeInsets.all(24),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              AppCard(
                elevated: true,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: const [
                    Text('Getting Started', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                    SizedBox(height: 4),
                    Text('Learn the basics of Flutter composition.', style: TextStyle(color: Colors.grey)),
                  ],
                ),
              ),
              const SizedBox(height: 16),
              AppCard(
                elevated: true,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: const [
                    Text('Widget Tree', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                    SizedBox(height: 4),
                    Text('Everything is a widget — compose, not configure.', style: TextStyle(color: Colors.grey)),
                  ],
                ),
              ),
              const SizedBox(height: 16),
              AppCard(
                elevated: true,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: const [
                    Text('Declarative UI', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                    SizedBox(height: 4),
                    Text('UI = f(state) — the same idea you know from React.', style: TextStyle(color: Colors.grey)),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class AppCard extends StatelessWidget {
  final Widget child;
  final bool elevated;
  const AppCard({super.key, required this.child, this.elevated = false});

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        boxShadow: elevated
            ? [
                BoxShadow(
                  color: Colors.black.withOpacity(0.1),
                  blurRadius: 12,
                  offset: const Offset(0, 4),
                )
              ]
            : [],
      ),
      child: child,
    );
  }
}
Flutter equivalent ของ React's children prop คืออะไร?
ใน Flutter คุณเพิ่ม tap handler ให้ widget ใดก็ได้อย่างไร?
React render props (ส่ง function ที่ return JSX) map เป็นอะไรใน Flutter?
ทำไม Flutter ถึงชอบ composition มากกว่า inheritance สำหรับ visual customization?