Android 无限滚动listview-在生成期间调用setState()或markNeedsBuild

Android 无限滚动listview-在生成期间调用setState()或markNeedsBuild,android,ios,dart,flutter,Android,Ios,Dart,Flutter,我正在创建一个无限滚动列表视图,我想在\u loadNames函数中使用setState将\u loadingState字符串从'loading…'更改为'loaded',但是当从itemBuilder调用\u loadNames时,我得到了“setState called during build error”。使用RefreshIndicator可以正常工作并更新项目,但滚动到底部会导致错误。如何从ListViews builder调用_loadNames而不出现错误,或者可以使用其他方法。

我正在创建一个无限滚动列表视图,我想在
\u loadNames
函数中使用setState将
\u loadingState
字符串从'loading…'更改为'loaded',但是当从
itemBuilder
调用\u loadNames时,我得到了“setState called during build error”。使用RefreshIndicator可以正常工作并更新项目,但滚动到底部会导致错误。如何从ListViews builder调用_loadNames而不出现错误,或者可以使用其他方法。 注意:不想使用redux或bloc

class _HomePageState extends State<HomePage> {
     List<String> names = [];
     List<String> _shownNames = [];
     int currentPage = 0;
     int limit = 20;
     String _loadingState = 'loading';
     bool loading = true;

     @override
     void initState() {
       super.initState();
       for (int i = 0; i < 200; i++) {
         names.add("hello $i");
       }
       _loadNames();
     }

     @override
     Widget build(BuildContext context) {
       // TODO: implement build
       return new Scaffold(
         appBar: new AppBar(title: new Text('User')),
         body: Column(children: <Widget>[
           Text(_loadingState),
           Expanded(child:_getListViewWidget()),
         ],)
       );
     }

     Widget _getListViewWidget(){
       ListView lv =  new ListView.builder(
         itemCount: _shownNames.length,
         itemBuilder: (context, index){
         if(index >= _shownNames.length - 5 && !loading){
           _loadNames(); // Getting error when this is called
         }
         return  ListTile(
           title: Text(_shownNames[index]),
         );
       });

       RefreshIndicator refreshIndicator = new RefreshIndicator(
         key: _refreshIndicatorKey,
         onRefresh: (){
           _loadNames();
           return null;
         },
         child: lv
       );
       return refreshIndicator;
     }

    _loadNames(){
       loading = true;
       setState(() {
         _loadingState = 'loading...';
       });

       new Timer(const Duration(seconds: 5), () {
          setState(() {
            _shownNames.addAll(names.getRange(currentPage, currentPage + limit));
           currentPage += limit;
           _loadingState = 'loaded';
         });
         loading = false;
       });
     }
  }
class\u HomePageState扩展状态{
列表名称=[];
列表_shownNames=[];
int currentPage=0;
整数极限=20;
字符串_loadingState='loading';
布尔加载=真;
@凌驾
void initState(){
super.initState();
对于(int i=0;i<200;i++){
名称。添加(“你好$i”);
}
_loadNames();
}
@凌驾
小部件构建(构建上下文){
//TODO:实现构建
归还新脚手架(
appBar:new appBar(标题:new Text('User')),
正文:列(子项:[
文本(_loadingState),
已展开(子项:_getListViewWidget()),
],)
);
}
小部件_getListViewWidget(){
ListView lv=新建ListView.builder(
itemCount:_shownNames.length,
itemBuilder:(上下文,索引){
如果(索引>=\u shownNames.length-5&&!加载){
_loadNames();//调用时出错
}
返回列表块(
标题:文本(_shownNames[索引]),
);
});
RefreshIndicator RefreshIndicator=新的RefreshIndicator(
键:_refreshIndicatorKey,
onRefresh:(){
_loadNames();
返回null;
},
儿童:吕
);
返回刷新指示器;
}
_loadNames(){
加载=真;
设置状态(){
_loadingState='loading…';
});
新计时器(常数持续时间(秒:5),(){
设置状态(){
_shownNames.addAll(names.getRange(currentPage,currentPage+limit));
当前页面+=限制;
_loadingState='loaded';
});
加载=假;
});
}
}
更改
\u loadNames(){

更新

_loadNames(){
   loading = true;

   new Future.delayed(Duration.zero, () => setState(() {
     _loadingState = 'loading...';
   }));

非常感谢您的帮助。主要问题来自ListViews itemBuilder。当
loadNames()时,如何显示“正在加载…”或“已加载”文本
在itemBuilder中被调用,而没有在loadNames函数中使用setState?我想要实现的只是一个带有进度指示器的无限滚动列表视图。我更新了我的答案。如果您执行
setState()
delayed它不会干扰
build
。这样你可以保持其他代码的原样。@GünterZöchbauer这种方法有名字吗?例如,我们是否直接设置静态字段而不使用setState()为了不触发重建?@Colin我真的不理解你的评论。我的回答是关于不从
build()
调用
setState()
,对不起,我不是太清楚。在OP的问题中,在
\u loadNames()中调用
setState()
导致了他的问题。在您的回答中,看起来您没有实际调用
setState()
而是直接调用
\u loadingState='loading…';
对吗?
     onRefresh: (){
       _loadNames();
       return null;
     },
     onRefresh: (){
       setState(() => _loadNames());
     },
_loadNames(){
   loading = true;

   new Future.delayed(Duration.zero, () => setState(() {
     _loadingState = 'loading...';
   }));