Cshitiz Dhingra
Cshitiz Dhingra

Reputation: 175

Flutter Visibility Toggle not working as expected

Creating a Screen where I want to perform Flutter-FireBase Searching.But Visibility Toggle is not working as desired.
Desired Toggle Behaviour : When clicked on TextForm field , prefix icon and result card should be visible. Upon Clicking the Prefix Icon(Back Arrow) , Result List (Card) and the prefix icon itself should become invisible and TextField should unfocus .
Actual Behaviour : On clicking the prefix icon , Result set and prefix icon don't disappear , Prefix icon remains there and result set becomes invisible but occupies some space beneath the TextFormField

class AddAppointmentWidget extends StatefulWidget {
  @override
  _AddAppointmentWidgetState createState() => _AddAppointmentWidgetState();
}

class _AddAppointmentWidgetState extends State<AddAppointmentWidget> {
  bool searchbartapped = false;
  var queryResultSet = [];
  var tempSearchStore = [];

// Search Function
  initiateSearch(value) {
    //body
  }

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: [
        SizedBox(
          height: 15,
        ),
        Padding(
          padding: const EdgeInsets.all(18.0),
          child: Text('Search',
              style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
        ),
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: Row(
            children: [
              Expanded(
                flex: 5,
                child: TextFormField(
                    style: TextStyle(color: Color(0xff2a2a2a), fontSize: 18),
                    keyboardType: TextInputType.name,
                    onChanged: (value) {
                      initiateSearch(value);
                    },
                    onTap: () {
                      setState(() {
                        searchbartapped = true;
                      });
                    },
                    cursorColor: Color(0xff2a2a2a),
                    cursorWidth: 1.5,
                    decoration: InputDecoration(
                        hintText: "Search by Name",
                        prefixIcon: Visibility(
                          visible: searchbartapped,
                          child: IconButton(
                              icon: Icon(Icons.arrow_back),
                              color: Colors.black54,
                              onPressed: () {
                                setState(() {
                                  searchbartapped = !searchbartapped;
                                  queryResultSet = [];
                                  tempSearchStore = [];
                                });
                                FocusScope.of(context).unfocus();
                              }),
                        ),
                        )),
              ),
            ],
          ),
        ),
        Visibility(
          visible: searchbartapped,
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: ListView(
                padding: EdgeInsets.all(5.0),
                primary: false,
                shrinkWrap: true,
                children: tempSearchStore.map((element) {
                  print(element['name']);
                  return buildResult(context, element);
                }).toList()),
          ),
        ),
      ],
    );
  }
}

Note The buildResult widget is working perfectly fine. Problem is only with the visibilty toggle

Upvotes: 0

Views: 1191

Answers (1)

Lulupointu
Lulupointu

Reputation: 3584

The issue: When you tap the prefixIcon:

  1. onPressed is called, setting searchbartapped to false which is what you want.
  2. The onTap method of your TextFormField is also called (since prefixIcon is inside it), setting searchbartapped to true.

So what you want is to prevent the second event from happening. I tried to prevent the notification from bubbling up the tree but I couldn't. So what I ended up doing is a bit more manual but works just as well.

Solution: Add a variable (for example hideSearchTapped) which is set to true when the prefixIcon is called. Then when the onTap method of your TextFormField is called, check this variable:

  • If hideSearchTapped is true, set it to false
  • Else change searchbartapped as you did

Here is a working example:

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

void main() async {
  runApp(
    MaterialApp(
      home: Scaffold(
        body: new AddAppointmentWidget(),
      ),
    ),
  );
}

class AddAppointmentWidget extends StatefulWidget {
  @override
  _AddAppointmentWidgetState createState() => _AddAppointmentWidgetState();
}

class _AddAppointmentWidgetState extends State<AddAppointmentWidget> {
  bool searchbartapped = false;
  bool hideSearchTapped = false;
  var queryResultSet = [];
  var tempSearchStore = [];

// Search Function
  initiateSearch(value) {
    //body
  }

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: [
        SizedBox(
          height: 15,
        ),
        Padding(
          padding: const EdgeInsets.all(18.0),
          child: Text('Search', style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
        ),
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: Row(
            children: [
              Expanded(
                flex: 5,
                child: TextFormField(
                    style: TextStyle(color: Color(0xff2a2a2a), fontSize: 18),
                    keyboardType: TextInputType.name,
                    onChanged: (value) {
                      initiateSearch(value);
                    },
                    onTap: () {
                      setState(() {
                        if (hideSearchTapped) {
                          hideSearchTapped = false;
                        } else {
                          searchbartapped = true;
                        }
                      });
                    },
                    cursorColor: Color(0xff2a2a2a),
                    cursorWidth: 1.5,
                    decoration: InputDecoration(
                      hintText: "Search by Name",
                      prefixIcon: Visibility(
                        visible: searchbartapped,
                        child: IconButton(
                            icon: Icon(Icons.arrow_back),
                            color: Colors.black54,
                            onPressed: () {
                              hideSearchTapped = true;
                              searchbartapped = !searchbartapped;
                              queryResultSet = [];
                              tempSearchStore = [];
                              setState(() {
                              });
                              FocusScope.of(context).unfocus();
                              return true;
                            }),
                      ),
                    )),
              ),
            ],
          ),
        ),
        Visibility(
          visible: searchbartapped,
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: ListView(
                padding: EdgeInsets.all(5.0),
                primary: false,
                shrinkWrap: true,
                children: tempSearchStore.map((element) {
                  print(element['name']);
                }).toList()),
          ),
        ),
      ],
    );
  }
}

Note: you should use lowerCamelCase to name your variable. So searchbartapped would become searchBarTapped.

Upvotes: 1

Related Questions