Props & Constructors
ใน React คุณส่งข้อมูลให้ component ผ่าน props ที่เป็น object ธรรมดาที่ destructure ใน signature ของฟังก์ชัน ส่วนใน Flutter คุณส่งข้อมูลให้ widget ผ่าน constructor parameter ที่ประกาศเป็น field แบบ final บน class ทั้งสองโมเดลแก้ปัญหาเดียวกันเป๊ะ คือทำให้ widget ปรับแต่งได้จากภายนอกโดยไม่ผูกติดกับแหล่งข้อมูลใดแหล่งข้อมูลหนึ่ง
Flutter ใช้ named parameter ของ Dart อย่างหนัก named parameter เป็นแบบ opt-in คือเมื่อห่อด้วย {} จะกลายเป็น keyword argument ตอนเรียกใช้ ถ้าทำเครื่องหมายตัวใดเป็น required Dart จะบังคับใช้ตอน compile ไม่ต้องพึ่ง PropTypes หรือ Required<> ของ TypeScript อีกต่อไป
เปรียบเทียบแบบเคียงข้างกัน
หัวข้อที่มีชื่อว่า “เปรียบเทียบแบบเคียงข้างกัน”// React — TypeScript propsinterface BadgeProps { label: string; color?: string; // optional, has default count: number; // required}
function Badge({ label, color = 'blue', count }: BadgeProps) { return ( <span style={{ background: color }}> {label} ({count}) </span> );}
// Usage<Badge label="Notifications" count={5} /><Badge label="Alerts" color="red" count={2} />// Flutter — constructor paramsclass Badge extends StatelessWidget { final String label; final Color color; // optional with default final int count; // required
const Badge({ super.key, required this.label, this.color = Colors.blue, // default value required this.count, });
@override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(12), ), child: Text( '$label ($count)', style: const TextStyle(color: Colors.white), ), ); }}
// UsageBadge(label: 'Notifications', count: 5)Badge(label: 'Alerts', color: Colors.red, count: 2)ตัวอย่างที่รันได้
หัวข้อที่มีชื่อว่า “ตัวอย่างที่รันได้”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: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
StatusBadge(label: 'Active', status: BadgeStatus.success),
SizedBox(height: 12),
StatusBadge(label: 'Pending', status: BadgeStatus.warning),
SizedBox(height: 12),
StatusBadge(label: 'Failed', status: BadgeStatus.error),
],
),
),
),
);
}
}
enum BadgeStatus { success, warning, error }
class StatusBadge extends StatelessWidget {
final String label;
final BadgeStatus status;
final double fontSize;
const StatusBadge({
super.key,
required this.label,
required this.status,
this.fontSize = 14,
});
Color get _color {
switch (status) {
case BadgeStatus.success: return Colors.green;
case BadgeStatus.warning: return Colors.orange;
case BadgeStatus.error: return Colors.red;
}
}
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: _color.withOpacity(0.15),
border: Border.all(color: _color),
borderRadius: BorderRadius.circular(20),
),
child: Text(
label,
style: TextStyle(color: _color, fontSize: fontSize,
fontWeight: FontWeight.w600),
),
);
}
}