我来自Android开发,是Flutter开发的新手,我遇到了ScopedModel和State Management的Provider,并为我的用例选择了Provider。我的用例是登录后,仪表板应该用数据呈现。我创建了一个ViewModel来扩展ChangeNotifier,并希望使用它,以便可以在不同的小部件之间重用此数据。我不确定如何使用ViewModel加载小部件的数据。可以为我提供适当的设计模式吗?
abstract class ViewModel extends ChangeNotifier {
}
class EventViewModel extends ViewModel {
EventDataState _eventDataState = EventDataUnInitialized();
EventDataState get eventDataState => _eventDataState;
set eventDataState(EventDataState value) {
_eventDataState = value;
notifyListeners();
}
gatherEvents() {
eventDataState = EventDataLoading();
EventService.create().getEvents().then((data){
var responseData = json.decode(data.bodyString);
eventDataState = EventDataLoaded(
responseData.map((each) => new EventData.fromJson(each)).toList());
}).catchError((err){
eventDataState = EventDataLoadFailed("Failed to do something");
});
}
}
class DashboardPage extends StatefulWidget {
static const routename = "/dashboard";
@override
_DashboardPageState createState() => _DashboardPageState();
}
class _DashboardPageState extends State<DashboardPage> {
@override
Widget build(BuildContext context) {
return Consumer<EventViewModel>(
builder: (context, vm, child) {
if (vm.eventDataState is EventDataUnInitialized) {
vm.gatherEvents();
return Scaffold(body: Container(),);
}else if (vm.eventDataState is EventDataLoading) {
return Scaffold(body: Container(child: Center(child: CircularProgressIndicator(),),),);
}else if (vm.eventDataState is EventDataLoadFailed) {
return Scaffold(body: Container(child: Center(child: Text("Error Loading Data."),),),);
}else{
return Scaffold(body: Container(child: Center(child: Text("Data done loading..."),),),);
}
},
);
}
}
我收到的错误消息是:
I/flutter (30492): ══╡ EXCEPTION CAUGHT BY FOUNDATION LIBRARY ╞════════════════════════════════════════════════════════
I/flutter (30492): The following assertion was thrown while dispatching notifications for EventViewModel:
I/flutter (30492): setState() or markNeedsBuild() called during build.
I/flutter (30492): This ListenableProvider<EventViewModel> widget cannot be marked as needing to build because the
I/flutter (30492): framework is already in the process of building widgets. A widget can be marked as needing to be
I/flutter (30492): built during the build phase only if one of its ancestors is currently building. This exception is
I/flutter (30492): allowed because the framework builds parent widgets before children, which means a dirty descendant
I/flutter (30492): will always be built. Otherwise, the framework might not visit this widget during this build phase.
I/flutter (30492): The widget on which setState() or markNeedsBuild() was called was:
I/flutter (30492): ListenableProvider<EventViewModel>
I/flutter (30492): The widget which was currently being built when the offending call was made was:
I/flutter (30492): Consumer<EventViewModel>
答案 0 :(得分:0)
您可以在initState
生命周期中使用,请参见下面的示例:
initState() {
super.initState();
Provider.of<EventViewModel>(context, listen: false).gatherEvents;
}
答案 1 :(得分:0)
我正在使用get_it来提供服务定位器,以便您在注册后可以在任何地方访问视图模型。
并尝试FutureBuilder
用API构建小部件。
我不确定该错误是否会解决,也许您应该在collectEvents()方法中返回响应数据。
和代码:
/// * serviceLocator.dart
import 'package:get_it/get_it.dart';
GetIt serviceLocator = GetIt.instance;
void setupInfoServiceLocator() {
serviceLocator.registerLazySingleton(() => EventViewModel());
}
/// * main.dart or somewhere else
@override
void initState() {
setupInfoServiceLocator();
}
class _DashboardPageState extends State<DashboardPage> {
final EventViewModel _viewModel = serviceLocator<EventViewModel>();
Future<EventDataState> _gatherEvents;
@override
void initState() {
super.initState();
_gatherEvents = _viewModel.gatherEvents();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _gatherEvents,
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.active:
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator();
);
case ConnectionState.done:
if (snapshot.hasError) {
return Text('Error Loading Data.');
}
/// * there is at least three ways to access your data
final EventDataState data = _viewModel.eventDataState;
/// * or final EventDataState data = snapshot.data; if you return the response data
/// * or use Consumer<EventViewModel>(builder: (context, vm, child) => vm.eventDataState);
/// * use your data
return Text('Data done loading...');
default:
return Container();
}
},
);
}
}
答案 2 :(得分:0)
您可以使用SchedulerBinding阻止此异常:
替代1:
initState() {
super.initState();
final gatherEvents =
Provider.of<EventViewModel>(context, listen: false).gatherEvents;
SchedulerBinding.instance.addPostFrameCallback((_) => gatherEvents());
}
替代2:
initState() {
super.initState();
Future.microtask(() =>
Provider.of<EventViewModel>(context, listen: false).gatherEvents();
);
}