使用提供者获取数据 onLoad

Posted

技术标签:

【中文标题】使用提供者获取数据 onLoad【英文标题】:Using provider in fetching data onLoad 【发布时间】:2019-12-16 16:04:10 【问题描述】:

我来自 android 开发,对 Flutter 开发非常陌生,我遇到过用于状态管理的 ScopedModel 和 Provider,我为我的用例选择了 Provider。我的用例是登录后,仪表板应该用数据呈现。我创建了一个扩展 ChangeNotifier 的 ViewModel,并希望使用它以便我可以在不同的小部件中重用这些数据。我不完全确定如何使用我的 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>

【问题讨论】:

或者请帮我一个合适的小部件,我应该在视图中使用 我怀疑删除 vm.gatherEvents() 至少可以消除错误。 【参考方案1】:

您可以使用 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();
  );

【讨论】:

我节省了很多时间。我一直在寻找同样的东西。【参考方案2】:

我正在使用get_it,它提供了一个服务定位器,以便您在注册后可以在任何地方访问您的视图模型。

并尝试FutureBuilder 使用 API 构建小部件。

我不确定错误是否会得到解决,也许你应该在gatherEvents()方法中返回你的响应数据。

和代码:

/// * 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();
        
      ,
    );
  


【讨论】:

【参考方案3】:

您可以在initState生命周期中使用,见下例:

initState() 
  super.initState();
  Provider.of<EventViewModel>(context, listen: false).gatherEvents;

【讨论】:

这不起作用,不幸的是,我得到了以下断言在构建 Builder 时被抛出:dependOnInheritedWidgetOfExactType>() 或 dependOnInheritedElement() 在 _PageState.initState() 完成之前被调用

以上是关于使用提供者获取数据 onLoad的主要内容,如果未能解决你的问题,请参考以下文章

wepy父组件onload中请求数据,更新.sync动态传值绑定的数据,子组件onload中获取不到值。

wepy父组件onload中请求数据,更新.sync动态传值绑定的数据,子组件onload中获取不到值。

小程序父子组件onLoad和Created之间的问题

使用api获取数据————小程序

记录一次异步导致导致res获取到的数据为空

小程序云开发获取不到数据库的记录