openplanning

Hướng dẫn và ví dụ Flutter flutter_form_builder

  1. Cài đặt thư viện
  2. Ví dụ cơ bản
  3. FormBuilderTextField
  4. FormBuilderCheckbox
  5. FormBuilderSwitch
  6. FormBuilderCheckboxGroup
  7. FormBuilderRadioGroup
  8. FormBuilderChoiceChip
  9. FormBuilderFilterChip
  10. FormBuilderDateRangePicker
  11. FormBuilderDateTimePicker
  12. FormBuilderDropdown
  13. FormBuilderSlider
  14. FormBuilderRangeSlider
flutter_form_builder là một thư viện Flutter cho phép bạn xây dựng một biểu mẫu thu thập dữ liệu từ người dùng môt cách ngắn gọn và đơn giản, loại bỏ sự rườm rà. Gói flutter_form_builder được kết hợp với gói form_builder_validators để xác thực (validate) các trường và phản hồi các thay đổi.
Nếu bạn thường xuyên phải sử dụng TextField bạn sẽ cảm thấy mệt mỏi với TextEditingController vì chúng luôn phải kết hợp với nhau. Thật may mắn với thư viện này bạn có thể sử dụng FormBuilderTextField để thay thế mà không cần tới Controller.
Thư viện này cung cấp các trường đầu vào thông dụng, ngoài ra bạn cũng thể tạo ra các trường đầu vào tuỳ biến bằng cách sử dụng hoặc mở rộng lớp cơ sở FormBuilderField.
  • Tuỳ biến flutter_form_builder FormBuiderField
Các trường đầu vào (input field) thông dụng được cung cấp bởi thư viện này:
FormBuilderCheckbox
Một trường Checkbox đơn.
FormBuilderCheckboxGroup
Danh sách các Checkbox cho phép đa lựa chọn.
FormBuilderChoiceChip
Tạo một Chip hoạt động giống như một Radio.
FormBuilderDateRangePicker
Cho phép lựa chọn một phạm vi ngày tháng.
FormBuilderDateTimePicker
Cho phép đầu vào Date, TimeDateTime.
FormBuilderDropdown
Được sử dụng để lựa chọn một giá trị từ một danh sách thả xuống.
FormBuilderFilterChip
Tạo một nhóm các Chip hoạt động giống như một nhóm các Checkbox.
FormBuilderRadioGroup
Được sử dụng để lựa chọn một giá trị từ một danh sách các Radio.
FormBuilderRangeSlider
Được sử dụng để chọn một phạm vi từ một phạm vi giá trị.
FormBuilderSlider
Để chọn một giá trị số trên một Slider (thanh trượt).
FormBuilderSwitch
Một trường bật/tắt (On/Off).
FormBuilderTextField
Một trường văn bản đầu vào hoạt động như một TextField.
Các gói thư viện liên quan:
  • Flutter form_builder_validators
Gói thư viện form_builder_extra_fields cung cấp các trường đầu vào bổ xung như:
FormBuilderColorPicker, FormBuilderRating, FormBuilderSearchableDropdown, FormBuilderSignaturePad, FormBuilderTouchSpin, FormBuilderTypeAhead ,...
  • Flutter form_builder_extra_fields
  • Flutter form_builder_file_picker
  • Flutter form_builder_image_picker

1. Cài đặt thư viện

Gói thư viện flutter_form_builder cung cấp các trường đầu vào thông dụng, trong khi form_builder_validators được sử dụng để xác thực (validate) dữ liệu.
pubspec.yaml
dependencies:
  flutter_form_builder: ^9.3.0
  form_builder_validators: ^11.0.0

2. Ví dụ cơ bản

Chúng ta hãy bắt đầu với một ví dụ cơ bản sử dụng gói flutter_form_builder.
FormBuilder
final _formKey = GlobalKey<FormBuilderState>();

FormBuilder(
    key: _formKey,
    ...
)
Tất cả các trường đầu vào cần được đặt trong một FormBuilder, nó giúp bạn lưu trữ dữ liệu của tất cả các trường, hỗ trợ xác thực dữ liệu ở mức toàn cục và nhận biết sự thay đổi dữ liệu của toàn biểu mẫu (form).
main_ex1.dart
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:form_builder_validators/form_builder_validators.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      home: MyLoginPage(),
    );
  }
}

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

  @override
  State<MyLoginPage> createState() => _MyLoginPageState();
}

class _MyLoginPageState extends State<MyLoginPage> {
  final _formKey = GlobalKey<FormBuilderState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text("Flutter FormBuilder Demo"),
      ),
      body: FormBuilder(
        key: _formKey,
        child: Column(
          children: [
            FormBuilderTextField(
              name: 'email',
              decoration: const InputDecoration(labelText: 'Email'),
              validator: FormBuilderValidators.compose(
                [
                  FormBuilderValidators.required(),
                  FormBuilderValidators.email(),
                ],
              ),
            ),
            const SizedBox(height: 10),
            FormBuilderTextField(
              name: 'password',
              decoration: const InputDecoration(labelText: 'Password'),
              obscureText: true,
              validator: FormBuilderValidators.compose(
                [
                  FormBuilderValidators.required(),
                ],
              ),
            ),
            const SizedBox(height: 10),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: () {
                    bool isValid = _formKey.currentState?.validate(
                          autoScrollWhenFocusOnInvalid: true,
                          focusOnInvalid: true,
                        ) ??
                        false;
                    debugPrint(_formKey.currentState?.instantValue.toString());
                    //
                    if (isValid) {
                      // Submit to Server...
                    }
                  },
                  child: const Text('Login'),
                ),
                const SizedBox(width: 10),
                ElevatedButton(
                  onPressed: () {
                    _formKey.currentState?.reset();
                  },
                  child: const Text('Reset'),
                )
              ],
            )
          ],
        ),
      ),
    );
  }
}
Thông qua "key" của FormBuider bạn có thể có được dữ liệu hiện thời của Form và kiểm tra trạng thái hợp lệ của nó. Tự động nhẩy tới trường dữ liệu không hợp lệ để người dùng nhập lại.
bool isValid = _formKey.currentState?.validate(
      autoScrollWhenFocusOnInvalid: true,
      focusOnInvalid: true,
    ) ??
    false;
debugPrint(_formKey.currentState?.instantValue.toString());
//
if (isValid) {
  // Submit to Server...
}
Output:
{email: tom@example.com, password: 123}
Bạn cũng có thể lưu trữ các giá trị của biểu mẫu như một trạng thái ban đầu mới, các giá trị này sẽ được khôi phục nếu người dùng nhấn vào nút "Reset" (Thiết lập lại dữ liệu).
// _formKey.currentState?.save(..)

// Save state of data and validate data.
bool isValid = _formKey.currentState?.saveAndValidate(
      autoScrollWhenFocusOnInvalid: true,
      focusOnInvalid: true,
    ) ??
    false;

3. FormBuilderTextField

TextField là trường đầu vào thông dụng nhất để thu thập dữ liệu từ người dùng, nó cần được sử dụng cùng với TextEditingController khiến việc viết mã trở lên dài dòng. FormBuilderTextField là một sự thay thế hoàn hảo mà không cần tới Controller, nó chắc chắn là widget giá trị nhất của thư viện này.
main_textfield_ex1.dart (*)
FormBuilderTextField(
  autovalidateMode: AutovalidateMode.always,
  name: 'age',
  decoration: InputDecoration(
    labelText: 'Age',
    suffixIcon: _ageHasError
        ? const Icon(Icons.error, color: Colors.red)
        : const Icon(Icons.check, color: Colors.green),
  ),
  onChanged: (val) {
    setState(() {
      _ageHasError =
          !(_formKey.currentState?.fields['age']?.validate() ??
              false);
    });
  },
  // valueTransformer: (text) => num.tryParse(text),
  validator: FormBuilderValidators.compose(
    [
      FormBuilderValidators.required(),
      FormBuilderValidators.numeric(),
      FormBuilderValidators.max(70),
    ],
  ),
  // initialValue: '12',
  keyboardType: TextInputType.number,
  textInputAction: TextInputAction.next,
),

4. FormBuilderCheckbox

FormBuiderCheckbox là một widget được xây dựng trên CheckboxListTile. Theo mặc định nó hỗ trợ hai trạng thái "checked" & "unchecked".
  • Hướng dẫn và ví dụ Flutter CheckboxListTile
main_checkbox_ex1.dart (*)
FormBuilderCheckbox(
  name: 'accept_terms',
  initialValue: false,
  onChanged: (bool? value) {
    // Code..
  },
  title: RichText(
    text: TextSpan(
      children: [
        const TextSpan(
          text: 'I have read and agree to the ',
          style: TextStyle(color: Colors.black),
        ),
        WidgetSpan(
          alignment: PlaceholderAlignment.middle,
          child: InkWell(
            child: const Text(
              "Terms and Conditions",
              style: TextStyle(color: Colors.blue),
            ),
            onTap: () {
              // Open Link
            },
          ),
        ),
      ],
    ),
  ),
  validator: FormBuilderValidators.equal(
    true,
    errorText: 'You must accept terms and conditions to continue',
  ),
),
Ví dụ một FormBuiderCheckbox hỗ trợ 3 trạng thái:
FormBuilderCheckbox(
  name: 'checkboxName',
  initialValue: false,
  tristate: true,
  onChanged: (bool? value) {
    // Code..
  },
  title: const Text("Title"),
  subtitle: const Text("Subtitle"),
  secondary: const Icon(Icons.image),
),

5. FormBuilderSwitch

FormBuilderSwitch cho phép người dùng "On/Off" (Bật/tắt) một thiết lập đơn. Nó được xây dựng dựa trên SwitchListTile sẵn có trong Flutter.
main_switch_ex1.dart (*)
FormBuilderSwitch(
  title: const Text('I Accept the terms and conditions'),
  name: 'accept_terms_switch',
  initialValue: false,
  onChanged: (bool? value) {
    print("Value: $value");
  },
  validator: FormBuilderValidators.equal(
    true,
    errorText: 'You must accept terms and conditions to continue',
  ),
),
  • Hướng dẫn và ví dụ Flutter Switch
  • Hướng dẫn và ví dụ Flutter SwitchListTile

6. FormBuilderCheckboxGroup

FormBuilderCheckboxGroup được sử dụng để tạo ra một danh sách các Checkbox cho phép người dùng chọn nhiều tuỳ chọn. Các tuỳ chọn có thể được xắp xếp trên một hàng, một cột hoặc có thể cuộn lại (wrap).
main_checkboxgroup_ex1.dart (*)
FormBuilderCheckboxGroup<String>(
  autovalidateMode: AutovalidateMode.onUserInteraction,
  decoration: const InputDecoration(
    labelText: 'Select Languages:',
  ),
  name: 'languages',
  initialValue: const ['Dart'],
  options: const [
    FormBuilderFieldOption(value: 'Dart'),
    FormBuilderFieldOption(value: 'Kotlin'),
    FormBuilderFieldOption(value: 'Java'),
    FormBuilderFieldOption(value: 'Swift'),
    FormBuilderFieldOption(value: 'Objective-C'),
  ],
  onChanged: (List<String>? langs) {
    // Code..
  },
  separator: const SizedBox(width: 8.0),
  validator: FormBuilderValidators.compose(
    [
      FormBuilderValidators.minLength(1),
      FormBuilderValidators.maxLength(3),
    ],
  ),
  orientation: OptionsOrientation.wrap,
),
FormBuilderCheckboxGroup<String>(
  autovalidateMode: AutovalidateMode.onUserInteraction,
  decoration: const InputDecoration(
    labelText: 'Select Languages:',
  ),
  name: 'languages',
  initialValue: const ['Dart'],
  options: const [
    FormBuilderFieldOption(value: 'Dart'),
    FormBuilderFieldOption(value: 'Kotlin'),
    FormBuilderFieldOption(value: 'Java'),
    FormBuilderFieldOption(value: 'Swift'),
    FormBuilderFieldOption(value: 'Javascript'),
    FormBuilderFieldOption(value: 'Delphi'),
    FormBuilderFieldOption(value: 'Pascal'),
    FormBuilderFieldOption(value: 'C/C++'),
    FormBuilderFieldOption(value: 'Golang'),
    FormBuilderFieldOption(value: 'Visual Basic'),
  ],
  orientation: OptionsOrientation.wrap,
  separator: const SizedBox(
    height: 20,
    child: VerticalDivider(),
  ),
  wrapSpacing: 10,
  wrapRunSpacing: 20,
  wrapRunAlignment: WrapAlignment.end,
  validator: FormBuilderValidators.compose(
    [
      FormBuilderValidators.minLength(1),
      FormBuilderValidators.maxLength(3),
    ],
  ),
  onChanged: (List<String>? langs) {
    // Code..
  },
),

7. FormBuilderRadioGroup

FormBuilderRadioGroup được sử dụng để tạo ra một danh sách các Radio cho phép người dùng chọn nhiều nhất một tuỳ chọn trong danh sách các tuỳ chọn. Các tuỳ chọn có thể được xắp xếp trên một hàng, một cột hoặc có thể cuộn lại (wrap).
main_radiogroup_ex1.dart (*)
FormBuilderRadioGroup<String>(
  decoration: const InputDecoration(
    labelText: 'My chosen language',
  ),
  initialValue: null,
  name: 'best_language',
  onChanged: (String? value) {
    print("Value: $value");
  },
  validator: FormBuilderValidators.compose(
    [
      FormBuilderValidators.required(),
    ],
  ),
  options: ['Dart', 'Kotlin', 'Java', 'Swift', 'Objective-C']
      .map(
        (lang) => FormBuilderFieldOption(
          value: lang,
          child: Text(lang),
        ),
      )
      .toList(growable: false),
  controlAffinity: ControlAffinity.trailing,
)

8. FormBuilderChoiceChip

FormBuilderChoiceChip được sử dụng để tạo ra một danh sách các Chip cho phép người dùng chọn nhiều nhất một tuỳ chọn trong danh sách các tuỳ chọn. Các tuỳ chọn có thể được xắp xếp trên một hàng, một cột hoặc có thể cuộn lại (wrap).
main_choicechip_ex1.dart (*)
FormBuilderChoiceChip<String>(
  autovalidateMode: AutovalidateMode.onUserInteraction,
  spacing: 5,
  labelPadding: const EdgeInsets.all(0),
  visualDensity: const VisualDensity(
    vertical: -3,
    horizontal: -3,
  ),
  decoration: const InputDecoration(
    labelText: 'Select Languages:',
    isDense: true,
    contentPadding: EdgeInsets.all(10),
  ),
  name: 'languages_choice',
  initialValue: 'Dart',
  options: const [
    FormBuilderChipOption(
      value: 'Dart',
      avatar: CircleAvatar(),
    ),
    FormBuilderChipOption(
      value: 'Kotlin',
      avatar: CircleAvatar(),
    ),
    FormBuilderChipOption(
      value: 'Java',
      avatar: CircleAvatar(),
    ),
    FormBuilderChipOption(
      value: 'Swift',
      avatar: CircleAvatar(),
    ),
    FormBuilderChipOption(
      value: 'Objective-C',
      avatar: CircleAvatar(),
    ),
  ],
  onChanged: (String? value) {
    print("Value: $value");
  },
),

9. FormBuilderFilterChip

FormBuilderFilterChip được sử dụng để tạo ra một danh sách các Chip cho phép người dùng chọn nhiều tuỳ chọn trong danh sách các tuỳ chọn. Các tuỳ chọn có thể được xắp xếp trên một hàng, một cột hoặc có thể cuộn lại (wrap).
Về cơ bản, FormBuilderFilterChip có chức năng giống với FormBuilderCheckboxGroup, nó thường được sử dụng để người dùng lựa chọn các tiêu chí tìm kiếm và lọc dữ liệu.
main_filterchip_ex1.dart (*)
FormBuilderFilterChip<String>(
  autovalidateMode: AutovalidateMode.onUserInteraction,
  spacing: 10,
  decoration: const InputDecoration(
    labelText: 'Select Languages',
  ),
  name: 'languages_filter',
  selectedColor: Colors.blue.withAlpha(80),
  options: const [
    FormBuilderChipOption(
      value: 'Dart',
      avatar: CircleAvatar(),
    ),
    FormBuilderChipOption(
      value: 'Kotlin',
      avatar: CircleAvatar(),
    ),
    FormBuilderChipOption(
      value: 'Java',
      avatar: CircleAvatar(),
    ),
    FormBuilderChipOption(
      value: 'Swift',
      avatar: CircleAvatar(),
    ),
    FormBuilderChipOption(
      value: 'Objective-C',
      avatar: CircleAvatar(),
    ),
  ],
  onChanged: (List<String>? values) {
    // Code..
  },
  validator: FormBuilderValidators.compose(
    [
      FormBuilderValidators.minLength(1),
      FormBuilderValidators.maxLength(3),
    ],
  ),
),

10. FormBuilderDateRangePicker

FormBuilderDateRangePicker cho phép người dùng lựa chọn một phạm vi ngày tháng (Từ ngày .. đến ngày).
main_daterangepicker_ex1.dart (*)
FormBuilderDateRangePicker(
  name: 'date_range',
  firstDate: DateTime(1970),
  lastDate: DateTime(2030),
  format: DateFormat('yyyy-MM-dd'),
  initialValue: DateTimeRange(
    start: DateTime(2000, 1, 1),
    end: DateTime(2020, 2, 1),
  ),
  onChanged: (DateTimeRange? range) {
    print("Range: $range");
  },
  decoration: InputDecoration(
    labelText: 'Date Range',
    helperText: 'Helper text',
    hintText: 'Hint text',
    suffixIcon: IconButton(
      icon: const Icon(Icons.close),
      onPressed: () {
        _formKey.currentState!.fields['date_range']
            ?.didChange(null);
      },
    ),
  ),
),

11. FormBuilderDateTimePicker

FormBuilderDateTimePicker cho phép người dùng lựa chọn ngày tháng và thời gian.
main_datetimepicker_ex1.dart (*)
FormBuilderDateTimePicker(
  name: 'date',
  initialEntryMode: DatePickerEntryMode.calendar,
  initialValue: DateTime.now(),
  inputType: InputType.both,
  decoration: InputDecoration(
    labelText: 'Appointment Time',
    suffixIcon: IconButton(
      icon: const Icon(Icons.close),
      onPressed: () {
        _formKey.currentState!.fields['date']?.didChange(null);
      },
    ),
  ),
  initialTime: const TimeOfDay(hour: 8, minute: 0),
),

12. FormBuilderDropdown

FormBuilderDropdown cho phép người dùng lựa chọn một tuỳ chọn từ danh sách thả xuống.
main_dropdown_ex1.dart (*)
FormBuilderDropdown<String>(
  name: 'gender',
  decoration: InputDecoration(
    labelText: 'Gender',
    suffix: _genderHasError
        ? const Icon(Icons.error)
        : const Icon(Icons.check),
    hintText: 'Select Gender',
  ),
  validator: FormBuilderValidators.compose(
    [
      FormBuilderValidators.required(),
    ],
  ),
  items: ['Male', 'Female', 'Other']
      .map(
        (gender) => DropdownMenuItem(
          alignment: AlignmentDirectional.centerStart,
          value: gender,
          child: Text(gender),
        ),
      )
      .toList(),
  onChanged: (val) {
    bool hasError =
        !(_formKey.currentState?.fields['gender']?.validate() ??
            false);
    setState(() {
      _genderHasError = hasError;
    });
  },
  valueTransformer: (val) => val?.toString(),
),
  • Flutter DropdownMenu (***)

13. FormBuilderSlider

FormBuilderSlider cho phép người dùng lựa chọn một giá trị số trong một khoảng giá trị. Slider có một cái cần gạt (thumb). Bạn có thể chạm vào cần gạt, sử dụng phím mũi tên hoặc chạm vào thanh bên trái hoặc bên phải để di chuyển cần gạt.
main_slider_ex1.dart (*)
FormBuilderSlider(
  name: 'slider',
  validator: FormBuilderValidators.compose(
    [
      FormBuilderValidators.min(6),
    ],
  ),
  onChanged: (double? value) {
    print("Value: $value");
  },
  min: 0.0,
  max: 10.0,
  initialValue: 7.0,
  divisions: 20,
  activeColor: Colors.red,
  inactiveColor: Colors.pink[100],
  decoration: const InputDecoration(
    labelText: 'Number of things',
  ),
),

14. FormBuilderRangeSlider

FormBuilderRangeSlider cho phép người dùng lựa chọn một khoảng giá trị số.
main_rangeslider_ex1.dart (*)
FormBuilderRangeSlider(
  name: 'range_slider',
  onChanged: (RangeValues? range) {
    print("Range: $range");
  },
  min: 0.0,
  max: 100.0,
  initialValue: const RangeValues(4, 7),
  divisions: 20,
  maxValueWidget: (max) => TextButton(
    onPressed: () {
      _formKey.currentState?.patchValue(
        {'range_slider': const RangeValues(4, 100)},
      );
    },
    child: Text(max),
  ),
  activeColor: Colors.red,
  inactiveColor: Colors.pink[100],
  decoration: const InputDecoration(labelText: 'Price Range'),
),
  • Flutter RangeSlider (***)

Các hướng dẫn lập trình Flutter

Show More