When I open a dialog, the app autofocus the latest textfield

Viewed 31

I'm trying to create a Flutter app with the main focus as a PWA. I faced some problems like...

When user tap back button in an android device, we need to listen this action and unfocus the textfield, if you do not do this... keyboard will constantly appear.

  • I found that I just need to wrap in the main level the code below:

    Listener( onPointerDown: (_) { FocusScopeNode currentFocus = FocusScope.of(context); if (!currentFocus.hasPrimaryFocus) { print('unfocus'); currentFocus.focusedChild?.unfocus(); } }, child: MaterialApp(

And yeah! Everything apparently works properly but then... when a user first select a textfield, then tap a button how's contains a dialog, the keyboard appears for less than a second and, obviously, is very annoying.

I could reproduce my problem with the code below:

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

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

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

// This widget is the root of your application.
@OverRide
Widget build(BuildContext context) {
return Listener(
onPointerDown: (_) {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
print('unfocus');
currentFocus.focusedChild?.unfocus();
}
},
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
),
);
}
}

class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;

@OverRide
State createState() => _MyHomePageState();
}

class _MyHomePageState extends State {
@OverRide
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SingleChildScrollView(
child: Column(
children: [
FocusScope(
child: Focus(
onFocusChange: (focus) async {
print('$focus new');
},
child: const TextField())),
const SizedBox(
height: 10,
),
FocusScope(
child: Focus(
onFocusChange: (focus) async {
print(focus);
if (focus) {
final nowDate = DateTime.now();
final selectedDate = await showDatePicker(
context: context,
initialDate: nowDate,
firstDate: DateTime(1900),
lastDate: nowDate,
);
FocusManager.instance.primaryFocus?.unfocus();

                if (selectedDate != null) {}
              }
            },
            child: const TextField(
              readOnly: true,
            ),
          ),
        ),
        const SizedBox(
          height: 10,
        ),
        ElevatedButton(
          onPressed: () {
            FocusManager.instance.primaryFocus?.unfocus();
            showDialog(
              context: context,
              builder: (context) => CupertinoAlertDialog(
                actions: [
                  ElevatedButton(
                    onPressed: () => Navigator.pop(context),
                    child: const Text(
                      'Accept',
                    ),
                  ),
                ],
              ),
            );
          },
          child: const Text('Submitted'),
        )
      ],
    ),
  ),
);
}
}

If you want to reproduce my problem just select the textfield which shows the date picker, then press button you will see a dialog and on top of that widget another date time selector...

If you analyze the code, is the same behavior on a simple textfield, when you press the button, the focus comes back to the latest textfield selected... the only way that I found to avoid this problem is and old solution like:

ElevatedButton(
onPressed: () {
FocusScope.of(context).requestFocus(FocusNode());

            showDialog(
              context: context,
              builder: (context) => CupertinoAlertDialog(
                actions: [
                  ElevatedButton(
                    onPressed: () => Navigator.pop(context),
                    child: const Text(
                      'Accept',
                    ),
                  ),
                ],
              ),
            );
          },
          child: const Text('Submitted'),
        )

But sure, is not the optimal solution.... so then, as you can see, when a dialog is open, the latest textfield selected is autofocus and the result of that is a keyboard bug in flutter web mobile. I can not use third party packages because they do not support web.

Can you advise me what I'm doing wrong? Or how can I avoid this problem? Is this a flutter problem?

1 Answers

When user tap back button in an android device, if you do not do this... keyboard will constantly appears.

  • every time we leave the screen or page, make sure you dispose all controller. to avoid memory leak and also same with your issue, the textfield controller not disposed yet. thats why when you press back button, its still appear.
@override
  void dispose() {
    yourTextController.dispose();
    super.dispose();
  }
  • i wouldn't recommend you to wrap the MaterialApp with
Listener( onPointerDown: (_) { FocusScopeNode currentFocus = FocusScope.of(context);

which is: when you tap ANYWHERE in ANY SCREEN, you will execute focusNode. included when your screen didnt have any textfield. i think you can wrap only current screen.

Related