如何在Flutter中使用Provider正确获取API。

8

我目前正在解决一个问题,需要从API中获取数据并将其显示在我的小部件中。我遵循了一些Provider架构模式,其中您需要设置两次setState:

1- 获取数据时

2- 数据已经获取时

所以我目前处理的问题是,我的小部件抛出以下错误:

setState() 或 markNeedsBuild() 在 build 期间被调用。

我知道这个错误是因为在build期间调用了setState,但是...我如何在build期间获取我的api,然后将其显示到我的小部件中呢?下面是我的代码:

NewsPage.dart

    class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  SideBarWidget _sidebar;

  @override
  void initState() {
    Provider.of<HomeViewModel>(context, listen: false)
        .fetchUltimaNoticia(context); --> ****Data to get fetched****
    _sidebar = const SideBarWidget();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('INICIO'),
        centerTitle: true,
        automaticallyImplyLeading: false,
        leading: Builder(
          builder: (context) => IconButton(
            icon: const Icon(Icons.menu),
            onPressed: () => Scaffold.of(context).openDrawer(),
          ),
        ),
      ),
      drawer: _sidebar,
      body: FormNoticiaContainer(),
    );
  }
}

FormContainer()

Widget build(BuildContext context) {
    return _crearBodyNoticia(context);
  }

  Widget _crearBodyNoticia(context) {
    final homeVm = Provider.of<HomeViewModel>(context, listen: true);
    return homeVm.state == ViewState.Busy
        ? Center(child: CircularLoading())
        : Center(
            child: DataToShowWidget()

HomeViewModel.dart

    class HomeViewModel extends BaseModel {
  ////////
  //NOTICIAS
  ////////

  NoticiaDB _noticia;

  NoticiaDB get noticia => _noticia;

  set setNoticia(NoticiaDB noticia) {
    _noticia = noticia;
  }

  Future fetchUltimaNoticia(BuildContext context) async {
    setState(ViewState.Busy);

    var response = await noticiaProvider.obtenerNoticiaPublicada();

    setNoticia = response;

    setState(ViewState.Idle);
  }
}

基本模型

 ViewState _state = ViewState.Idle;

  ViewState get state => _state;

  void setState(ViewState viewState) {
    _state = viewState;
    notifyListeners();
  }

build 过程中不要进行数据获取操作。请使用 FutureBuilder。参见 https://dev59.com/Zbnoa4cB1Zd3GeqPLCU8#60141803。 - Ted Henry
如果我使用FutureBuilder获取数据,setState将再次被调用,问题仍然存在。 - Nikssj_
我的意思是,如果我将API的获取放在FutureBuilder中,那么循环将是:FutureBuilder -> 构建 -> 在FutureBuilder内部获取API(使用setState)-> 然后再次重建 -> 调用FutureBuilder -> ...... 再次进行相同的循环 - Nikssj_
FutureBuilder 是更好的实践方式。你的使用案例正是 FutureBuilder 成为 Flutter 一部分的原因。你的使用案例和 FutureBuilder 都包含在 Flutter 文档中。https://flutter.dev/docs/cookbook/networking/fetch-data - Ted Henry
1
如果您打算使用HomeViewModel方法,请使用包提供程序的Consumer而不是Provider.of - Ted Henry
显示剩余3条评论
1个回答

13
你可以使用WidgetsBinding.instance.addPostFrameCallback
有关详细信息,请参考https://www.didierboelens.com/faq/week2/

代码片段

@override
void initState(){
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_){
    Provider.of<HomeViewModel>(context, listen: false)
    .fetchUltimaNoticia(context);  

  });
}

这个功能能按照我的预期工作。这是正确的做法吗?也就是说,如果我需要在新页面被推出时获取数据,我可以毫无问题地使用它吗? - Nikssj_
没问题。 - chunhunghan
@NicoAybar,如果这篇回答对您有帮助,请将其标记为答案。谢谢。 - chunhunghan
@chunhunghan 这不会出现"不要在异步间隙中使用BuildContexts"的警告吗? - nox

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接