Flutter - 如何使用 ChangeNotifier 正确获取数据
Flutter - how to fetch data correctly with ChangeNotifier
我想在应用运行后获取整个应用的天气统计数据。
我的ChangeNotifier
:
class WeatherStatsNotifier extends ChangeNotifier {
WeatherService weatherService;
Future<WeatherStats> _stats;
WeatherStatsNotifier({this.weatherService});
Future<WeatherStats> get stats => _stats;
void refreshStats() {
_stats = weatherService.fetchWeatherStats();
notifyListeners(); // it works when I remove this line
}
}
我的主文件:
final WeatherService weatherService = new WeatherService();
void main() => runApp(ChangeNotifierProvider(
create: (context) => WeatherStatsNotifier(weatherService: weatherService),
child: App()));
class App extends StatefulWidget {
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
@override
void didChangeDependencies() {
Provider.of<WeatherStatsNotifier>(context, listen: false).refreshStats();
super.didChangeDependencies();
}
//... build method
}
还有更深的地方:
@override
Widget build(BuildContext context) {
return Consumer<WeatherStatsNotifier>(
builder: (context, value, child) {
return FutureBuilder<WeatherStats>(
future: value.stats,
builder: (context, snapshot) {
return _buildForSnapshot(snapshot);
},
);
},
);
}
最后我得到一个错误:
======== Exception caught by widgets library =======================================================
The following assertion was thrown building ChangeNotifierProvider<WeatherStatsNotifier>(message: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 4195 pos 12: '!_dirty': is not true.\nSee also: https://flutter.dev/docs/testing/errors, dirty, renderObject: RenderErrorBox#c84e8 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE DETACHED):
'package:flutter/src/widgets/framework.dart': Failed assertion: line 4195 pos 12: '!_dirty': is not true.
如何正确操作?我可以在 initState
或 didChangeDependencies
中使用 context
吗?
这是我如何使用值通知程序获取数据的最佳实践
- 使用类型数据
定义值通知器
ex:
class _AppState extends State<App> {
ValueNotifier<String> _weatherStatsNotifier;
- 添加到默认值的初始状态(以处理空值)是的,如果需要,您也可以使用上下文
@override
void initState() {
super.initState();
_weatherStatsNotifier = ValueNotifier(emtpyString);
- 然后,如果数据已经获取,则更改值
_weatherStatsNotifier.value = weatherService;
或者在我的例子中,我通常使用来自监听状态的值通知器
_weatherBloc.listen(
(state) {
if (state is weatherLoadedState) {
_weatherStatsNotifier.value = state.weather;
}
},
);
4.Use 作为数据 ex String
的 return 的数据
ValueListenableBuilder<String>(
valueListenable: _weatherStatsNotifier,
builder: (context, value, _) => NextScreen(
Weather: value,
),
),
考虑将您的方法包装在 SchedulerBinding.instance.addPostFrameCallback
中,以便在下一帧调用该方法,而不仅仅是在重建小部件时调用该方法
class _AppState extends State<App> {
@override
void didChangeDependencies() {
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
Provider.of<WeatherStatsNotifier>(context, listen: false).refreshStats();
}
super.didChangeDependencies();
}
});
当依赖项发生变化时,即 WeatherStatsNotifier,didChangeDependencies
被调用。小部件已经被认为是脏的并且正在构建(可以有多个侦听器)。
然而,您的 refreshStats
正在同步 通知监听器 这意味着应该重新构建监听器,但它们已经在构建了。这可能会导致框架不一致,所以这就是错误的原因。这也是为什么删除此 notifyListeners();
是“解决”问题的原因。
修复它的方法是通过 addPostFrameCallback
或 Future.microtask
执行异步更改。
@override
void didChangeDependencies() {
super.didChangeDependencies();
Future.microtask(() {
Provider.of<WeatherStatsNotifier>(context, listen: false).refreshStats();
});
}
请注意,如果您有多个依赖项,您可能应该检查 didChangeDependencies
中的实例是否已更改。
我想在应用运行后获取整个应用的天气统计数据。
我的ChangeNotifier
:
class WeatherStatsNotifier extends ChangeNotifier {
WeatherService weatherService;
Future<WeatherStats> _stats;
WeatherStatsNotifier({this.weatherService});
Future<WeatherStats> get stats => _stats;
void refreshStats() {
_stats = weatherService.fetchWeatherStats();
notifyListeners(); // it works when I remove this line
}
}
我的主文件:
final WeatherService weatherService = new WeatherService();
void main() => runApp(ChangeNotifierProvider(
create: (context) => WeatherStatsNotifier(weatherService: weatherService),
child: App()));
class App extends StatefulWidget {
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
@override
void didChangeDependencies() {
Provider.of<WeatherStatsNotifier>(context, listen: false).refreshStats();
super.didChangeDependencies();
}
//... build method
}
还有更深的地方:
@override
Widget build(BuildContext context) {
return Consumer<WeatherStatsNotifier>(
builder: (context, value, child) {
return FutureBuilder<WeatherStats>(
future: value.stats,
builder: (context, snapshot) {
return _buildForSnapshot(snapshot);
},
);
},
);
}
最后我得到一个错误:
======== Exception caught by widgets library =======================================================
The following assertion was thrown building ChangeNotifierProvider<WeatherStatsNotifier>(message: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 4195 pos 12: '!_dirty': is not true.\nSee also: https://flutter.dev/docs/testing/errors, dirty, renderObject: RenderErrorBox#c84e8 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE DETACHED):
'package:flutter/src/widgets/framework.dart': Failed assertion: line 4195 pos 12: '!_dirty': is not true.
如何正确操作?我可以在 initState
或 didChangeDependencies
中使用 context
吗?
这是我如何使用值通知程序获取数据的最佳实践
- 使用类型数据 定义值通知器
ex:
class _AppState extends State<App> {
ValueNotifier<String> _weatherStatsNotifier;
- 添加到默认值的初始状态(以处理空值)是的,如果需要,您也可以使用上下文
@override
void initState() {
super.initState();
_weatherStatsNotifier = ValueNotifier(emtpyString);
- 然后,如果数据已经获取,则更改值
_weatherStatsNotifier.value = weatherService;
或者在我的例子中,我通常使用来自监听状态的值通知器
_weatherBloc.listen(
(state) {
if (state is weatherLoadedState) {
_weatherStatsNotifier.value = state.weather;
}
},
);
4.Use 作为数据 ex String
的 return 的数据ValueListenableBuilder<String>(
valueListenable: _weatherStatsNotifier,
builder: (context, value, _) => NextScreen(
Weather: value,
),
),
考虑将您的方法包装在 SchedulerBinding.instance.addPostFrameCallback
中,以便在下一帧调用该方法,而不仅仅是在重建小部件时调用该方法
class _AppState extends State<App> {
@override
void didChangeDependencies() {
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
Provider.of<WeatherStatsNotifier>(context, listen: false).refreshStats();
}
super.didChangeDependencies();
}
});
当依赖项发生变化时,即 WeatherStatsNotifier,didChangeDependencies
被调用。小部件已经被认为是脏的并且正在构建(可以有多个侦听器)。
然而,您的 refreshStats
正在同步 通知监听器 这意味着应该重新构建监听器,但它们已经在构建了。这可能会导致框架不一致,所以这就是错误的原因。这也是为什么删除此 notifyListeners();
是“解决”问题的原因。
修复它的方法是通过 addPostFrameCallback
或 Future.microtask
执行异步更改。
@override
void didChangeDependencies() {
super.didChangeDependencies();
Future.microtask(() {
Provider.of<WeatherStatsNotifier>(context, listen: false).refreshStats();
});
}
请注意,如果您有多个依赖项,您可能应该检查 didChangeDependencies
中的实例是否已更改。