首页 > 解决方案 > 在 Flutter 中展开 ExpansionTile 时从 API 加载子数据

问题描述

这个想法是,我有主要类别,它们有自己的 id,当用户展开图块(应该是 fetchFromApi)时,它将从 api 中获取数据(在本例中为模拟资产 json 文件)并显示它作为瓷砖。

我正在努力获得正确的机制来加载它。我想设置状态?我已经尝试过使用futurebuilder,因为我确实希望在加载时出现加载图标,但我似乎无法正确理解这个想法。关于如何实现这一点的建议将不胜感激。

这是我现在的课。

class DrawerNavigationPart extends StatelessWidget {
  final List<Category> mainCategories = Categories.categoryData;

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: mainCategories.length,
      itemBuilder: (BuildContext context, int index) {
        final Category category = mainCategories[index];
        if (category.fetchFromApi) {
          List<Category> api = [];
          return ExpansionTile(
            key: PageStorageKey<Category>(category),
            leading: Icon(category.icon),
            title: Text(
              category.title,
              softWrap: false,
              overflow: TextOverflow.ellipsis,
            ),
            onExpansionChanged: (bool open) async {
              if (open) {
                String response = await DefaultAssetBundle.of(context)
                    .loadString(
                        'assets/822828-2228222-2222#category-api-mock.json');
                api = categoryResponseFromJson(response).data;
                print(api);
              }
            },
            children: [
//              FutureBuilder(
//                  future: DefaultAssetBundle.of(context).loadString(
//                      'assets/822828-2228222-2222#category-api-mock.json'),
//                  builder: (context, snapshot) {
//                    final CategoryResponse categoryResponse =
//                        categoryResponseFromJson(snapshot.data);
////                    return ListView.builder(
////                        itemCount: 3,
////                        itemBuilder: (BuildContext context, int index) {
////                          return Text(index.toString());
////                        });
//                    return ListTile(
//                        title: Text(categoryResponse.data[index].title));
//                  })
            ],
          );
        } else {
          return ListTile(
            leading: Icon(category.icon),
            title: Text(
              category.title,
              softWrap: false,
              overflow: TextOverflow.ellipsis,
            ),
            onTap: () {
              print(category.title + ' clicked');
            },
          );
        }
      },
    );
  }

  Widget _buildTiles(Category root) {
    if (root.children.isEmpty)
      return ListTile(
        leading: Icon(root.icon),
        title: Text(
          root.title,
          softWrap: false,
          overflow: TextOverflow.ellipsis,
        ),
        onTap: () {
          print("${root.title} listTile clicked");
        },
      );
    return ExpansionTile(
      key: PageStorageKey<Category>(root),
      leading: Icon(root.icon),
      title: Text(
        root.title,
        softWrap: false,
        overflow: TextOverflow.ellipsis,
      ),
      onExpansionChanged: (bool open) {
        print("${root.title} expansionTile clicked $open");
      },
      children: root.children.map(_buildTiles).toList(),
    );
  }
}

标签: flutterfutureexpansion

解决方案


StatelessWidget 未更改视图

所以使用 StatefulWidget 和 State Class

例如

class  DrawerNavigationPart extends StatefulWidget{
  @override
  State<StatefulWidget> createState() => _DrawerNavigationPart();
} 

class _DrawerNavigationPart extends State<DrawerNavigationPart> {

    
  final List<Category> mainCategories = Categories.categoryData;
  
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: mainCategories.length,
      itemBuilder: (BuildContext context, int index) {
        final Category category = mainCategories[index];
        if (category.fetchFromApi) {
          List<Category> api = [];
          return ExpansionTile(
            key: PageStorageKey<Category>(category),
            leading: Icon(category.icon),
            title: Text(
              category.title,
              softWrap: false,
              overflow: TextOverflow.ellipsis,
            ),
            onExpansionChanged: (bool open) async {
              if (open) {
                String response = await DefaultAssetBundle.of(context)
                    .loadString(
                    'assets/822828-2228222-2222#category-api-mock.json');
                api = categoryResponseFromJson(response).data;
                print(api);
              }
            },
            children: [
              FutureBuilder(
                  future: getJsonData(),
                  builder: (context, snapshot) {
                    final CategoryResponse categoryResponse =
                        categoryResponseFromJson(snapshot.data);
//                    return ListView.builder(
//                        itemCount: 3,
//                        itemBuilder: (BuildContext context, int index) {
//                          return Text(index.toString());
//                        });
                    return ListTile(
                        title: Text(categoryResponse.data[index].title));
                  })
            ],
          );
        } else {
          return ListTile(
            leading: Icon(category.icon),
            title: Text(
              category.title,
              softWrap: false,
              overflow: TextOverflow.ellipsis,
            ),
            onTap: () {
              print(category.title + ' clicked');
            },
          );
        }
      },
    );
  }

  Widget _buildTiles(Category root) {
    if (root.children.isEmpty)
      return ListTile(
        leading: Icon(root.icon),
        title: Text(
          root.title,
          softWrap: false,
          overflow: TextOverflow.ellipsis,
        ),
        onTap: () {
          print("${root.title} listTile clicked");
        },
      );
    return ExpansionTile(
      key: PageStorageKey<Category>(root),
      leading: Icon(root.icon),
      title: Text(
        root.title,
        softWrap: false,
        overflow: TextOverflow.ellipsis,
      ),
      onExpansionChanged: (bool open) {
        print("${root.title} expansionTile clicked $open");
      },
      children: root.children.map(_buildTiles).toList(),
    );
  }

  Future<String> getJsonData() async {
    var str = await DefaultAssetBundle.of(context).loadString(
        'assets/822828-2228222-2222#category-api-mock.json');
    return str;
  }
}

推荐阅读