flutter - 如何实现 pull_to_refresh 到 streambuilder

标签 flutter dart

我正在尝试实现 pull_to_refresh .我有两个我将使用的无状态小部件。首先是_ProductCategoryDe​​tailPageState 这里是代码

  @override
  Widget build(BuildContext context) {
    return Material(
        child: Scaffold(
        appBar: AppBar(
          title: Text(widget.categoryName),
        ),
        body: Container(
          height: MediaQuery.of(context).size.height,
          child: Column(
            children: <Widget>[
              Divider(
                height: 20.0,
              ),
              Flexible(
                  child:StreamBuilder<List<Products>>(
                stream: _productController.stream,
                builder: (context, snapshot) {
                    if (snapshot.hasError) {
                      return errMess(context, "Failed to fetch data");
                    } else {
                      if (snapshot.hasData) {
                        if (snapshot.data.length > 0) {
                          return ProductList(category: snapshot.data);
                        } else {
                          return errMess(context,
                              "There is no available product in this category");
                        }
                      } else {
                        return errMess(context,
                            "There is no available product in this category");
                      }
                    }
                },
              )),
              Divider(
                height: 25.0,
              ),
            ],
          ),
        )));
  }

  loadProduct(String categoryId, int limit, int offset) async {
    List<Products> products = await fetchProducts(http.Client(), categoryId, limit, offset);
    _productController.sink.add(products);
  }
  static List<Products> parseProducts(String responseBody) {
    final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
    return parsed.map<Products>((json) => Products.fromJson(json)).toList();
  }

  Future<List<Products>> fetchProducts(http.Client client, String categoryId, int limit, int offset) async {
    final response = await http.post(Configuration.url +
        "api/getProducts/" +
        categoryId +
        "/" +
        limit.toString() +
        "/" +
        offset.toString());
    if (response.statusCode < 200 || response.statusCode > 300) {
      throw new Exception('Failed to fetch data');
    } else {
      return compute(parseProducts, response.body);
    }
  }

这是我的第二个无状态小部件

class _ProductListState extends State<ProductList> {
  int limit = 0;
  int offset = 4;

  RefreshController _refreshController2 =
      RefreshController(initialRefresh: false);

  @override
  Widget build(BuildContext context) {
    return SmartRefresher(
      child: new GridView.builder(
        itemCount: widget.category.length,
        gridDelegate:
            new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
        itemBuilder: (BuildContext context, int index) {
          return new GestureDetector(
            onTap: () {
              print("Product detail");
            },
            child: Card(
              semanticContainer: true,
              clipBehavior: Clip.antiAliasWithSaveLayer,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Expanded(
                    child: Image.network(
                      Configuration.url +
                          "assets/app_assets/" +
                          widget.category[index].productImage,
                      width: 250,
                      height: 250,
                      filterQuality: FilterQuality.low,
                    ),
                  ),
                  SizedBox(
                    height: 25,
                  ),
                  Text(
                    widget.category[index].productName,
                    style: TextStyle(fontSize: 15.0),
                  ),
                  SizedBox(
                    height: 25,
                  ),
                ],
              ),
            ),
          );
        },
      ),
      controller: _refreshController2,
      enablePullUp: true,
      header: MaterialClassicHeader(),
      onRefresh: () {
        _onRefresh(_refreshController2, widget.category,
            widget.category[0].categoryId);
      },
      onLoading: () {
        _onLoading(_refreshController2, widget.category,
            widget.category[0].categoryId);
      },
    );
  }

  void _onLoading(RefreshController controller, List<Products> data,String categoryId) async {
    await Future.delayed(Duration(milliseconds: 2000));
    setState(() {
      limit = limit + offset;
      offset = 6;
    });

   _ProductCategoryDetailPageState().loadProduct(categoryId, limit, offset);

    controller.loadComplete();
  }

  void _onRefresh(RefreshController controller, List<Products> data,
      String categoryId) async {
    await Future.delayed(Duration(milliseconds: 1000));
    controller.refreshCompleted();
  }
}

当我拉动网格时没有错误,但数据没有改变。在我检查了这部分之后

    Flexible(
                      child:StreamBuilder<List<Products>>(
                    stream: _productController.stream,
                    builder: (context, snapshot) {
print("run")
                        if (snapshot.hasError) {
                          return errMess(context, "Failed to fetch data");
                        } else {
                          if (snapshot.hasData) {
                            if (snapshot.data.length > 0) {
                              return ProductList(category: snapshot.data);
                            } else {
                              return errMess(context,
                                  "There is no available product in this category");
                            }
                          } else {
                            return errMess(context,
                                "There is no available product in this category");
                          }
                        }
                    },
                  )),

如您所见,我添加了 print("run") ,它只显示一次。

我的完整脚本 https://gist.github.com/bobykurniawan11/04f2584c6de97f1d9324bfe3b24f669f

最佳答案

这将不起作用,因为您正在创建 State 对象的新 State 实例。例如,您应该使用回调连接这两个小部件。

_ProductCategoryDetailPageState().loadProduct(categoryId, limit, offset);

像这样:

// Custom callback function
typedef void OnLoadProductsFunction(String categoryId, int limit, int offset); 

class ProductList extends StatefulWidget {
    OnLoadProductsFunction onLoad;

    ProductList({
        this.category,
        this.onLoad,
    })
}

...

 void _onLoading(RefreshController controller, List<Products> data,String categoryId) async {
    await Future.delayed(Duration(milliseconds: 2000));
    setState(() {
      limit = limit + offset;
      offset = 6;
    });

    widget.onLoad(categoryId, limit, offset);

    controller.loadComplete();
}

...


// In the parent widget
return ProductList(
    category: snapshot.data
    onLoad: (categoryId, limit, offset) {
        loadProduct(categoryId, limit, offset);
    }
);

通过这种方式,streamcontroller 将从回调函数中更新。您拥有的其他选项是将 StreamController 实例传递给 ProductList 小部件,并且是将产品列表添加到接收器的子项。

关于flutter - 如何实现 pull_to_refresh 到 streambuilder,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57039229/

相关文章:

flutter - 如何在 Flutter 的 ListView 中添加复选框?

android - flutter 应用程序 : Required to terminate screen while navigating to next screen

dart - 接口(interface)的依赖注入(inject)

java - Google 移动广告 SDK 未正确初始化。 AdMob 发布商应遵循此处的说明 :

android - flutter中实现自定义底部导航栏

dart - 创建 MaterialApp 后如何调用方法?

css - 在 Polymer.dart 中切换样式表 URL

firebase - 即使应用程序在 flutter 中被杀死,如何将用户位置发送到 Firestore?

flutter - 我试图将object.parameter传递到自定义小部件中并失败

list - 在 firestore 集合中保存从 JSON URL 获取的列表