Home > Software design >  Flutter Text field not updating with setState
Flutter Text field not updating with setState

Time:06-16

I have seen other responses to questions similar to this, but none of those solutions have worked for me. I am trying to update a text field with a date that has been selected after closing a showDatePicker window, but the Text does not update? I'm not certain why the setState() call does not update the Text. Printing to the console shows that the variable is updated, but the Text does not change. I initially used a String variable to hold the Text information and then changed it to a TextEditingController but that did not help either. Is the display immutable, and if so, is there a way to update the Text after the date has been selected?

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

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

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

@override
Widget build(BuildContext context) {
  return MaterialApp(
    title: 'Date Test',
    theme: ThemeData(
      primarySwatch: Colors.blue,
    ),
    home: const PickDate(),
   );
 }
}

class PickDate extends StatefulWidget {
  const PickDate({Key? key}) : super(key: key);

  @override
  State<PickDate> createState() => _PickDateState();
}

class _PickDateState extends State<PickDate> {
  DateTime selectedDate = DateTime.now();
  final DateFormat formatter = DateFormat('MM/dd/yy');
  var dateController = TextEditingController();

  @override
  void initState() {
    super.initState();
    dateController.text = formatter.format(selectedDate);
  }

  Future<void> _selectDate(BuildContext context) async {
    final DateTime? picked = await showDatePicker(
        context: context,
        initialDate: selectedDate,
        firstDate: DateTime(2022, 5),
        lastDate: DateTime(2023));
    if (picked != null && picked != selectedDate) {
      setState(() {
        selectedDate = picked;
        dateController.text = formatter.format(selectedDate);
        print('Select Date: ${dateController.text}');
      });
    }
  }

  Future<String?> displayDialog(BuildContext context, String title) async {
    Widget dialogWithTextField(BuildContext context) => Container(
          height: 300,
          decoration: const BoxDecoration(
            color: Colors.white,
            shape: BoxShape.rectangle,
            borderRadius: BorderRadius.all(Radius.circular(12)),
          ),
          child: Column(
            children: <Widget>[
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: <Widget>[
                  Text(dateController.text),
                  ElevatedButton.icon(
                      onPressed: () async {
                        await _selectDate(context);
                        setState(() {
                          dateController.text = formatter.format(selectedDate);
                          print(dateController.text);
                        });
                      },
                      icon: const Icon(Icons.date_range_sharp),
                      label: const Text('Pick Date')),
                ],
              ),
              Row(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  TextButton(
                    onPressed: () {
                      Navigator.of(context).pop('x');
                    },
                    child: const Text(
                      "Cancel",
                    ),
                  ),
                  ElevatedButton(
                    style: ButtonStyle(
                      backgroundColor:
                          MaterialStateProperty.all(Colors.lightBlue),
                    ),
                    child: Text(
                      "Save".toUpperCase(),
                    ),
                    onPressed: () {
                      Navigator.of(context).pop('');
                    },
                  )
                ],
              ),
            ],
          ),
        );

    return showDialog(
        context: context,
        builder: (BuildContext context) {
          return Dialog(
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(50),
            ),
            elevation: 6,
            backgroundColor: Colors.transparent,
            child: dialogWithTextField(context),
           );
        });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Test'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
                 onPressed: () {
                   displayDialog(context, 'New Date');
                },
                child: const Text('New Date')),
           ],
        ),
      ),
    );
  }
}

CodePudding user response:

The widget displayed by showDialog is not the widget you are using to call showDialog, therefore calling setState in your widget will not cause the already mounted widget to be rebuilt. In order to make this work you can extract your dialog into a separate StatefulWidget. Here's a full example based on your code:

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Date Test',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const PickDate(),
    );
  }
}

class MyDialog extends StatefulWidget {
  final TextEditingController dateController;
  const MyDialog({required this.dateController, Key? key}) : super(key: key);

  @override
  State<MyDialog> createState() => _MyDialogState();
}

class _MyDialogState extends State<MyDialog> {
  DateTime selectedDate = DateTime.now();
  final DateFormat formatter = DateFormat('MM/dd/yy');

  Future<void> _selectDate(BuildContext context) async {
    final DateTime? picked = await showDatePicker(
        context: context, initialDate: selectedDate, firstDate: DateTime(2022, 5), lastDate: DateTime(2023));
    if (picked != null && picked != selectedDate) {
      setState(() {
        selectedDate = picked;
        widget.dateController.text = formatter.format(selectedDate);
        print('Select Date: ${widget.dateController.text}');
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 300,
      decoration: const BoxDecoration(
        color: Colors.white,
        shape: BoxShape.rectangle,
        borderRadius: BorderRadius.all(Radius.circular(12)),
      ),
      child: Column(
        children: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              Text(widget.dateController.text),
              ElevatedButton.icon(
                  onPressed: () async {
                    await _selectDate(context);
                    setState(() {
                      widget.dateController.text = formatter.format(selectedDate);
                      print(widget.dateController.text);
                    });
                  },
                  icon: const Icon(Icons.date_range_sharp),
                  label: const Text('Pick Date')),
            ],
          ),
          Row(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              TextButton(
                onPressed: () {
                  Navigator.of(context).pop('x');
                },
                child: const Text(
                  "Cancel",
                ),
              ),
              ElevatedButton(
                style: ButtonStyle(
                  backgroundColor: MaterialStateProperty.all(Colors.lightBlue),
                ),
                child: Text(
                  "Save".toUpperCase(),
                ),
                onPressed: () {
                  Navigator.of(context).pop('');
                },
              )
            ],
          ),
        ],
      ),
    );
  }
}

class PickDate extends StatefulWidget {
  const PickDate({Key? key}) : super(key: key);

  @override
  State<PickDate> createState() => _PickDateState();
}

class _PickDateState extends State<PickDate> {
  var dateController = TextEditingController();

  DateTime selectedDate = DateTime.now();
  final DateFormat formatter = DateFormat('MM/dd/yy');

  @override
  void initState() {
    super.initState();
    dateController.text = formatter.format(selectedDate);
  }

  Future<String?> displayDialog(BuildContext context, String title) async {
    return showDialog(
        context: context,
        builder: (BuildContext context) {
          return Dialog(
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(50),
            ),
            elevation: 6,
            backgroundColor: Colors.transparent,
            child: MyDialog(
              dateController: dateController,
            ),
          );
        });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Test'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
                onPressed: () {
                  displayDialog(context, 'New Date');
                },
                child: const Text('New Date')),
          ],
        ),
      ),
    );
  }
}
  • Related