【问题标题】:Setting provider value in FutureBuilder在 FutureBuilder 中设置提供者值
【发布时间】:2019-10-14 23:25:58
【问题描述】:

我有一个小部件向返回地图的 api 发出请求。我想做的不是每次加载小部件时都发出相同的请求并将列表保存到appState.myList。但。当我在FutureBuilder 中执行此操作appState.myList = snapshot.data; 时,出现以下错误:

flutter: ══╡ EXCEPTION CAUGHT BY FOUNDATION LIBRARY ╞════════════════════════════════════════════════════════
flutter: The following assertion was thrown while dispatching notifications for MySchedule:
flutter: setState() or markNeedsBuild() called during build.
flutter: This ChangeNotifierProvider<MySchedule> widget cannot be marked as needing to build because the
flutter: framework is already in the process of building widgets. A widget can be marked as needing to be
flutter: built during the build phase only if one of its ancestors is currently building. ...

sun.dart文件:

class Sun extends StatelessWidget {
  Widget build(BuildContext context) {
    final appState = Provider.of<MySchedule>(context);
    var db = PostDB();

    Widget listBuild(appState) {
      final list = appState.myList;
      return ListView.builder(
        itemCount: list.length,
        itemBuilder: (context, index) {
          return ListTile(title: Text(list[index].title));
        },
      );
    }

    Widget futureBuild(appState) {
      return FutureBuilder(
        future: db.getPosts(),
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          if (snapshot.hasData) {
            // appState.myList = snapshot.data;
            return ListView.builder(
              itemCount: snapshot.data.length,
              itemBuilder: (context, index) {
                return ListTile(title: Text(snapshot.data[index].title));
              },
            );
          } else if (snapshot.hasError) {
            return Text("${snapshot.error}");
          }
          return Center(
            child: CircularProgressIndicator(),
          );
        },
      );
    }

    return Scaffold(
        body: appState.myList != null
            ? listBuild(appState)
            : futureBuild(appState));
  }
}

postService.dart文件:

class PostDB {
  var isLoading = false;

  Future<List<Postmodel>> getPosts() async {
    isLoading = true;
    final response =
        await http.get("https://jsonplaceholder.typicode.com/posts");

    if (response.statusCode == 200) {
      isLoading = false;
      return (json.decode(response.body) as List)
          .map((data) => Postmodel.fromJson(data))
          .toList();
    } else {
      throw Exception('Failed to load posts');
    }
  }
}

我了解myList 调用notifyListeners(),这就是导致错误的原因。希望我做对了。如果是这样,我如何设置appState.myList并在应用程序中使用而不会出现上述错误?

import 'package:flutter/foundation.dart';
import 'package:myflutter/models/post-model.dart';

class MySchedule with ChangeNotifier {
  List<Postmodel> _myList;

  List<Postmodel> get myList => _myList;

  set myList(List<Postmodel> newValue) {
    _myList = newValue;
    notifyListeners();
  }
}

【问题讨论】:

  • 你在哪里使用myList的setter?
  • 在 FutureBuilder ... if (snapshot.hasData) { // appState.myList = snapshot.data;
  • 为什么不在getPosts里面?
  • 是的......那会更好......但我可以在里面有final appState = Provider.of&lt;MySchedule&gt;(context);吗?我没有上下文......我从来没有真正理解过context 的作用。我是 Flutter 的新手。请发布答案。
  • 我不知道,我没有Provider类的经验

标签: flutter dart flutter-provider


【解决方案1】:

出现该异常是因为您正在从其后代同步修改一个小部件。

这很糟糕,因为它可能导致小部件树不一致。一些小部件。可以使用突变前的值构建小部件,而其他小部件可能正在使用突变值。

解决方案是消除不一致。使用ChangeNotifierProvider,通常有两种情况:

  • 在您的ChangeNotifier 上执行的突变总是在与创建您的ChangeNotifier 的版本相同的build 中完成。

    在这种情况下,您可以直接从 ChangeNotifier 的构造函数中调用:

    class MyNotifier with ChangeNotifier {
      MyNotifier() {
        // TODO: start some request
      }
    }
    
  • 执行的更改可能会“延迟”发生(通常在更改页面之后)。

    在这种情况下,您应该将您的突变包装在 addPostFrameCallbackFuture.microtask 中:

    class Example extends StatefulWidget {
      @override
      _ExampleState createState() => _ExampleState();
    }
    
    class _ExampleState extends State<Example> {
      MyNotifier notifier;
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        final notifier = Provider.of<MyNotifier>(context);
    
        if (this.notifier != notifier) {
          this.notifier = notifier;
          Future.microtask(() => notifier.doSomeHttpCall());
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Container();
      }
    }
    

【讨论】:

  • 这个解决方案很好。我以为 OP 只想加载一次。在这种情况下,ChangeNotifier 似乎不合适,使用Future.microtask 来逃避来自build 限制的调用setState 似乎是一种黑客攻击。
【解决方案2】:

我在使用提供程序时遇到了与您类似的问题。我的解决方法是在获取数据的时候加上WidgetsBinding.instance.addPostFrameCallback()

【讨论】:

  • 请举个例子
【解决方案3】:

只需从代码中删除notifyListeners();。我遇到了这个错误,这就是我为解决问题所做的。

【讨论】:

  • 如何修复代码?通过删除代码!天才。
猜你喜欢
  • 2012-04-04
  • 2019-12-15
  • 2023-03-29
  • 2020-04-17
  • 1970-01-01
  • 2022-09-23
  • 2023-01-19
  • 1970-01-01
  • 2020-04-22
相关资源
最近更新 更多