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

Styling & Theme

ความแตกต่างที่พื้นฐานที่สุดระหว่างการทำงาน UI ของ React กับ Flutter คือสิ่งนี้ ไม่มี stylesheet ไม่มีไฟล์ CSS ไม่มี className ไม่มี styled-components ไม่มี utility class ของ Tailwind การทำ styling ใน Flutter ถูกแสดงออกผ่าน property ของ widget constructor ทั้งหมด ทั้ง color, typography, spacing และ decoration ล้วนเป็น Dart object ที่ซ้อนอยู่ภายใน widget tree

สำหรับ design token ระดับทั้งแอป เช่น สีของแบรนด์ scale ของ text รูปทรงของปุ่ม Flutter ใช้ ThemeData ซึ่งคุณตั้งค่าครั้งเดียวที่ MaterialApp(theme: ...) แล้วเรียกใช้จากที่ไหนก็ได้ผ่าน Theme.of(context) นี่คือสิ่งที่เทียบเท่าโดยตรงกับ ThemeProvider ที่ห่อแอป React ของคุณ

React
// React — CSS-in-JS / styled-components style
// 1. Inline styles (like Flutter widget props)
<p style={{ fontSize: 18, color: '#1a1a1a', fontWeight: 700 }}>
Hello
</p>
// 2. styled-components (design token from ThemeProvider)
const Heading = styled.h1`
font-size: ${({ theme }) => theme.fontSizes.xl}px;
color: ${({ theme }) => theme.colors.primary};
font-weight: 700;
`;
// 3. ThemeProvider wrapping the app
<ThemeProvider theme={myTheme}>
<App />
</ThemeProvider>
// 4. Consuming the theme in a component
const { theme } = useTheme();
<div style={{ background: theme.colors.surface }}>...</div>
Flutter
// Flutter — widget properties + ThemeData
// 1. Inline TextStyle (equivalent to inline styles)
Text(
'Hello',
style: TextStyle(
fontSize: 18,
color: Color(0xFF1A1A1A),
fontWeight: FontWeight.bold,
),
)
// 2. BoxDecoration (background, border, radius, shadow)
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey[300]!),
),
child: ...,
)
// 3. ThemeData wrapping the app (in MaterialApp)
MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
textTheme: const TextTheme(
headlineMedium: TextStyle(fontWeight: FontWeight.bold),
),
),
home: const MyHome(),
)
// 4. Consuming the theme anywhere
final theme = Theme.of(context);
Container(color: theme.colorScheme.surface, ...)
import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF0468D7)),
        useMaterial3: true,
        textTheme: const TextTheme(
          headlineSmall: TextStyle(fontWeight: FontWeight.bold, fontSize: 22),
          bodyMedium: TextStyle(fontSize: 15, height: 1.6),
        ),
      ),
      home: const StyleDemo(),
    ),
  );
}

class StyleDemo extends StatelessWidget {
  const StyleDemo({super.key});

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final cs = theme.colorScheme;

    return Scaffold(
      backgroundColor: cs.surfaceContainerLow,
      appBar: AppBar(
        backgroundColor: cs.primary,
        title: Text('Styling & Theme',
            style: TextStyle(color: cs.onPrimary, fontWeight: FontWeight.bold)),
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          spacing: 16,
          children: [
            // TextStyle from theme
            Text('Headline from ThemeData',
                style: theme.textTheme.headlineSmall),
            Text('Body text with theme line-height',
                style: theme.textTheme.bodyMedium),

            // TextStyle override via copyWith
            Text('Local color override',
                style: theme.textTheme.bodyMedium!
                    .copyWith(color: cs.error, fontWeight: FontWeight.w600)),

            // BoxDecoration card
            Container(
              padding: const EdgeInsets.all(20),
              decoration: BoxDecoration(
                color: cs.primaryContainer,
                borderRadius: BorderRadius.circular(16),
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                spacing: 8,
                children: [
                  Text('Primary container card',
                      style: TextStyle(color: cs.onPrimaryContainer,
                          fontWeight: FontWeight.bold, fontSize: 16)),
                  Text('Colours from ColorScheme.fromSeed',
                      style: TextStyle(color: cs.onPrimaryContainer)),
                ],
              ),
            ),

            // BoxDecoration with gradient
            Container(
              height: 80,
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  colors: [cs.primary, cs.tertiary],
                ),
                borderRadius: BorderRadius.circular(12),
              ),
              alignment: Alignment.center,
              child: Text('Gradient via BoxDecoration',
                  style: TextStyle(color: cs.onPrimary,
                      fontWeight: FontWeight.bold)),
            ),

            // Buttons inherit theme automatically
            Row(
              spacing: 8,
              children: [
                ElevatedButton(onPressed: () {}, child: const Text('Elevated')),
                TextButton(onPressed: () {}, child: const Text('Text')),
                OutlinedButton(onPressed: () {}, child: const Text('Outlined')),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
styling ทั้งหมดของ Flutter อยู่ที่ไหน — และส่งถึง widget อย่างไร?
อะไรคือสิ่งที่เทียบเท่ากับ `ThemeProvider` + `useTheme()` ของ styled-components ใน Flutter?
คุณต้องการเก็บ property ของ TextStyle ทั้งหมดจาก theme ไว้แต่เปลี่ยนแค่สี อะไรคือ pattern ที่ถูกต้องใน Flutter?
Flutter class ตัวไหนที่เก็บ background-color, border-radius, border และ box-shadow สำหรับ Container?