Home > Mobile >  There is a problem that is rebuild when I click TextField() in Flutter
There is a problem that is rebuild when I click TextField() in Flutter

Time:08-13

My code includes FutureBuilder(), which get data from Firestore, and its child widgets include GridView.builder and TextField widgets etc.

When I click on a TexField(focus), the codes in FutureBuilder are rebuild.

The following is the test code for this.

Can you tell me the cause and solution of this problem?

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

  @override
  State<TestRoom> createState() => _TestRoomState();
}

class _TestRoomState extends State<TestRoom> {
  List<RoomModel> _roomModels = [];
  TextEditingController _textEditingController = TextEditingController();
  bool _isTablet = false;

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

  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;

    if (size.width >= 800) {
      _isTablet = true;
    } else {
      _isTablet = false;
    }

    return Scaffold(
      appBar: AppBar(),
      body: Padding(
        padding: EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text(
              "Test",
              style: TextStyle(
                fontSize: 35,
                fontWeight: FontWeight.bold,
              ),
            ),
            SizedBox(
              height: 20,
            ),
            FutureBuilder(
              future: _getAllRoom(),
              builder: (context, snapshot) {
                if (snapshot.connectionState == ConnectionState.waiting) {
                  return const Center(
                    child: CircularProgressIndicator(),
                  );
                }

                return Expanded(
                    child: TestList1(
                  isTablet: _isTablet,
                  roomModels: _roomModels,
                  isListStyle1: true,
                ));
              },
            ),
          ],
        ),
      ),
    );
  }

  // get user's models from firestore
  Future _getAllRoom() async {
    _roomModels.clear();
    _roomModels.addAll(await RoomService().getAllRoomModel("userName"));
  }
}

//
//

class TestList1 extends StatefulWidget {
  final isTablet;
  final List<RoomModel> roomModels;
  final bool isListStyle1;

  TestList1({
    Key? key,
    required this.isTablet,
    required this.roomModels,
    required this.isListStyle1,
  }) : super(key: key);

  @override
  State<TestList1> createState() => _TestList1State();
}

class _TestList1State extends State<TestList1> {
  TextEditingController _textEditingController = TextEditingController();
  double _paddingSize = 40.0;

  @override
  void dispose() {
    // _textEditingController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      shrinkWrap: true,
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: widget.isTablet ? 6 : 3,
        childAspectRatio: 1 / 1.5,
        mainAxisSpacing: _paddingSize,
        crossAxisSpacing: 10,
      ),
      itemBuilder: (context, index) {
        return _buildMyRooms(widget.roomModels[index], index);
      },
      itemCount: widget.roomModels.length,
    );
  }

  Widget _buildMyRooms(RoomModel roomModel, int index) {
    return Column(
      children: [
        InkWell(
          onTap: () {},
          child: Container(
            width: 120,
            height: 168,
            decoration: BoxDecoration(
              color: Colors.white,
              border: Border.all(
                color: Colors.blue,
                width: 2.0,
              ),
              borderRadius: const BorderRadius.all(
                Radius.circular(10),
              ),
            ),
          ),
        ),
        SizedBox(
          height: sm_padding,
        ),
        PopupMenuButton<int>(
          color: Colors.grey[100],
          itemBuilder: (context) => _fileMenuItemLust(roomModel),
          onSelected: _onSeletedFileMenu,
          child: Column(
            children: [
              Container(
                width: 130,
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Flexible(
                      child: Text(
                        roomModel.roomName,
                        overflow: TextOverflow.ellipsis,
                        maxLines: 1,
                        style: const TextStyle(
                          color: Colors.blue,
                          fontSize: 15,
                        ),
                      ),
                    ),
                    const Icon(
                      Icons.arrow_drop_down_outlined,
                      color: Colors.blue,
                    ),
                  ],
                ),
              ),
            ],
          ),
        )
      ],
    );
  }

  // 파일 메뉴 아이템
  List<PopupMenuEntry<int>> _fileMenuItemLust(RoomModel roomModel) {
    _textEditingController.text = roomModel.roomName;

    return [
      // 파일명
      PopupMenuItem(
        enabled: false,
        // TODO textfield
        child: TextField(
            controller: _textEditingController,
            style: const TextStyle(
              color: Colors.white,
              fontWeight: FontWeight.bold,
            ),
            maxLines: 1,
            decoration: InputDecoration(
              border: _textFieldBorder(),
              enabledBorder: _textFieldBorder(),
              disabledBorder: _textFieldBorder(),
              focusedBorder: _textFieldBorder(),
              focusColor: Colors.white60,
              filled: true,
              fillColor: Colors.grey.withOpacity(0.3),
              isDense: true, // padding 조절을 위해 추가
              contentPadding: EdgeInsets.all(sm_padding),
            )),
      ),
      const PopupMenuDivider(),
      PopupMenuItem(
        value: 0,
        child: Row(
          children: [
            const Icon(
              Icons.copy,
            ),
            SizedBox(
              width: sm_padding,
            ),
            const Text(
              "Menu Item1",
            ),
          ],
        ),
      ),
    ];
  }

  void _onSeletedFileMenu(value) {}

  OutlineInputBorder _textFieldBorder() {
    return OutlineInputBorder(
      borderRadius: BorderRadius.all(Radius.circular(20)),
      borderSide: BorderSide(
        color: Colors.grey.withOpacity(0.3),
        width: 1,
      ),
    );
  }
}

// ======================

I modified the code based on Yeasin Sheikh's answer. The existing problem has been solved, but a new problem has arisen.

I'm going to update the Firestore using onSubmitted() of TextField and then update my list using stream builder and future builder.

This is a modified part of the code above to solve an existing problem

class _TestRoomState extends State<TestRoom> {
  late final myFuture = _getAllRoom(); // new

        FutureBuilder(
          future: myFuture, //_getAllRoom(), // change

The problem of constantly rebuilding the text field according to the focus has disappeared, but there is a problem that getAllRoom() is called only at the first time and cannot be called afterwards, so the new room list cannot be updated.

CodePudding user response:

Here future: _getAllRoom() calls the api on every state changes.

Create a state variable for future

  late final myFuture  =  _getAllRoom();
  @override
  Widget build(BuildContext context) {

And use

FutureBuilder(
  future:myFuture  ,

You can check Fixing a common FutureBuilder and StreamBuilder problem

  • Related