Chuyển đến nội dung chính

Bài 8: Forms, Validation & Theming

Form widget, TextFormField, validation. flutter_form_builder, custom form fields. ThemeData, ColorScheme, TextTheme. Dark mode, dynamic theming. Custom fonts, Material 3.

💻 Lập trình — Bài 8 Bài 8: Forms, Validation & Theming

Flutter & Dart: Từ Cơ bản đến Nâng cao

Phần 2: Navigation & State Management

xdev.asia

1. Form & Validation

class RegisterForm extends StatefulWidget {
  const RegisterForm({super.key});

  @override
  State<RegisterForm> createState() => _RegisterFormState();
}

class _RegisterFormState extends State<RegisterForm> {
  final _formKey = GlobalKey<FormState>();
  final _emailController = TextEditingController();
  final _passwordController = TextEditingController();

  @override
  void dispose() {
    _emailController.dispose();
    _passwordController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          TextFormField(
            controller: _emailController,
            decoration: const InputDecoration(labelText: 'Email'),
            keyboardType: TextInputType.emailAddress,
            validator: (value) {
              if (value == null || value.isEmpty) return 'Email là bắt buộc';
              final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
              if (!emailRegex.hasMatch(value)) return 'Email không hợp lệ';
              return null;
            },
          ),
          TextFormField(
            controller: _passwordController,
            obscureText: true,
            decoration: const InputDecoration(labelText: 'Mật khẩu'),
            validator: (value) {
              if (value == null || value.length < 8) return 'Tối thiểu 8 ký tự';
              return null;
            },
          ),
          ElevatedButton(
            onPressed: () {
              if (_formKey.currentState!.validate()) {
                // Submit
              }
            },
            child: const Text('Đăng ký'),
          ),
        ],
      ),
    );
  }
}

2. Flutter Form Builder

FormBuilder(
  key: _formKey,
  child: Column(
    children: [
      FormBuilderTextField(
        name: 'email',
        decoration: const InputDecoration(labelText: 'Email'),
        validator: FormBuilderValidators.compose([
          FormBuilderValidators.required(),
          FormBuilderValidators.email(),
        ]),
      ),
      FormBuilderDropdown(
        name: 'role',
        decoration: const InputDecoration(labelText: 'Vai trò'),
        items: ['Admin', 'User', 'Editor']
            .map((role) => DropdownMenuItem(value: role, child: Text(role)))
            .toList(),
      ),
      FormBuilderDateTimePicker(
        name: 'dob',
        decoration: const InputDecoration(labelText: 'Ngày sinh'),
        inputType: InputType.date,
      ),
    ],
  ),
);

3. ThemeData & Material 3

MaterialApp(
  theme: ThemeData(
    useMaterial3: true,
    colorSchemeSeed: Colors.indigo,
    brightness: Brightness.light,
    textTheme: GoogleFonts.interTextTheme(),
    inputDecorationTheme: InputDecorationTheme(
      border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
      filled: true,
    ),
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ElevatedButton.styleFrom(
        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      ),
    ),
  ),
  darkTheme: ThemeData(
    useMaterial3: true,
    colorSchemeSeed: Colors.indigo,
    brightness: Brightness.dark,
  ),
  themeMode: ThemeMode.system,
);

4. Dynamic Theming

// Dùng Riverpod quản lý theme
final themeModeProvider = StateProvider<ThemeMode>((ref) => ThemeMode.system);

class MyApp extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final themeMode = ref.watch(themeModeProvider);
    return MaterialApp(
      themeMode: themeMode,
      theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo),
      darkTheme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo, brightness: Brightness.dark),
      home: const HomePage(),
    );
  }
}

// Toggle trong Settings
Switch(
  value: ref.watch(themeModeProvider) == ThemeMode.dark,
  onChanged: (isDark) {
    ref.read(themeModeProvider.notifier).state = isDark ? ThemeMode.dark : ThemeMode.light;
  },
);

Bài tiếp theo: HTTP & REST API Integration.