Home > Software engineering >  How to update a form after a field is changed
How to update a form after a field is changed

Time:04-20

I've got a form and I want to re-do a calculation when the user finishes editing any of the fields. I thought onFieldSubmitted would do it but this never gets called. In the minimal example below I want the recalc() function to be called when the user moves off the field. I know onChange will work but I don't want the recalculation to be done every time the user enters a character as it is a bit slow and it's irritating, in my opinion, to see validation errors while still composing the fields contents.

I'm using Flutter 2.10.4 with the "Edge (web-javascript)" device.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(title: 'Form demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: const Center(child: MyForm()),
    );
  }
}

class MyForm extends StatefulWidget {
  const MyForm({Key? key}) : super(key: key);
  @override
  State<MyForm> createState() => _MyFormState();
}

class _MyFormState extends State<MyForm> {
  int _q = 0;
  void recalc() {
    print("Recalculate");
    setState(() {
      _q  = 1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Form(
        child: Column(
      children: [
        TextFormField(
          onFieldSubmitted: (value) {
            recalc();
          },
        ),
        TextFormField(
          onFieldSubmitted: (value) {
            recalc();
          },
        ),
        Text('changed $_q')
      ],
    ));
  }
}

EDIT: I realise now that onFieldSubmitted gets fired if I press ENTER on a field, not when I tab away from it. I'm using the web-javascript device on a PC so I do have an ENTER key - not sure how this would work if I were using the Android emulator

CodePudding user response:

You need a TextEditingController to controll value of TextField. see more details at this post.

In case you want to detect user unfocus the field without submit, u can use FocusNode to take control of them

final controller1 = TextEditingController(text: defaultValue);
final controller2 = TextEditingController(text: defaultValue);

int _q = 0;
void recalc() {
  print("Recalculate");
  setState(() {
    _q  = 1;
  });
  controller1.text = defaultValue;
  controller2.text = defaultValue;
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
      children: [
        TextFormField(
          controller: controller1,
          onFieldSubmitted: (value) {
            recalc();
          },
        ),
        TextFormField(
          controller: controller2,
          onFieldSubmitted: (value) {
            recalc();
          },
        ),
        Text('changed $_q')
      ],
    ),
  );
}

CodePudding user response:

if I understand it correctly you need a focusNode for that

first step create focusNode

FocusNode focusNode = FocusNode();
TextEditingController firstController = TextEditingController();

second step send to textformfield

   TextFormField(
      focusNode: focusNode,
      controller: firstController,

    ),

and addlistener in initstate

 void initState() {
     focusNode.addListener(() {
      if(!focusNode.hasFocus){
        print(firstController.text); 
        recalc();
      }
    });
    super.initState();
  }

Dont forget dispose

  @override
  void dispose() {
  
     focusNode.dispose();

    super.dispose();
  }

Full code:

import 'package:flutter/material.dart';
import 'package:googleapis/storage/v1.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(title: 'Form demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: const Center(child: MyForm()),
    );
  }
}

class MyForm extends StatefulWidget {
  const MyForm({Key? key}) : super(key: key);
  @override
  State<MyForm> createState() => _MyFormState();
}

class _MyFormState extends State<MyForm> {
  int _q = 0;
  void recalc() {
    print("Recalculate");
    setState(() {
      _q  = 1;
    });
  }
  //TODO: create focusnode and textcontroller 
  FocusNode focusNode = FocusNode();
  TextEditingController firstController = TextEditingController();

  @override
  void initState() {
  //TODO: create a listener
     focusNode.addListener(() {
      if(!focusNode.hasFocus){
        //TODO: You can write any function

       //TODO: you can take the text
        print(firstController.text); 

        recalc();
      }
    });
    super.initState();
  }
  @override
  void dispose() {
    // TODO: Dont forget to dispose
     focusNode.dispose();

    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return Form(
        child: Column(
      children: [
        TextFormField(
          focusNode: focusNode,
          controller: firstController,
         
        ),
        TextFormField(
         //You can do for here
        ),
        Text('changed $_q')
      ],
    ));
  }
}

hopefully it benefits

CodePudding user response:

You Can try this way there is no other way the user can to other textfield it seems!!

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(title: 'Form demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: const Center(child: MyForm()),
    );
  }
}

class MyForm extends StatefulWidget {
  const MyForm({Key? key}) : super(key: key);
  @override
  State<MyForm> createState() => _MyFormState();
}

class _MyFormState extends State<MyForm> {
  int _q = 0;
  void recalc() {
    print("Recalculate");
    setState(() {
      _q  = 1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Form(
        child: Column(
          children: [
            TextFormField(
              onEditingComplete: ( ){
                recalc();
              },
            ),
            TextFormField(
              onEditingComplete: ( ){
                recalc();
              },
            ),
            Text('changed $_q')
          ],
        ));
  }
}
  • Related