Form Layout

Estimated reading: 7 minutes 33 views

FlutKit uses AdaptiveWrap as the foundation for building responsive, grid-based form layouts across mobile, tablet, and desktop screens.

Instead of hard-coding rows and columns, AdaptiveWrap allows the layout to adapt dynamically based on screen width, while keeping form structure predictable and maintainable.

The Core Problem AdaptiveWrap Solves

Traditional form layouts often rely on:

  • Row + Expanded (hard to scale)
  • GridView (poor for form validation & scrolling)
  • MediaQuery-based conditions (repetitive and fragile)

These approaches become difficult to maintain as form complexity grows.

AdaptiveWrap addresses this by:

  • Decoupling layout logic from screen size
  • Allowing semantic grouping of form fields
  • Enabling responsive behavior without branching code

How AdaptiveWrap Works

Think of AdaptiveWrap as:

A responsive container that distributes form fields into columns based on screen width rules.

It has three main responsibilities:

  1. Decide how many columns to render
  2. Decide how wide each column should be
  3. Automatically wrap fields to the next row when needed

Please visit the following link to learn more about AdaptiveWrap: https://flutkit.com/docs/flutter-dashboard-ui-kits/layout-ui-widgets-components/responsive-content-layout-with-adaptivewrap/

Why AdaptiveWrap Is Ideal for Forms

1. Forms Are Vertical by Nature

Unlike grids or lists, forms:

  • Must scroll vertically
  • Contain validation logic
  • Depend on visual hierarchy

AdaptiveWrap works inside Column and Form naturally, unlike GridView.

2. Natural Progressive Disclosure

As screen size increases:

  • More fields appear side-by-side
  • Cognitive load stays manageable
  • No layout rewrite is needed

3. Better Maintainability

You can:

  • Reorder fields without touching layout logic
  • Change breakpoints globally
  • Reuse layout patterns across screens

Example of AdaptiveWrap implementation in a form

From the form image above, it can be broken down as follows:

Dart
Form
└─ Column
  ├─ AdaptiveWrap 2 Columns(First Name, Last Name, Email, Phone Number, Department, Evidence)
  ├─ AdaptiveWrap 3 Columns(City, State, Zip Code)
  ├─ TextFormField (Description)
  ├─ Checkbox
  └─ Actions

We will focus on AdaptiveWrap 2 Columns and AdaptiveWrap 3 Columns. Here is an example code:

AdaptiveWrap 2 Columns

Dart
AdaptiveWrap(
  breakpoints: {
    kScreenWidthSm: 1, // set breakpoint for 1 column layout,
    kScreenWidthMd: 2, // set breakpoint for 2 column layout
  },
  columnRatios: const [
    0.5, // set column A as 50% width
    0.5, // set column B as 50% width
  ],
  spacing: kDefaultPadding, // spacing
  runSpacing: kDefaultPadding, // run spacing
  children: [
    // first name
    Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        FormLabel(
          text: 'First Name',
          showRequired: false,
        ),
        SizedBox(
          height: kDefaultPadding / 2,
        ),
        CustomTextFormField(
          controller: firstNameController,
          hintText: 'Your First Name',
          suffixIcon: Icons.person_outline,
          validator: (value) =>
              Validators.requiredField(value, context),
          successMessage: 'Looks Good!',
        ),
      ],
    ),

    // last name
    Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        FormLabel(
          text: 'Last Name',
          showRequired: false,
        ),
        SizedBox(
          height: kDefaultPadding / 2,
        ),
        CustomTextFormField(
          controller: lastNameController,
          hintText: 'Your Last Name',
          suffixIcon: Icons.person_outline,
          validator: (value) =>
              Validators.requiredField(value, context),
          successMessage: 'Looks Good!',
        ),
      ],
    ),

    // email
    Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        FormLabel(
          text: 'Email',
          showRequired: false,
        ),
        SizedBox(
          height: kDefaultPadding / 2,
        ),
        CustomTextFormField(
          controller: emailController,

          hintText: 'e.g. johndoe@example.com',
          suffixIcon: Icons.mail_outline,
          // combining required field and email validator
          validator: Validators.combineValidators(
            [
              (value) => Validators.requiredField(value, context),
              (value) => Validators.email(value, context)
            ],
          ),
          successMessage: 'Looks Good!',
        ),
      ],
    ),

    // phone
    Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        FormLabel(
          text: 'Phone Number',
          showRequired: false,
        ),
        SizedBox(
          height: kDefaultPadding / 2,
        ),
        CustomTextFormField(
          controller: phoneNumberController,
          hintText: 'e.g. +123456789012345 or 123456789012345',
          validator: Validators.combineValidators(
            [
              (value) => Validators.requiredField(value, context),
              (value) => Validators.phone(value, context)
            ],
          ),
          suffixIcon: Icons.call_outlined,
          keyboardType: TextInputType.number,
          successMessage: 'Looks Good!',
        ),
      ],
    ),

    // Division
    Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        FormLabel(
          text: 'Department',
          showRequired: false,
        ),
        SizedBox(
          height: kDefaultPadding / 2,
        ),
        CustomDropdownFormField<String>(
          hint: Text(
            'Choose Departement',
            style: TextStyle(
              color: kTextColor,
              fontSize: kBodyMedium,
            ),
          ),
          items: const [
            DropdownMenuItem(
              value: "hrd",
              child: Text("HRD"),
            ),
            DropdownMenuItem(
              value: "it",
              child: Text("IT"),
            ),
            DropdownMenuItem(
              value: "marketing",
              child: Text("Marketing"),
            ),
          ],
          initialValue: _selectedDept,
          onChanged: (value) => setState(() => _selectedDept = value),
          validator: (value) => Validators.requiredField(
            value,
            context,
            customMessage: 'Dept must be selected',
          ),
        ),
      ],
    ),

    // Evidence
    Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        FormLabel(
          text: 'Evidence',
          showRequired: false,
        ),
        SizedBox(
          height: kDefaultPadding / 2,
        ),
        FileUploadForm(
          // combining required and file extension validators
          onFilesSelected: (files) =>
              setState(() => _evidenceFiles = files),
          validator: Validators.combineFileValidators(
            [
              (files) => Validators.fileRequired(files, context),
              (files) => Validators.fileExtension(files,
                  ['pdf', 'jpg'], context), // pdf, jpg extension only
            ],
          ),
          successMessage: 'Looks Good!',
        ),
      ],
    )
  ],
),          

AdaptiveWrap 3 Columns

Dart
AdaptiveWrap(
  breakpoints: {
    kScreenWidthSm: 1, // set breakpoint for 1 column layout,
    kScreenWidthMd: 3, // set breakpoint for 3 column layout
  },
  columnRatios: const [
    1 / 3, // set column A as 1/3 width
    1 / 3, // set column B as 1/3 width
    1 / 3, // set column C as 1/3 width
  ],
  spacing: kDefaultPadding, // spacing
  runSpacing: kDefaultPadding, // run spacing
  children: [
    // City
    Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        FormLabel(
          text: 'City',
          showRequired: false,
        ),
        SizedBox(
          height: kDefaultPadding / 2,
        ),
        CustomTextFormField(
          controller: cityController,
          hintText: 'Your City',
          suffixIcon: Icons.location_city_outlined,
          validator: (value) =>
              Validators.requiredField(value, context),
          successMessage: 'Looks Good!',
        ),
      ],
    ),

    // State
    Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        FormLabel(
          text: 'State',
          showRequired: false,
        ),
        SizedBox(
          height: kDefaultPadding / 2,
        ),
        CustomDropdownFormField<String>(
          hint: Text(
            'Choose State',
            style: TextStyle(
              color: kTextColor,
              fontSize: kBodyMedium,
            ),
          ),
          items: const [
            DropdownMenuItem(
              value: "california",
              child: Text("California"),
            ),
            DropdownMenuItem(
              value: "florida",
              child: Text("Florida"),
            ),
            DropdownMenuItem(
              value: "texas",
              child: Text("Texas"),
            ),
          ],
          initialValue: _selectedState,
          onChanged: (val) => setState(() => _selectedState = val),
          validator: (value) => Validators.requiredField(
            value,
            context,
            customMessage: 'State must be selected',
          ),
        ),
      ],
    ),

    // zip code

    Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        FormLabel(
          text: 'Zip Code',
          showRequired: false,
        ),
        SizedBox(
          height: kDefaultPadding / 2,
        ),
        CustomTextFormField(
          controller: zipController,
          hintText: 'e.g. 90210',
          validator: FormBuilderValidators.zipCode(),
          suffixIcon: Icons.location_history_outlined,
          keyboardType: TextInputType.number,
          successMessage: 'Looks Good!',
        ),
      ],
    ),
  ],
),

Demo

You can preview several form layout demo and behaviors in the FlutKit demo application.

Forms → Form Layout

Summary

AdaptiveWrap is not just a layout widget—it is a form architecture tool in FlutKit.

It enables:

  • Clean responsive logic
  • Scalable form design
  • Consistent UI across screen sizes
  • Easier long-term maintenance
Share this Doc

Form Layout

Or copy link

CONTENTS