【发布时间】:2020-01-10 17:31:23
【问题描述】:
我正在尝试使用 Google Maps 插件编写一个简单的 Flutter 应用程序。我需要使用多个 BLoC/ChangeNotifier 对象来管理屏幕上显示的对象。
当我在 ChangeNotifier 上调用 notifyListeners() 时,问题就出现了。调用notifyListeners()的方法完成执行,然后应用程序完全冻结(没有小部件更新,无法与现有小部件交互)。
我试图了解问题出在哪里:我唯一了解的是它可以正常工作,而 CompaniesData(这是导致问题的 ChangeNotifier)为空。
class CompaniesData extends ChangeNotifier {
Map<MarkerId, Company> _companiesMap;
set companies(Set<Company> companies) {
_companiesMap = companies != null
? Map.fromIterable(
companies,
key: (company) => MarkerId(company.id.toString()),
value: (company) => company,
)
: null;
notifyListeners();
;
}
bool get available => _companiesMap != null;
Company companyWithId(MarkerId id) => available ? _companiesMap[id] : null;
Map<MarkerId, Company> get companiesIfAvailable =>
available ? _companiesMap : Map();
Iterable<Company> companiesFromIds(BuildContext context, Set<int> ids) {
Set<int> idsCopy = Set.from(ids);
return companiesIfAvailable.entries
.where((entry) => idsCopy.remove(entry.value.id))
.map<Company>((entry) => entry.value);
}
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Consumer<CompaniesData>(
builder: (context, data, child) {
return BlocBuilder(
bloc: BlocProvider.of<ShownCompaniesBloc>(context),
builder: (context, shownCompaniesState) {
return BlocBuilder(
bloc: BlocProvider.of<FavoriteCompaniesBloc>(context),
builder: (context, favoriteCompaniesState) {
return BlocBuilder(
bloc: BlocProvider.of<MapPropertiesBloc>(context),
builder: (context, mapPropertiesState) {
CompaniesData data =
Provider.of<CompaniesData>(context, listen: false);
// ...
如您所见,build 方法包含多个嵌套的 BLoC/Consumer 对象。
@override
void initState() {
_fetchCompanies();
super.initState();
}
void _fetchCompanies() {
findUser().then((location) {
Set<Company> companies = Set.from([Company.fake()]);
// CompaniesData.companies is a setter, which calls
// notifyListeners
_companiesData.companies = companies;
});
}
我没有收到错误消息,异常,我的应用程序在执行给findUser().then() 的回调结束后就死了。
编辑:
我稍微更改了代码,我发现问题不是notifyListeners(或者至少现在不是)。
final Completer<Map<MarkerId, Company>> _companiesData = Completer();
_AeroMainViewState() {
findUser()
.then(_fetchCompanies)
.then((companies) => _companiesData.complete(Map.fromIterable(
companies,
key: (company) => MarkerId(company.id.toString()),
value: (company) => company,
)));
}
Future<Set<Company>> _fetchCompanies(LatLng location) async =>
Set.from([Company.fake()]);
// ...
child: FutureBuilder<Map<MarkerId, Company>>(
future: _companiesData.future,
builder: (context, snapshot) {
// this builder function isn't called at all
// when the Completer _companiesData is completed
if (snapshot.connectionState == ConnectionState.done) {
return Provider<Map<MarkerId, Company>>.value(
value: snapshot.data,
child: // ...
} else {
return Center(child: CircularProgressIndicator());
}
}),
// ...
删除 ChangeNotifier 并不能解决问题。
【问题讨论】:
-
出于好奇,你为什么使用
listen: false来获取CompanyData? -
当我也使用
Consumer时,是否有理由将其设置为true?编辑:true -
啊,确实,我刚刚意识到你也使用了消费者(顺便说一句,这很奇怪。你不需要两者)
-
notifyListener是否正确完成,还是应用程序卡在那里? -
我读到
listen: true调用setState时发生变化,我认为它与StatelessWidget不兼容