Skip to content

Software Development at Program Tom LTD

Place for coding, programming, development and software in general.

Menu
  • Blog
  • PDF Booklets
  • Dev Utils & Content
  • Java Spring Boot Or Web Apps
  • English
    • български
    • English
    • Español
    • Português
    • हिन्दी
    • Русский
    • Deutsch
    • Français
    • Italiano
    • العربية
  • About Us
Menu
TextEditingController State in Flutter

TextEditingController State in Flutter

Posted on February 14, 2025 by Toma Velev

n Flutter, TextEditingController is typically used with StatefulWidget because it needs to persist its state across widget rebuilds and be disposed after it is not needed.

Example

The best practice for managing a TextEditingController is to use a StatefulWidget so that the controller is properly disposed of when no longer needed.

import 'package:flutter/material.dart';

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

  @override
  _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  final TextEditingController _controller = TextEditingController();

  @override
  void dispose() {
    _controller.dispose(); // Properly dispose of the controller
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("StatefulWidget with TextEditingController")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              controller: _controller,
              decoration: const InputDecoration(labelText: "Enter text"),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                print(_controller.text);
              },
              child: const Text("Submit"),
            ),
          ],
        ),
      ),
    );
  }
}
  • TextEditingController is properly disposed of when the widget is removed from the widget tree, preventing memory leaks.
  • It allows you to manage the state of the text input dynamically.

Alternative Approach: Using Provider

If you prefer a StatelessWidget, you can use a state management solution like Provider to manage the TextEditingController.

Example Using Provider:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => TextEditingProvider(),
      child: const MyApp(),
    ),
  );
}

class TextEditingProvider extends ChangeNotifier {
  final TextEditingController controller = TextEditingController();

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

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

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

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

  @override
  Widget build(BuildContext context) {
    final textProvider = Provider.of<TextEditingProvider>(context);

    return Scaffold(
      appBar: AppBar(title: const Text("Provider with TextEditingController")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              controller: textProvider.controller,
              decoration: const InputDecoration(labelText: "Enter text"),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                print(textProvider.controller.text);
              },
              child: const Text("Submit"),
            ),
          ],
        ),
      ),
    );
  }
}

Why Use Provider?

  • TextEditingController is managed globally and disposed of properly.
  • You can use StatelessWidget while still handling state dynamically.
  • It works well in larger applications where text controllers need to be shared between widgets.

Would you like an approach using Riverpod or GetX for even more flexibility?

Yes! You can use an InheritedWidget to provide the TextEditingController to its descendants. However, InheritedWidget alone does not provide automatic state management, so you may need to combine it with StatefulWidget to properly dispose of the TextEditingController.


✅ Using InheritedWidget for TextEditingController Management

import 'package:flutter/material.dart';

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

// InheritedWidget to provide TextEditingController
class TextControllerProvider extends InheritedWidget {
  final TextEditingController controller;

  TextControllerProvider({
    super.key,
    required this.controller,
    required Widget child,
  }) : super(child: child);

  static TextControllerProvider? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<TextControllerProvider>();
  }

  @override
  bool updateShouldNotify(TextControllerProvider oldWidget) {
    return controller != oldWidget.controller;
  }
}

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

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

// Wrapper that creates and disposes of the controller
class TextControllerWrapper extends StatefulWidget {
  final Widget child;

  const TextControllerWrapper({super.key, required this.child});

  @override
  _TextControllerWrapperState createState() => _TextControllerWrapperState();
}

class _TextControllerWrapperState extends State<TextControllerWrapper> {
  final TextEditingController _controller = TextEditingController();

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

  @override
  Widget build(BuildContext context) {
    return TextControllerProvider(
      controller: _controller,
      child: widget.child,
    );
  }
}

// Stateless Widget that consumes the TextEditingController
class MyStatelessWidget extends StatelessWidget {
  const MyStatelessWidget({super.key});

  @override
  Widget build(BuildContext context) {
    final textController = TextControllerProvider.of(context)!.controller;

    return Scaffold(
      appBar: AppBar(title: const Text("InheritedWidget Example")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              controller: textController,
              decoration: const InputDecoration(labelText: "Enter text"),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                print(textController.text);
              },
              child: const Text("Submit"),
            ),
          ],
        ),
      ),
    );
  }
}

🏆 Why Use InheritedWidget?

  1. Allows StatelessWidget to use the TextEditingController
  2. Provides a reusable, global way to share the controller
  3. Disposes of the controller properly when no longer needed

❓ When to Use InheritedWidget vs. Provider

| Feature | InheritedWidget | Provider |
|———————-|——————|———–|
Good for small apps? | ✅ Yes | ✅ Yes |
Good for large apps? | ❌ Not ideal | ✅ Yes (better scalability) |
Requires boilerplate? | ✅ Some | ❌ Less |

For small projects, InheritedWidget is fine. However, for more complex apps, Provider (or Riverpod) is usually a better choice.

I’ve introduced this option because it is part of the Flutter SDK. You may use it without any external library. I’ve also written how to totally encapsulate the state https://programtom.com/dev/2024/10/17/minimal-flutter-code-to-extract-state-from-statefulwidget/ out of the stateful widget.


Use a Reusable Component

https://github.com/Prime-Holding/widget_toolkit/blob/master/packages/widget_toolkit/doc/text-field-dialog.md

The text field dialog has

  • readonly view of the text field with an edit feature
  • editable field capsulated in a dialog where custom client side and server side validators may be plugged in.
  • have an on changed event in your stateless widget that you could pass up the value to your preferred state management
  • Join iOS Beta Testing Explained
  • Firebase App Distribution Setup
  • iOS App Lifetime Unverified
  • Flutter Bottom Border
  • Get Flutter current time zone

Categories

  • Apps (25)
  • ChatGPT (24)
  • Choosing a Framework (38)
  • Flutter (279)
  • Graphical User Interface (14)
  • Marketing (118)
  • Software Development (288)
  • Spring (45)
  • StartUp (22)
  • Uncategorized (4)
  • Uncategorized (14)
  • Vaadin (15)

Tags

Algorithms (9) crypto (29) flutterdev (39) General (86) Java (7) QR & Bar Codes (3) Software Dev Choices (33) Spring Boot (1) standards (1) Theme (3) User Authentication & Authorization (9) User Experience (10) Utilities (19) WordPress (11)

Product categories

  • All Technologies (86)
    • Flutter Apps (26)
    • GPT (4)
    • Java (38)
    • Native Android (3)
    • PHP (9)
    • Spring (Boot) / Quarkus (35)
    • Utils (15)
    • Vaadin 24+ (27)
    • Vaadin 8 (1)
  • Apps (18)
    • Employees DB (1)
    • Notes (6)
    • Personal Budget (1)
    • Recipes Book (1)
    • Stuff Organizer (1)
    • To-Do (2)
  • PDF Books (3)
  • Source Code Generators (8)

Recent Posts

  • Join iOS Beta Testing Explained
  • Firebase App Distribution Setup
  • iOS App Lifetime Unverified
  • Flutter Bottom Border
  • Get Flutter current time zone

Post Categories

  • Apps (25)
  • ChatGPT (24)
  • Choosing a Framework (38)
  • Flutter (279)
  • Graphical User Interface (14)
  • Marketing (118)
  • Software Development (288)
  • Spring (45)
  • StartUp (22)
  • Uncategorized (4)
  • Uncategorized (14)
  • Vaadin (15)