Popular Searches
Popular Course Categories
Popular Courses

Reading and controlling user input

Reading and controlling user input

Flutter Forms & User Input


Reading and Controlling User Input in Flutter


Reading and controlling user input is an important part of Flutter application development. Flutter provides widgets such as TextField and TextFormField for accepting user input, while TextEditingController can be used to read, modify, clear, and monitor the text entered by the user. Flutter's official documentation recommends using a controller when an application needs direct access to the current text value or needs to control the text field programmatically.




1. What is User Input?


User input is any information entered or selected by a user while interacting with an application.



  • Name

  • Email address

  • Password

  • Phone number

  • Search keywords

  • Address

  • Comments and messages

  • Product quantities

  • Login credentials


For example, when a user types Manish into a name field, "Manish" is user input.


Example


TextField(
  decoration: InputDecoration(
    labelText: 'Enter your name',
  ),
)



2. Flutter Widgets Used for User Input


Flutter mainly provides two commonly used text input widgets:






WidgetPurpose
TextFieldUsed for general text input.
TextFormFieldUsed for text input inside a Form, especially when validation is required.

TextField Example


TextField(
  decoration: InputDecoration(
    labelText: 'Username',
    hintText: 'Enter your username',
    border: OutlineInputBorder(),
  ),
)

TextFormField Example


TextFormField(
  decoration: InputDecoration(
    labelText: 'Email',
    border: OutlineInputBorder(),
  ),
)

TextFormField integrates with Flutter's Form widget and is particularly useful for validation.




3. What is TextEditingController?


TextEditingController is a Flutter class used to control an editable text field. It allows developers to read the current text, change the text programmatically, clear the field, listen for changes, and work with the current text selection.


When a TextEditingController is connected to a TextField, changes made by the user update the controller, and programmatic changes made through the controller update the text field.


Basic Syntax


final TextEditingController nameController = TextEditingController();



4. Creating a TextEditingController


A controller is normally created inside the State of a StatefulWidget when the controller belongs to that widget.


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

  @override
  State createState() => _UserFormState();
}

class _UserFormState extends State {
  final TextEditingController nameController = TextEditingController();

  @override
  void dispose() {
    nameController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: nameController,
    );
  }
}


Calling dispose() when the controller is no longer needed is important for proper resource cleanup.




5. Connecting a Controller to a TextField


The controller is connected to a TextField using the controller property.


final TextEditingController nameController = TextEditingController();

TextField(
  controller: nameController,
)


After connecting the controller, the application can read or control the text using the controller.




6. Reading User Input


The most common way to read text from a controller is by using its text property.


String name = nameController.text;

Complete Example


import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: UserInputScreen(),
    );
  }
}

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

  @override
  State createState() => _UserInputScreenState();
}

class _UserInputScreenState extends State {
  final TextEditingController nameController = TextEditingController();

  @override
  void dispose() {
    nameController.dispose();
    super.dispose();
  }

  void readInput() {
    String name = nameController.text;

    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('Hello, $name'),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Read User Input'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          children: [
            TextField(
              controller: nameController,
              decoration: const InputDecoration(
                labelText: 'Enter your name',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: readInput,
              child: const Text('Submit'),
            ),
          ],
        ),
      ),
    );
  }
}




7. Controlling User Input Programmatically


A controller is not only used for reading input. It can also change the contents of a text field programmatically.


Changing Text


nameController.text = 'Flutter Developer';

This replaces the current text with Flutter Developer.


Example


ElevatedButton(
  onPressed: () {
    nameController.text = 'Flutter Developer';
  },
  child: const Text('Set Text'),
)



8. Clearing User Input


The clear() method removes all text from the controller and therefore clears the connected text field.


nameController.clear();

Example


ElevatedButton(
  onPressed: () {
    nameController.clear();
  },
  child: const Text('Clear'),
)

Clear Button Example


Row(
  children: [
    Expanded(
      child: TextField(
        controller: nameController,
      ),
    ),
    IconButton(
      onPressed: () {
        nameController.clear();
      },
      icon: const Icon(Icons.clear),
    ),
  ],
)



9. Setting Initial Text


A controller can contain initial text when it is created.


final TextEditingController nameController =
    TextEditingController(text: 'Manish');

When the text field is displayed, it initially contains Manish.


Example


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

  @override
  State createState() => _ProfileScreenState();
}

class _ProfileScreenState extends State {
  final TextEditingController nameController =
      TextEditingController(text: 'Manish');

  @override
  void dispose() {
    nameController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(20),
        child: TextField(
          controller: nameController,
        ),
      ),
    );
  }
}




10. Detecting Changes with onChanged


The onChanged callback runs whenever the text in a TextField changes.


TextField(
  onChanged: (value) {
    print(value);
  },
)

Example: Display Character Count


TextField(
  onChanged: (value) {
    print('Characters: ${value.length}');
  },
)

onChanged is useful when you only need to react to changes and do not need a controller.




11. Detecting Changes with TextEditingController


A controller can also notify the application whenever its value changes by using addListener().


final TextEditingController searchController =
    TextEditingController();

@override
void initState() {
  super.initState();

  searchController.addListener(() {
    print(searchController.text);
  });
}


When using a listener, remember to dispose of the controller when the widget is removed.




12. onChanged vs TextEditingController











FeatureonChangedTextEditingController
Read current textYes, inside callbackYes
Change text programmaticallyNoYes
Clear textNot directlyYes
Listen outside widget callbackLimitedYes
Access selectionNoYes
Best for simple input changesYesOptional
Best for advanced input controlNoYes



13. Reading Input on Button Press


A common pattern is to read the input when the user presses a button.


final TextEditingController emailController =
    TextEditingController();

ElevatedButton(
  onPressed: () {
    String email = emailController.text;

    print('Email: $email');
  },
  child: const Text('Submit'),
)




14. Reading Multiple Input Fields


Applications often require multiple controllers for multiple input fields.


final TextEditingController nameController = TextEditingController();
final TextEditingController emailController = TextEditingController();
final TextEditingController phoneController = TextEditingController();

Example


Column(
  children: [
    TextField(
      controller: nameController,
      decoration: const InputDecoration(
        labelText: 'Name',
      ),
    ),
    TextField(
      controller: emailController,
      decoration: const InputDecoration(
        labelText: 'Email',
      ),
    ),
    TextField(
      controller: phoneController,
      decoration: const InputDecoration(
        labelText: 'Phone',
      ),
    ),
  ],
)

Reading All Values


void submitForm() {
  String name = nameController.text;
  String email = emailController.text;
  String phone = phoneController.text;

  print(name);
  print(email);
  print(phone);
}




15. Controlling Input with TextInputType


keyboardType can be used to suggest an appropriate keyboard for the type of input.


Email Input


TextField(
  controller: emailController,
  keyboardType: TextInputType.emailAddress,
)

Phone Input


TextField(
  controller: phoneController,
  keyboardType: TextInputType.phone,
)

Number Input


TextField(
  keyboardType: TextInputType.number,
)



16. Controlling Password Input


For passwords, use obscureText: true.


final TextEditingController passwordController =
    TextEditingController();

TextField(
  controller: passwordController,
  obscureText: true,
  decoration: const InputDecoration(
    labelText: 'Password',
    border: OutlineInputBorder(),
  ),
)


Password Visibility Example


bool isPasswordVisible = false;

TextField(
  controller: passwordController,
  obscureText: !isPasswordVisible,
  decoration: InputDecoration(
    labelText: 'Password',
    suffixIcon: IconButton(
      icon: Icon(
        isPasswordVisible
            ? Icons.visibility
            : Icons.visibility_off,
      ),
      onPressed: () {
        setState(() {
          isPasswordVisible = !isPasswordVisible;
        });
      },
    ),
  ),
)




17. Input Validation


User input should normally be validated before it is processed or submitted. Flutter's TextFormField provides a validator callback for this purpose.


Required Field Validation


TextFormField(
  controller: nameController,
  validator: (value) {
    if (value == null || value.trim().isEmpty) {
      return 'Please enter your name';
    }
    return null;
  },
)

Email Validation


TextFormField(
  controller: emailController,
  validator: (value) {
    if (value == null || value.trim().isEmpty) {
      return 'Please enter your email';
    }

    if (!value.contains('@')) {
      return 'Enter a valid email address';
    }

    return null;
  },
)




18. Using Form with User Input


A Form can group multiple input fields and validate them together.


final GlobalKey formKey = GlobalKey();

Form(
  key: formKey,
  child: Column(
    children: [
      TextFormField(
        controller: nameController,
        validator: (value) {
          if (value == null || value.isEmpty) {
            return 'Enter your name';
          }
          return null;
        },
      ),
      ElevatedButton(
        onPressed: () {
          if (formKey.currentState!.validate()) {
            print('Form is valid');
          }
        },
        child: const Text('Submit'),
      ),
    ],
  ),
)




19. Reading and Trimming User Input


Users may accidentally enter spaces before or after their text. The trim() method can remove unnecessary leading and trailing whitespace.


String name = nameController.text.trim();

print(name);


Example


String email = emailController.text.trim();

if (email.isEmpty) {
  print('Email is required');
}




20. Controlling Input Length


The maxLength property can limit the amount of text entered into a field.


TextField(
  controller: nameController,
  maxLength: 30,
  decoration: const InputDecoration(
    labelText: 'Name',
  ),
)

Example: OTP Input


TextField(
  maxLength: 6,
  keyboardType: TextInputType.number,
  decoration: const InputDecoration(
    labelText: 'Enter OTP',
  ),
)



21. Using InputFormatter


Input formatters can be used to control what users are allowed to enter.


Allow Only Digits


import 'package:flutter/services.dart';

TextField(
  keyboardType: TextInputType.number,
  inputFormatters: [
    FilteringTextInputFormatter.digitsOnly,
  ],
)


Allow a Specific Number of Characters


TextField(
  inputFormatters: [
    LengthLimitingTextInputFormatter(10),
  ],
)



22. Selecting Text


TextEditingController provides access to the current text selection.


nameController.selection = TextSelection(
  baseOffset: 0,
  extentOffset: nameController.text.length,
);

Select All Text


void selectAllText() {
  nameController.selection = TextSelection(
    baseOffset: 0,
    extentOffset: nameController.text.length,
  );
}



23. Moving the Cursor


The controller can also control the cursor position using selection.


nameController.selection = TextSelection.collapsed(
  offset: nameController.text.length,
);

This places the cursor at the end of the text.




24. TextEditingValue


TextEditingController contains a value of type TextEditingValue. The value contains information about the current text, selection, and composing region.


TextEditingValue currentValue = nameController.value;

print(currentValue.text);
print(currentValue.selection);


The value property is useful when an application needs more control than simply reading the text.




25. Changing Text and Selection Together


When advanced control is required, the controller's value can be updated.


nameController.value = TextEditingValue(
  text: 'Flutter',
  selection: const TextSelection.collapsed(
    offset: 7,
  ),
);

This sets both the text and cursor position.




26. Search Field Example


Search boxes are one of the most common examples of reading and controlling user input.


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

  @override
  State createState() => _SearchScreenState();
}

class _SearchScreenState extends State {
  final TextEditingController searchController =
      TextEditingController();

  String searchText = '';

  @override
  void dispose() {
    searchController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: TextField(
          controller: searchController,
          onChanged: (value) {
            setState(() {
              searchText = value;
            });
          },
          decoration: InputDecoration(
            hintText: 'Search...',
            suffixIcon: IconButton(
              icon: const Icon(Icons.clear),
              onPressed: () {
                searchController.clear();
                setState(() {
                  searchText = '';
                });
              },
            ),
          ),
        ),
      ),
      body: Center(
        child: Text(
          'Searching for: $searchText',
        ),
      ),
    );
  }
}




27. Login Form Example


import 'package:flutter/material.dart';

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

  @override
  State createState() => _LoginScreenState();
}

class _LoginScreenState extends State {
  final TextEditingController emailController =
      TextEditingController();

  final TextEditingController passwordController =
      TextEditingController();

  @override
  void dispose() {
    emailController.dispose();
    passwordController.dispose();
    super.dispose();
  }

  void login() {
    final email = emailController.text.trim();
    final password = passwordController.text;

    if (email.isEmpty || password.isEmpty) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Please enter email and password'),
        ),
      );
      return;
    }

    print('Email: $email');
    print('Password: $password');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Login'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          children: [
            TextField(
              controller: emailController,
              keyboardType: TextInputType.emailAddress,
              decoration: const InputDecoration(
                labelText: 'Email',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 16),
            TextField(
              controller: passwordController,
              obscureText: true,
              decoration: const InputDecoration(
                labelText: 'Password',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 20),
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: login,
                child: const Text('Login'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}




28. Registration Form Example


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

  @override
  State createState() =>
      _RegistrationScreenState();
}

class _RegistrationScreenState
    extends State {
  final nameController = TextEditingController();
  final emailController = TextEditingController();
  final passwordController = TextEditingController();

  @override
  void dispose() {
    nameController.dispose();
    emailController.dispose();
    passwordController.dispose();
    super.dispose();
  }

  void registerUser() {
    final name = nameController.text.trim();
    final email = emailController.text.trim();
    final password = passwordController.text;

    if (name.isEmpty ||
        email.isEmpty ||
        password.isEmpty) {
      return;
    }

    print('Name: $name');
    print('Email: $email');
    print('Password: $password');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          children: [
            TextField(
              controller: nameController,
              decoration: const InputDecoration(
                labelText: 'Full Name',
              ),
            ),
            TextField(
              controller: emailController,
              keyboardType: TextInputType.emailAddress,
              decoration: const InputDecoration(
                labelText: 'Email',
              ),
            ),
            TextField(
              controller: passwordController,
              obscureText: true,
              decoration: const InputDecoration(
                labelText: 'Password',
              ),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: registerUser,
              child: const Text('Register'),
            ),
          ],
        ),
      ),
    );
  }
}




29. Sending User Input to an API


User input can be read from a controller and then passed to a networking function or API request.


final TextEditingController titleController =
    TextEditingController();

ElevatedButton(
  onPressed: () {
    final title = titleController.text.trim();

    createData(title);
  },
  child: const Text('Send Data'),
)


Conceptual API Function


Future createData(String title) async {
  print('Sending title: $title');

  // API request can be performed here.
}


This pattern is commonly used for registration, login, search, profile editing, product creation, comments, and other data-entry features.




30. Editing Existing User Data


A controller is especially useful when an application needs to display existing data and allow the user to modify it.


final TextEditingController nameController =
    TextEditingController();

void loadUserData() {
  nameController.text = 'Manish';
}


The text field will display the existing value, and the user can edit it before submitting the updated data.




31. Resetting a Form


Multiple controllers can be cleared together when the user wants to reset a form.


void resetForm() {
  nameController.clear();
  emailController.clear();
  phoneController.clear();
  passwordController.clear();
}

Reset Button


OutlinedButton(
  onPressed: resetForm,
  child: const Text('Reset'),
)



32. Listening to Controller Changes


The addListener() method can be used when the application needs to react whenever the controller's value changes.


@override
void initState() {
  super.initState();

  nameController.addListener(() {
    print('Current text: ${nameController.text}');
  });
}


For example, this can be used for live search, character counters, enabling or disabling buttons, formatting, or updating another part of the UI.




33. Enabling a Button Based on Input


bool canSubmit = false;

@override
void initState() {
  super.initState();

  nameController.addListener(() {
    setState(() {
      canSubmit = nameController.text.trim().isNotEmpty;
    });
  });
}


Button


ElevatedButton(
  onPressed: canSubmit ? submitForm : null,
  child: const Text('Submit'),
)



34. Important Listener Warning


A controller listener can create an infinite update loop if it changes the same controller value every time it is notified.


Problematic Pattern


nameController.addListener(() {
  nameController.text = nameController.text.toUpperCase();
});

Changing the controller inside its listener can trigger the listener again. When modifying controller values from a listener, carefully design the condition and update logic to avoid repeated notifications or feedback loops.




35. Disposing Controllers


Controllers should be disposed when the widget that owns them is removed from the widget tree.


@override
void dispose() {
  nameController.dispose();
  emailController.dispose();
  passwordController.dispose();
  super.dispose();
}

This is particularly important when controllers are created by a StatefulWidget.




36. Common Mistakes


Mistake 1: Forgetting dispose()


final controller = TextEditingController();

If this controller belongs to a stateful screen, remember to dispose of it.


Mistake 2: Creating Controllers Inside build()


@override
Widget build(BuildContext context) {
  final controller = TextEditingController();
  return TextField(controller: controller);
}

This can create a new controller during rebuilds. For a stateful screen, keep the controller as a state field instead.


Mistake 3: Reading Input Without Trimming


final email = emailController.text.trim();

Using trim() is often helpful when processing values such as names, usernames, and email addresses.


Mistake 4: No Validation


Never assume that user input is valid. Validate required fields and expected formats before processing or sending the data.


Mistake 5: Storing Sensitive Input Carelessly


Passwords and other sensitive values should be handled carefully. Avoid printing sensitive information to logs in production applications.




37. TextField Controller vs Form Validation











RequirementRecommended Approach
Read textTextEditingController
Change text programmaticallyTextEditingController
Clear inputTextEditingController.clear()
React to every changeonChanged or controller listener
Validate multiple fieldsForm + TextFormField
Limit or filter inputinputFormatters
Password fieldTextField + obscureText



38. Practical Example: Feedback Form


import 'package:flutter/material.dart';

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

  @override
  State createState() => _FeedbackScreenState();
}

class _FeedbackScreenState extends State {
  final TextEditingController nameController =
      TextEditingController();

  final TextEditingController feedbackController =
      TextEditingController();

  @override
  void dispose() {
    nameController.dispose();
    feedbackController.dispose();
    super.dispose();
  }

  void submitFeedback() {
    final name = nameController.text.trim();
    final feedback = feedbackController.text.trim();

    if (name.isEmpty || feedback.isEmpty) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Please complete all fields'),
        ),
      );
      return;
    }

    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
        content: Text('Feedback submitted successfully'),
      ),
    );

    nameController.clear();
    feedbackController.clear();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Feedback'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          children: [
            TextField(
              controller: nameController,
              decoration: const InputDecoration(
                labelText: 'Your Name',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 16),
            TextField(
              controller: feedbackController,
              maxLines: 5,
              decoration: const InputDecoration(
                labelText: 'Your Feedback',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 20),
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: submitFeedback,
                child: const Text('Submit Feedback'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}




39. Practical Example: Live Character Counter


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

  @override
  State createState() => _MessageScreenState();
}

class _MessageScreenState extends State {
  final TextEditingController messageController =
      TextEditingController();

  @override
  void initState() {
    super.initState();

    messageController.addListener(() {
      setState(() {});
    });
  }

  @override
  void dispose() {
    messageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          children: [
            TextField(
              controller: messageController,
              maxLength: 200,
              maxLines: 5,
              decoration: const InputDecoration(
                labelText: 'Message',
                border: OutlineInputBorder(),
              ),
            ),
            Text(
              'Characters: ${messageController.text.length}',
            ),
          ],
        ),
      ),
    );
  }
}




40. Important TextEditingController Properties and Methods











Property/MethodPurpose
textGets or sets the current text.
valueGets or sets the complete TextEditingValue.
selectionGets or sets the selected text/cursor position.
clear()Removes all text.
addListener()Registers a callback for changes.
removeListener()Removes a previously registered listener.
dispose()Releases resources when the controller is no longer needed.



41. Recommended Workflow for Handling User Input



  1. Create the required input widget.

  2. Create a TextEditingController when direct control or retrieval is needed.

  3. Connect the controller to the TextField or TextFormField.

  4. Read the value using controller.text.

  5. Trim or validate the input when appropriate.

  6. Use input formatters when specific input restrictions are required.

  7. Process or submit the data.

  8. Clear the controller when necessary.

  9. Dispose of the controller when the owning widget is destroyed.




42. Real-World Applications



  • Login forms

  • Registration forms

  • Search screens

  • Chat applications

  • Feedback forms

  • Profile editing

  • Checkout forms

  • Address forms

  • OTP input

  • Product creation forms

  • Admin dashboards

  • API data submission




43. Best Practices



  • Use TextEditingController when you need to read or control text programmatically.

  • Use onChanged for simple change callbacks.

  • Use TextFormField and Form when structured validation is required.

  • Always dispose controllers owned by a stateful widget.

  • Do not unnecessarily create controllers inside build().

  • Use trim() where whitespace should not affect validation.

  • Use appropriate keyboardType values.

  • Use inputFormatters when input needs restrictions.

  • Avoid logging passwords or other sensitive information.

  • Be careful when modifying controller values inside listeners to avoid update loops.




44. Interview Questions


Q1. What is TextEditingController?


TextEditingController is a Flutter controller used to read and control the text being edited in a TextField or TextFormField.


Q2. How do you read text from a TextField?


String value = controller.text;

Q3. How do you clear a TextField?


controller.clear();

Q4. How do you set text programmatically?


controller.text = 'Hello Flutter';

Q5. Why should a TextEditingController be disposed?


It should be disposed when no longer needed so that resources associated with the controller are released properly.


Q6. What is the difference between TextField and TextFormField?


TextField is a general-purpose text input widget, while TextFormField integrates with Form and supports form validation.


Q7. What is onChanged used for?


onChanged is called whenever the text entered in a text field changes.


Q8. How can you listen to controller changes?


controller.addListener(() {
  print(controller.text);
});

Q9. How can you limit user input?


You can use properties such as maxLength and input formatters such as FilteringTextInputFormatter.


Q10. How can you hide password input?


TextField(
  obscureText: true,
)



45. Practical Exercise


Create a Flutter User Registration Screen containing:



  • Full Name field

  • Email field

  • Phone field

  • Password field

  • Confirm Password field

  • Register button

  • Clear button


Requirements:



  1. Use a separate controller for each field.

  2. Read all values when Register is pressed.

  3. Trim the name and email values.

  4. Validate that required fields are not empty.

  5. Validate that password and confirm password match.

  6. Use the correct keyboard type for email and phone.

  7. Hide password fields using obscureText.

  8. Clear all fields after successful registration.

  9. Dispose all controllers properly.




46. Quick Revision













TaskCode
Create controllerTextEditingController()
Connect controllercontroller: nameController
Read textnameController.text
Set textnameController.text = 'Flutter'
Clear textnameController.clear()
Listen for changesnameController.addListener(...)
Get selectionnameController.selection
Get complete valuenameController.value
Release resourcesnameController.dispose()



47. Key Takeaways



  • Flutter uses TextField and TextFormField for text input.

  • TextEditingController provides direct access to the current text.

  • Use controller.text to read user input.

  • Use controller.text = ... or the controller's value to update input programmatically.

  • Use clear() to remove text.

  • Use onChanged or addListener() to respond to changes.

  • Use Form and TextFormField for structured validation.

  • Use inputFormatters and maxLength to control input.

  • Use dispose() when a controller is no longer needed.

  • Never trust unvalidated user input when processing or sending data.




48. Official Flutter Resources





49. JustAcademy Flutter Training Resources


Learn more about Flutter development through the following resources:



whatsapp