如何为 ListView.builder 加载数据以从提供者的数据构建

Posted

技术标签:

【中文标题】如何为 ListView.builder 加载数据以从提供者的数据构建【英文标题】:How to load data for ListView.builder to build from provider's data 【发布时间】:2020-11-30 12:00:47 【问题描述】:

我希望我的应用程序创建一个 ListView.builder,其中包含从后端 API 获取的用户数据以及提供程序和 http 包。

提供者获取数据的方法(现在可以正常工作):

  Future<List<User>> fetchUsersList() async 
   var resp = await UserService().getAllUsers();
    if (resp.statusCode == 200) 
     return userListFromJson(resp.body);
     else 
     decodeErrorMessage(resp);
     return _usersList = [];
   
 

我的创建 ListView.builder 的代码:

class _StaffViewState extends State<StaffView> 
    bool _searchaMode = false;
    bool _fetchingData = false;
    List<User> _userList = List<User>();

@override
Widget build(BuildContext context) 

    Provider.of<UserProvider>(context).fetchUsersList().then(
     (data) => this._userList = data);
   

return Consumer<UserProvider>(
  builder: (context, providerData, _) => Scaffold(
    appBar: AppBar(
      title: _searchaMode == false
          ? Text('Staff\s List')
          : TextField(
              autofocus: true,
              cursorColor: Colors.white,
              onSubmitted: (String value) async 
                await showDialog<void>(
                  context: context,
                  builder: (BuildContext context) 
                    return AlertDialog(
                      title: const Text('Thanks!'),
                      content: Text('You typed "$value".'),
                      actions: <Widget>[
                        FlatButton(
                          onPressed: () 
                            Navigator.pop(context);
                          ,
                          child: const Text('OK'),
                        ),
                      ],
                    );
                  ,
                );
              ,
              style: TextStyle(
                color: Colors.white,
              ),
              decoration: InputDecoration(
                hintText: 'Search . . .',
                hintStyle: TextStyle(color: Colors.white),
                border: InputBorder.none,
              ),
            ),
      actions: [
        _searchaMode == false
            ? IconButton(
                onPressed: () 
                  setState(() 
                    _searchaMode = true;
                  );
                ,
                icon: Icon(Icons.search),
              )
            : IconButton(
                onPressed: () 
                  setState(() 
                    _searchaMode = false;
                  );
                ,
                icon: Icon(
                  Icons.cancel,
                  color: Colors.tealAccent,
                ),
              ),
        RaisedButton.icon(
          onPressed: () async ,
          color: Colors.yellow.shade900,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.only(
              bottomLeft: Radius.circular(100.0),
              topLeft: Radius.circular(100.0),
            ),
          ),
          icon: Icon(
            Icons.add,
            size: 25.0,
            color: Colors.white,
          ),
          label: Text(
            'Add User',
            style: TextStyle(
              fontSize: 15.0,
              fontWeight: FontWeight.bold,
              color: Colors.white,
            ),
          ),
        ),
      ],
    ),
    drawer: AppSideNav(),
    body: _fetchingData == true
        ? pr.show()
        : ListView.builder(
            itemCount: _userList.length,
            itemBuilder: (context, index) 
              return Container(
                decoration: BoxDecoration(
                  border: Border(
                    bottom: BorderSide(
                      width: .9,
                      color: Colors.yellow.shade700,
                    ),
                  ),
                ),
                child: ListTile(
                  leading: Image.network(
                      providerData.getUsersList()[index].image != null
                          ? providerData.getUsersList()[index].image
                          : 'https://via.placeholder.com/150'),
                  title: Center(
                      child:
                          Text(providerData.getUsersList()[index].email)),
                  subtitle: Center(
                      child: Text(providerData.getUsersList()[index].nid)),
                  trailing:
                      providerData.getUsersList()[index].isAuthority == true
                          ? Text('Authority')
                          : providerData.getUsersList()[index].isStaff ==
                                  true
                              ? Text('Staff')
                              : Text('General User'),
                  contentPadding: EdgeInsets.symmetric(horizontal: 20.0),
                  visualDensity: VisualDensity.adaptivePlatformDensity,
                  isThreeLine: false,
                  onTap: () ,
                ),
              );
            ,
          ),
       ),
     );
   
 

正在加载我的数据以构建列表。但问题是发生了一个错误,说 在构建过程中调用了 setState() 或 markNeedsBuild()

我的问题是使用提供程序包填充数据的实际方法是什么,首先执行 dataFetching 方法,然后构建列表。

【问题讨论】:

【参考方案1】:

您可以简单地使用FutureBuilder 小部件,而不是在build 函数的开头或initState 函数中调用您的获取函数。通过这种方式,您可以准确指定在加载时显示什么以及完成时显示什么,而无需添加越来越多的成员变量。

您修改后的代码可能如下所示:

class _StaffViewState extends State<StaffView> 
  
  bool _searchaMode = false;

  @override
  Widget build(BuildContext context) 
    return Consumer<UserProvider>(
      builder: (context, providerData, _) => FutureBuilder(
        future: providerData.fetchUsersList(),
        builder: (context, snapshot) 
          if (snapshot.connectionState != ConnectionState.done) 
            return Text("Loading...");
          

          List<User> userList = snapshot.data;

          return Scaffold(
            appBar: AppBar(
              title: _searchaMode == false
                  ? Text('Staff\s List')
                  : TextField(
                      autofocus: true,
                      cursorColor: Colors.white,
                      onSubmitted: (String value) async 
                        await showDialog<void>(
                          context: context,
                          builder: (BuildContext context) 
                            return AlertDialog(
                              title: const Text('Thanks!'),
                              content: Text('You typed "$value".'),
                              actions: <Widget>[
                                FlatButton(
                                  onPressed: () 
                                    Navigator.pop(context);
                                  ,
                                  child: const Text('OK'),
                                ),
                              ],
                            );
                          ,
                        );
                      ,
                      style: TextStyle(
                        color: Colors.white,
                      ),
                      decoration: InputDecoration(
                        hintText: 'Search . . .',
                        hintStyle: TextStyle(color: Colors.white),
                        border: InputBorder.none,
                      ),
                    ),
              actions: [
                _searchaMode == false
                    ? IconButton(
                        onPressed: () 
                          setState(() 
                            _searchaMode = true;
                          );
                        ,
                        icon: Icon(Icons.search),
                      )
                    : IconButton(
                        onPressed: () 
                          setState(() 
                            _searchaMode = false;
                          );
                        ,
                        icon: Icon(
                          Icons.cancel,
                          color: Colors.tealAccent,
                        ),
                      ),
                RaisedButton.icon(
                  onPressed: () async ,
                  color: Colors.yellow.shade900,
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.only(
                      bottomLeft: Radius.circular(100.0),
                      topLeft: Radius.circular(100.0),
                    ),
                  ),
                  icon: Icon(
                    Icons.add,
                    size: 25.0,
                    color: Colors.white,
                  ),
                  label: Text(
                    'Add User',
                    style: TextStyle(
                      fontSize: 15.0,
                      fontWeight: FontWeight.bold,
                      color: Colors.white,
                    ),
                  ),
                ),
              ],
            ),
            drawer: AppSideNav(),
            body: ListView.builder(
                    itemCount: userList.length,
                    itemBuilder: (context, index) 
                      return Container(
                        decoration: BoxDecoration(
                          border: Border(
                            bottom: BorderSide(
                              width: .9,
                              color: Colors.yellow.shade700,
                            ),
                          ),
                        ),
                        child: ListTile(
                          leading: Image.network(
                              providerData.getUsersList()[index].image != null
                                  ? providerData.getUsersList()[index].image
                                  : 'https://via.placeholder.com/150'),
                          title: Center(
                              child: Text(
                                  providerData.getUsersList()[index].email)),
                          subtitle: Center(
                              child:
                                  Text(providerData.getUsersList()[index].nid)),
                          trailing: providerData
                                      .getUsersList()[index]
                                      .isAuthority ==
                                  true
                              ? Text('Authority')
                              : providerData.getUsersList()[index].isStaff ==
                                      true
                                  ? Text('Staff')
                                  : Text('General User'),
                          contentPadding:
                              EdgeInsets.symmetric(horizontal: 20.0),
                          visualDensity: VisualDensity.adaptivePlatformDensity,
                          isThreeLine: false,
                          onTap: () ,
                        ),
                      );
                    ,
                  ),
          );
        ,
      ),
    );
  


【讨论】:

【参考方案2】:
@override
   void initState() 
     super.initState();

    use api here
  

【讨论】:

尝试了 initialState 但它给出了这个错误.. 在 _StaffViewState.initState() 完成之前调用了dependOnInheritedWidgetOfExactType<_inheritedproviderscope>>() 或dependOnInheritedElement()。 其实用 didChangeDependencies() 试过了,但总是报错。 不,它不起作用...它说 ::: _StaffViewState.initState() 返回了一个 Future。【参考方案3】:

您不应该在 build 内调用 API,因为每次框架重建屏幕时都会有一个新的调用,而是在 initState() 内调用它。

如果您需要等待 API 数据,请在您的 build 函数中使用 FutureBuilder,以便在检索到数据后自动重建。

【讨论】:

@override Widget build(BuildContext context) FutureBuilder _getUserData() Provider.of(context) .fetchUsersList() .then((data) => this._userList = data);这是怎么...? 那我该怎么称呼它..? 只需在initState 上调用_userDataFuture = providerData.fetchUsersList(),将future 保存到_userDataFuture,将这个future 传递给FutureBuilder,完成后你会得到结果,不需要then子句,因为它可以在构建器中。

以上是关于如何为 ListView.builder 加载数据以从提供者的数据构建的主要内容,如果未能解决你的问题,请参考以下文章

Flutter IndexedWidgetBuilder 与 ListView.builder 与动态项目数?

ListView.builder itemCount 未更新

Flutter:使用 ListView.builder 消失的 SliverAppBar

Flutter ListView.Builder 导致滚动不连贯。如何使滚动平滑?

如果数据发生变化,颤动如何更新 Listview.builder

如何为 LiveServerTestCase 加载夹具