如何将ChangeNotifier
与SearchDelegate
一起正确使用?
我有类似这样的东西:
class SearchNotifier with ChangeNotifier {
List<String> results;
Future<void> search(String query) async {
results = await API.search(query);
notifyListeners();
}
}
在我的SearchDelegate
中:
Widget buildSuggestions(BuildContext context) {
final searchNotifier = Provider.of<SearchNotifier>(context);
searchNotifier.search(query);
...
}
结果更新时,SearchNotifier
更新其侦听器,重建SearchDelegate
,调用buildSuggestions
,并再次调用search
,进入循环。
有没有一种在构建方法之外进行searchNotifier.search(query)
的方法?也许我可以通过某种方式将监听器添加到SearchDelegate
_queryTextController
?
我正在使用提供程序注入我的SearchNotifier
,因此无论在哪里调用search
,我们都需要访问上下文。
答案 0 :(得分:1)
我通过直接从search
方法返回搜索结果,然后使用FutureBuilder
来解决了这个问题。
class SearchNotifier with ChangeNotifier {
Future<List<String>> search(String query) async {
return await API.search(query);
}
}
Widget buildSuggestions(BuildContext context) {
final searchNotifier = Provider.of<SearchNotifier>(context);
results = searchNotifier.search(query);
// FutureBuilder on results
...
}
SearchNotifier
不再是ChangeNotifier
,因为问题的根源是notifyListeners
。
答案 1 :(得分:0)
如果您想使用它TextEditingController
来在输入新内容(完全有意义)时开始一个新查询,则可以将以下内容添加到小部件的initState
中(假设您在这里使用有状态小部件。
@override
void initState() {
final searchNotifier = Provider.of<SearchNotifier>(context);
_myTextEditingController.addListener(() => searchNotifier.search(_myTextEditingController.text));
}
并且不要忘记从build()
方法中将其删除。
答案 2 :(得分:0)
我做了不同的选择,但我也在我的应用程序中使用了提供程序。所以我的自定义搜索委托看起来像这样:
class ExerciseSearchDelegate extends SearchDelegate {
@override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(Icons.clear),
onPressed: () {
query = '';
},
),
];
}
@override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
close(context, null);
},
);
}
@override
Widget buildResults(BuildContext context) {
// ExeciseList is a widget which accepts a query as optional parameter
return ExerciseList(
query: query,
);
}
@override
Widget buildSuggestions(BuildContext context) {
return Column();
}
@override
ThemeData appBarTheme(BuildContext context) {
assert(context != null);
final ThemeData theme = Theme.of(context);
assert(theme != null);
return theme;
}
}
和我的ExerciseList看起来像这样:
class ExerciseList extends StatelessWidget {
final String query;
const ExerciseList({this.query, Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
final _model = Provider.of<GlobalModel>(context);
List<ExerciseModel> _results = query != null
? _model.exercises
.where((ex) => ex.name.toLowerCase().contains(query.toLowerCase()))
.toList()
: _model.exercises;
return ListView.builder(
itemCount: _results.length,
itemBuilder: (context, position) {
var exercise = _results[position];
return MultiProvider(
providers: [
ChangeNotifierProvider.value(notifier: exercise),
],
child: ExerciseListItem(),
);
},
);
}
}
希望这会有所帮助。如果不清楚,请告诉我。谢谢!