Home > Net >  Flutter keyboard stays open after changing screen
Flutter keyboard stays open after changing screen

Time:09-17

I have several TextFormField on a screen. If I tap one of the fields the keyboard opens as expected however if I then select a new screen from the Drawer menu the keyboard closes and as soon as the new screen finishes loading the keyboard automatically opens again. More than that if I type something the text field is updated in the background if I return to the screen with the TextFormField it shows the correct input. I would expect the screen/widget to be disposed of when navigating to another screen(widget) from the navigation menu, and I definitely should not be able to update the content of a widget's text field while in another widget.

// Form Field

             Form(key: _constructionFormKey,
                  child: Column(children: [
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        const SizedBox(
                          width: 100,
                          child: Text(
                            'Homes',
                            style: regularBoldText,
                          ),
                        ),
                        SizedBox(
                          width: 75,
                          child: Text(
                            '${widget.tribe.homes} (${calculatePercent(widget.tribe.land, widget.tribe.homes)}%)',
                            style: regularText,
                          ),
                        ),
                        SizedBox(
                          height: 18,
                          width: MediaQuery.of(context).size.width / 3,
                          child: TextFormField(
                            autovalidateMode:
                                AutovalidateMode.onUserInteraction,
                            onChanged: (String? newValue) {
                              if (newValue != null && isNumber(newValue)) {
                                setState(() {
                                  buildHomes = int.parse(newValue);
                                });
                                // Requiered or variable will not clear properly
                                // when the user deletes input content
                              } else if (newValue == null || newValue.isEmpty) {
                                setState(() {
                                  buildHomes = 0;
                                });
                              }
                            },
                            inputFormatters: <TextInputFormatter>[
                              FilteringTextInputFormatter.digitsOnly
                            ],
                            style: const TextStyle(fontSize: 10),
                            decoration: const InputDecoration(
                                border: OutlineInputBorder()),
                            keyboardType: TextInputType.number,   
                          ),
                        ),
                      ],
                    ),));

// Home Screen where I have the navigation logic.

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

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  //! Default to tribe overview screen - 1 -, 0 is mail screen
  int _drawerNavIndex = 3;

  /// [setSelectedTab] will update the current screen based on the tapped option
  /// from [DrawerContentWidget]
  void setSelectedTab(index) {
    // if the [_drawerNavIndex] is not the same as [index] update it to [index]
    // value
    if (_drawerNavIndex != index) {
      setState(() {
        _drawerNavIndex = index;
      });
    }
  }

  /// [selectedTabContent] will return the screen selected from the
  /// [DrawerContentWidget] based on [_drawerNavIndex]
  Widget selectedTabContent() {
    List<Widget> pages = [
      // Tribe Screens
      const TribeMailScreen(),
      const TribeHomeScreen(),
      const TribeAdvisorScreen(),
      const ConstructionScreen()

      // Alliance
    ];
    return IndexedStack(
      index: _drawerNavIndex,
      children: pages,
    );
  }

  @override
  Widget build(BuildContext context) {
    TribeSummary tribe = Provider.of<TribeSummary>(context, listen: true);

    // If the tribe uid value is `placeHolderTribe` assume that there is no
    // existing or active tribe for this account
    if (tribe.uid == 'placeHolderTribe') {
      return Scaffold(
        /// TODO: create a proper drawer or appBar for the [StartTribeWidget]
        appBar: AppBar(
          title: const Text('Orkfia'),
        ),
        body: const StartTribeWidget(),
      );

      // If the tribe `uid` value is `placeHolderTribe` assume that an error
      // occurred while trying to get the tribe stream or while the tribe stream
      // is parsed to [TribeSummary], log should give more information
    } else if (tribe.uid == 'placeHolderErrorTribe') {
      // TODO: create a bettter error screen for this situation
      return const Center(
        child: Text('Unable to retrieve tribe data'),
      );
    }

    // This Scaffold wraps the entire app, anything here will be avilable
    // globally
    return Scaffold(
      // App Bar
      appBar: const AppBarContent(),

      // [DrawerContentWidget] holds all the drawer content, it requires
      // [selectedTab] function to handle the navigation between screens
      drawer: DrawerContentWidget(
        setSelectedTab: setSelectedTab,
        selectedTabIndex: _drawerNavIndex,
      ),

      // Display the contents of the selected screen
      body: selectedTabContent(),

      // Reserved
      bottomNavigationBar: SizedBox(
          height: 50,
          child: Container(
            color: Colors.red[100],
            child: const Center(child: Text('Reserved space')),
          )),
    );
  }
}

CodePudding user response:

Use TextEditingController for every TextFormField to solve this problem.

A controller for an editable text field.

  • First Whenever the user modifies a text field with an associated TextEditingController, the text field updates value and the controller notifies its listeners. Listeners can then read the text and selection properties to learn what the user has typed or how the selection has been updated.
  • Second, remember to dispose of the TextEditingController inside dispose() when it is no longer needed. This will ensure we discard any resources used by the object.

To close keyboard from screen
you can use GesterDetector widget.

FocusManager.instance.primaryFocus?.unfocus();

or use can below for hot fix

FocusScope.of(context).unfocus();

Example is given below

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

  @override
  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}

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

  @override
  void initState() {
    super.initState();
    _controller.addListener(() {
      final String text = _controller.text.toLowerCase();
      _controller.value = _controller.value.copyWith(
        text: text,
        selection:
            TextSelection(baseOffset: text.length, extentOffset: text.length),
        composing: TextRange.empty,
      );
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        alignment: Alignment.center,
        padding: const EdgeInsets.all(6),
        child: TextFormField(
          controller: _controller,
          decoration: const InputDecoration(border: OutlineInputBorder()),
        ),
      ),
    );
  }
}

CodePudding user response:

When you navigate to a new page, you are not really disposing of the previous page. The new page is simply added on top of the previous page. You could try wrapping the entire scaffold in a GestureDetector with the following onTap function:

FocusScope.of(context).unfocus();

This will make sure the keyboard is dismissed when you push a new page with user taps.

  • Related