使用提供程序在onLoad上获取数据

时间:2019-12-16 16:04:10

标签: flutter

我来自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>

3 个答案:

答案 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();
  );
}