Home > Net >  TextField widgets lose focus (Flutter)
TextField widgets lose focus (Flutter)

Time:09-17

I am creating an app with Flutter TextField widgets:

class CategoryData {
  int? id;
  String name;
  String description;

  CategoryData({this.id, required this.name, required this.description});
}

class CategoriesEdit extends StatefulWidget {
  Database? db;
  CategoryData? category;

  CategoriesEdit({super.key, required this.db, required this.category});

  @override
  State<StatefulWidget> createState() => CategoriesEditState();
}

class CategoriesEditState extends State<CategoriesEdit> {
  CategoryData? category;

  void saveState(BuildContext context) {
    // ...
  }

  @override
  Widget build(BuildContext context) {
    if (category == null) {
      setState(() {
        category = widget.category ?? CategoryData(name: "", description: "");
      });
    }

    return Scaffold(
        appBar: AppBar(
          leading: InkWell(
              child: const Icon(Icons.arrow_circle_left),
              onTap: () => Navigator.pop(context)),
          title: const Text("Edit Category"),
        ),
        body: Column(children: [
          Column(key: const Key('name'), children: [
            const Text("Category name:*"),
            TextField(
                controller: TextEditingController(text: category!.name),
                onChanged: (value) {
                  setState(() {
                    category!.name = value;
                  });
                })
          ]),
          Column(key: const Key('description'), children: [
            const Text("Description:"),
            TextField(
                controller: TextEditingController(text: category!.description),
                onChanged: (value) {
                  setState(() {
                    category!.description = value;
                  });
                })
          ]),
          Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
            ElevatedButton(
              onPressed: () => saveState(context), // passing false
              child: const Text('OK'),
            ),
            OutlinedButton(
              onPressed: () => Navigator.pop(context, false),
              // passing false
              child: const Text('Cancel'),
            ),
          ]),
        ]));
  }
}

But after I type a character in one of these two widgets, the cursor moves before the first character and the Android keyboard widget disappears. Why? And how to fix that bug?

I tried adding widget keys, but as you see it didn't help.

CodePudding user response:

There is a lot of things going wrong here, not only the stuff mentioned in the other answer.

  1. Move the setState in the builder into initState:

    if (category == null) {
      setState(() {
        category = widget.category ?? CategoryData(name: "", description: "");
      });
    }
    
  2. Don't use setState in the onChanged callback. Change:

    onChanged: (value) {
      setState(() {
        category!.description = value;
      });
    }
    

    to this:

    onChanged: (value) {
      category!.description = value;
    }
    
  3. Store the TextEditingControllers, because you have to dispose them once we dispose the state.

  4. If you are already using TextEditingControllers, then you don't need the onChanged callback. Just take text from the controller like explained in the other answer.

CodePudding user response:

You do not have to do

controller: TextEditingController(text: category!.name)

because the controller's text automatically changes once you connect it to TextField.

The reason is once you set some text to the controller, it re-applies the text thus moving the cursor to the front.

I have solved this for you :

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

class CategoryData {
  int? id;
  String name;
  String description;

  CategoryData({this.id, required this.name, required this.description});
}

class CategoriesEdit extends StatefulWidget {
  CategoryData? category;

  CategoriesEdit({required this.category});

  @override
  State<StatefulWidget> createState() => CategoriesEditState();
}

class CategoriesEditState extends State<CategoriesEdit> {
  CategoryData? category;
  // Database? db;
  TextEditingController nametextController =  TextEditingController();
  TextEditingController descriptionTextController = TextEditingController();
  void saveState(BuildContext context) {
    // ...
  }

  @override
  Widget build(BuildContext context) {
    if (category == null) {
      setState(() {
        category = widget.category ?? CategoryData(name: "", description: "");
      });


    }
    nametextController.text = category!.name??"";
    descriptionTextController.text = category!.description??"";



    return Scaffold(
        appBar: AppBar(
          leading: InkWell(
              child: const Icon(Icons.arrow_circle_left),
              onTap: () => Navigator.pop(context)),
          title: const Text("Edit Category"),
        ),
        body: Column(children: [
          Column(key: const Key('name'), children: [
            const Text("Category name:*"),
            TextField(
                controller: nametextController,
                onChanged: (value) {
                  setState(() {
                    category!.name = value;
                  });
                })
          ]),
          Column(key: const Key('description'), children: [
            const Text("Description:"),
            TextField(
                controller: descriptionTextController,
                onChanged: (value) {
                  setState(() {
                    category!.description = value;
                  });
                })
          ]),
          Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
            ElevatedButton(
              onPressed: () => saveState(context), // passing false
              child: const Text('OK'),
            ),
            OutlinedButton(
              onPressed: () => Navigator.pop(context, false),
              // passing false
              child: const Text('Cancel'),
            ),
          ]),
        ]));
  }
}

I have tested this code and it is working fine, let me know if you have any doubt. Hope this helps you.

  • Related