No JSX
JSX เป็นหนึ่งในฟีเจอร์ที่โดดเด่นที่สุดของ React — คุณเขียน HTML-like tags ที่ Babel แปลงเป็น React.createElement() calls Flutter ไม่มี JSX แต่คุณ compose widget โดยการ nest Dart constructor calls ด้วย named parameters เมื่อคุณเห็น mapping แบบ one-to-one ความต่างของ syntax ก็กลายเป็นเรื่องเล็กน้อย
Tags → constructor calls
หัวข้อที่มีชื่อว่า “Tags → constructor calls”// React — JSX listfunction FruitList({ fruits }) { return ( <ul style={{ padding: 0 }}> {fruits.map((fruit, i) => ( <li key={i} style={{ listStyle: 'none', padding: '8px 0' }}> {fruit} </li> ))} </ul> );}
// Conditional renderfunction Badge({ isNew }) { return ( <div> <span>Item</span> {isNew && <span style={{ color: 'green' }}>NEW</span>} </div> );}// Flutter — constructor calls + listclass FruitList extends StatelessWidget { final List<String> fruits; const FruitList({super.key, required this.fruits});
@override Widget build(BuildContext context) { return Column( children: fruits .map((fruit) => Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Text(fruit), )) .toList(), ); }}
// Conditional widget — collection-if in children listclass Badge extends StatelessWidget { final bool isNew; const Badge({super.key, required this.isNew});
@override Widget build(BuildContext context) { return Row( children: [ const Text('Item'), if (isNew) const Text('NEW', style: TextStyle(color: Colors.green)), ], ); }}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('Fruit List')),
body: const Padding(
padding: EdgeInsets.all(16),
child: FruitList(
fruits: ['Apple', 'Banana', 'Cherry', 'Dragonfruit', 'Elderberry'],
),
),
),
);
}
}
class FruitList extends StatelessWidget {
final List<String> fruits;
const FruitList({super.key, required this.fruits});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: fruits
.mapIndexed((i, fruit) => FruitCard(name: fruit, isNew: i == 0))
.toList(),
);
}
}
class FruitCard extends StatelessWidget {
final String name;
final bool isNew;
const FruitCard({super.key, required this.name, this.isNew = false});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 8),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.06),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
),
child: Row(
children: [
Text(name, style: const TextStyle(fontSize: 16)),
const Spacer(),
if (isNew)
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: Colors.green[100],
borderRadius: BorderRadius.circular(4),
),
child: const Text('NEW',
style: TextStyle(color: Colors.green, fontSize: 12, fontWeight: FontWeight.bold)),
),
],
),
);
}
}
extension IndexedIterable<E> on Iterable<E> {
Iterable<T> mapIndexed<T>(T Function(int i, E e) f) {
var i = 0;
return map((e) => f(i++, e));
}
}