dart 关闭SearchDelegate时出现SetState()错误

4bbkushb  于 2023-06-19  发布在  其他
关注(0)|答案(3)|浏览(100)

我已经建立了一个卡片,它将打开一个searchDelegate,用户可以在其中搜索汽车。选择汽车时,代表屏幕将关闭onTap: () {close(context, carList[index].id);}。然后,ID将在主屏幕上更新并添加到列表中。
但是,我一直有错误setState() called after dispose()

List<String> selectedCar = [];
// Function to open SearchDelegate
void openCarSearchDelegate() async {
    String? selectedCarId = await showSearch(
    context: context,
      delegate: CompareCarSearchDelegate(_firebaseServices.carsRef),
    );
    print(selectedCarId) // <- Returns value when car is selected

    if (selectedCarId != null) {
      addSelectedCar(selectedCarId);
    }
}
// Update the state to add to the List
void addSelectedCar(String carId) {
    // ERROR occurs here
    setState(() {
      selectedCar.add(carId);
    });
}

@override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(20),
      child: SelectCarCard(
        onTap: openCarSearchDelegate,
      ),
    );
  }
class SelectCarCard extends StatelessWidget {
  const SelectCarCard({
    Key? key,
    required this.onTap,
  }) : super(key: key);

  final void Function() onTap;

  @override
  Widget build(BuildContext context) {
    return InkWell(
      borderRadius: const BorderRadius.all(Radius.circular(12)),
      onTap: onTap,
      child: Ink(
        height: 200,
        width: double.infinity,
        child: SizedBox(
          height: 200,
          width: double.infinity,
          child: const Text(
            "Select Car",
            style: TextStyle(
              color: Colors.black,
              fontSize: 18,
            ),
          ),
        ),
      ),
    );
  }
}
class CompareCarSearchDelegate extends SearchDelegate<String> {
  final Query searchQuery;

  CompareCarSearchDelegate(this.searchQuery);

  @override
  String get searchFieldLabel => 'Select car...';

  @override
  TextStyle get searchFieldStyle => const TextStyle(fontSize: 18);

  @override
  List<Widget> buildActions(BuildContext context) {
    // Clear field
  }

  @override
  Widget buildLeading(BuildContext context) {
    // Return to previous screen
  }

  @override
  Widget buildResults(BuildContext context) {
    return StreamBuilder<QuerySnapshot>(
      stream: searchQuery.snapshots(),
      builder: (context, snapshot) {

        if (snapshot.connectionState == ConnectionState.waiting) {
          return const CircularProgressIndicator(
            backgroundColor: Colors.white,
            color: kPrimaryColor,
            strokeWidth: 3,
          );
        }

        List<DocumentSnapshot> documents = snapshot.data!.docs;

        final carList = documents.where((doc) {
          // Get results based on search text
        }).toList();

        return ListView.separated(
          separatorBuilder: (context, index) {
            return const Divider();
          },
          shrinkWrap: true,
          padding:
              const EdgeInsets.only(top: 20, bottom: 20, left: 10, right: 10),
          itemCount: carList.length,
          itemBuilder: (context, index) {
            return ListTile(
              contentPadding: const EdgeInsets.only(right: 10),
              title: Text(
                carList[index]['name'],
                overflow: TextOverflow.ellipsis,
              ),
              onTap: () {
                close(context, carList[index].id);
              },
            );
          },
        );
      },
    );
  }

  @override
  Widget buildSuggestions(BuildContext context) {
    return Container();
  }
}
I/flutter (28352): setState() called after dispose(): _BodyState#27420(lifecycle state: defunct, not mounted)
I/flutter (28352): This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
I/flutter (28352): The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
I/flutter (28352): This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().

我尝试添加if(mounted) {setState(() {selectedCar.add(carId);});},但mounted一直返回false。
任何想法,我如何可以实现它或任何解决方案将不胜感激。谢谢

nlejzf6q

nlejzf6q1#

试试这个

void addSelectedCar(String carId) {
  if (mounted) {
    setState(() {
      selectedCar.add(carId);
    });
  }
}
4zcjmb1e

4zcjmb1e2#

你的close(context, carList[index].id);函数是异步的吗?如果是,则替换
onTap: () { close(context, carList[index].id)},

onTap: () async {await close(context, carList[index].id)},
如果不是,那么你的错误表明有一些异步调用负责调用setState()
不同的上下文也可能是一个问题。

void openCarSearchDelegate(BuildContext context) async {
    String? selectedCarId = await showSearch(
    context: context,
      delegate: CompareCarSearchDelegate(_firebaseServices.carsRef),
    );
    print(selectedCarId) // <- Returns value when car is selected

    if (selectedCarId != null) {
      addSelectedCar(selectedCarId);
    }
}

和/或

onTap:() { openCarSearchDelegate(context);},

编辑:

尝试在此区域安装。

if (selectedCarId != null && mounted) {
           addSelectedCar(selectedCarId);
    }

或者因为StreamBuilder<QuerySnapshot>,你的代码仍然处于aync状态,(可能这就是它从不显示mount的原因)。尝试用以下内容替换上面的内容。(如果上面的内容不起作用)

Future((){
  if (selectedCarId != null && mounted) {
     addSelectedCar(selectedCarId);
  }
});
d8tt03nd

d8tt03nd3#

一个简单的测试代码与类似的情况

import 'package:flutter/material.dart';

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String selectedText = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('My App'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Selected Text: $selectedText',
              style: TextStyle(fontSize: 20),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                showSearch(
                  context: context,
                  delegate: CustomSearchDelegate(),
                ).then((selectedValue) {
                  if (selectedValue != null) {
                    setState(() {
                      selectedText = selectedValue;
                    });
                  }
                });
              },
              child: Text('Select Text'),
            ),
          ],
        ),
      ),
    );
  }
}

class CustomSearchDelegate extends SearchDelegate<String> {
  final List<String> textList = ['Text 1', 'Text 2', 'Text 3'];

  @override
  List<Widget> buildActions(BuildContext context) {
    return [
      IconButton(
        onPressed: () {
          query = '';
        },
        icon: Icon(Icons.clear),
      ),
    ];
  }

  @override
  Widget buildLeading(BuildContext context) {
    return IconButton(
      onPressed: () {
        close(context, '');
      },
      icon: Icon(Icons.arrow_back),
    );
  }

  @override
  Widget buildResults(BuildContext context) {
    final List<String> filteredList = textList
        .where((text) => text.toLowerCase().contains(query.toLowerCase()))
        .toList();

    return ListView.builder(
      itemCount: filteredList.length,
      itemBuilder: (context, index) {
        final String item = filteredList[index];

        return ListTile(
          title: Text(item),
          onTap: () {
            close(context, item);
          },
        );
      },
    );
  }

  @override
  Widget buildSuggestions(BuildContext context) {
    final List<String> filteredList = textList
        .where((text) => text.toLowerCase().contains(query.toLowerCase()))
        .toList();

    return ListView.builder(
      itemCount: filteredList.length,
      itemBuilder: (context, index) {
        final String item = filteredList[index];

        return ListTile(
          title: Text(item),
          onTap: () {
            query = item;
            close(context, item);
          },
        );
      },
    );
  }
}

void main() {
  runApp(MaterialApp(
    home: MyHomePage(),
  ));
}

相关问题