【问题标题】:pull down to REFRESH in Flutter在 Flutter 中下拉到 REFRESH
【发布时间】:2023-04-08 17:45:01
【问题描述】:

我的仪表板代码如下所示, 在这里,我在 getReport 方法中执行 get req,我在代码中添加了 RefreshIndicator,当在容器内下拉时应该进行刷新,我正在调用我的 getData(),但我没有得到刷新的内容,我我在下面添加我的代码,如果我犯了错误,请告诉我。

在我的dashboard.dart下方

class Window extends StatefulWidget {
  @override
  _WindowState createState() => _WindowState();
}

class _WindowState extends State<Window> {
  Future reportList;    
  @override
  void initState() {
    super.initState();
    reportList = getReport();
  }    

  Future<void> getReport() async {
    http.Response response =
        await http.get(reportsListURL, headers: {"token": "$token"});
    switch (response.statusCode) {
      case 200: 
        String reportList = response.body;
        var collection = json.decode(reportList);
        return collection;

      case 403:
          break;

      case 401:
        return null;

      default:
        return 1;
    }
  }

  getRefreshScaffold() {
    return Center(
      child: RaisedButton(
        onPressed: () {
          setState(() {
            reportList = getReport();
          });
        },
        child: Text('Refresh, Network issues.'),
      ),
    );
  }

  getDashBody(var data) {
    double maxHeight = MediaQuery.of(context).size.height;
    return Column(
      children: <Widget>[
        Container(
          height: maxHeight - 800,
        ),
        Container(
          margin: new EdgeInsets.all(0.0),
          height: maxHeight - 188,
          child: new Center(
          child: new RefreshIndicator(          //here I am adding the RefreshIndicator
          onRefresh:getReport,                  //and calling the getReport() which hits the get api
          child: createList(context, data),
          ),),
        ),
      ],
    );
  }

  Widget createList(BuildContext context, var data) {
    Widget _listView = ListView.builder(
      itemCount: data.length,
      itemBuilder: (context, count) {
        return createData(context, count, data);
      },
    );
    return _listView;
  }

  createData(BuildContext context, int count, var data) {
    var metrics = data["statistic_cards"].map<Widget>((cardInfo) {
      var cardColor = getColorFromHexString(cardInfo["color"]);
      if (cardInfo["progress_bar"] != null && cardInfo["progress_bar"]) {
        return buildRadialProgressBar(
          context: context,
          progressPercent: cardInfo["percentage"],
          color: cardColor,
          count: cardInfo["value"],
          title: cardInfo["title"],
        );
      } else {
        return buildSubscriberTile(context, cardInfo, cardColor);
      }
    }).toList();

    var rowMetrics = new List<Widget>();
    for (int i = 0; i < metrics.length; i += 2) {
      if (i + 2 < metrics.length)
        rowMetrics.add(Row(children: metrics.sublist(i, i + 2)));
      else
        rowMetrics.add(Row(children: [metrics[metrics.length - 1], Spacer()]));
    }
    return SingleChildScrollView(
      child: LimitedBox(
        //  maxHeight: MediaQuery.of(context).size.height / 1.30,
        child: Column(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.center,
          children: rowMetrics,
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: reportList,
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        switch (snapshot.connectionState) {
          case ConnectionState.none:
          case ConnectionState.waiting:
          case ConnectionState.active:
            return Center(
              child: CircularProgressIndicator(),
            );
          case ConnectionState.done:
            var data = snapshot.data;
            if (snapshot.hasData && !snapshot.hasError) {
              return getDashBody(data);
            } else if (data == null) {
              return Center(
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    Text("Timeout! Log back in to continue"),
                    Padding(
                      padding: EdgeInsets.all(25.0),
                    ),
                    RaisedButton(
                      onPressed: () {
                        setState(() {
                          token = null;
                        });
                        Navigator.of(context).pushReplacement(
                          CupertinoPageRoute(
                              builder: (BuildContext context) => LoginPage()),
                        );
                      },
                      child: Text('Login Again!'),
                    ),
                  ],
                ),
              );
            } else {
              getRefreshScaffold();
            }
        }
      },
    );
  }
}

【问题讨论】:

标签: flutter dart


【解决方案1】:

基本示例

下面是一个 StatefulWidget 的 State 类,其中:

  • ListView 包裹在 RefreshIndicator
    • words状态变量是它的数据源
  • onRefresh 调用 _pullRefresh 函数更新 ListView
    • _pullRefresh 是一个异步函数,不返回任何内容(Future&lt;void&gt;
    • _pullRefresh 的长时间运行数据请求完成时,words 成员/状态变量在setState() 调用中更新以重建ListView 以显示新数据
import 'package:english_words/english_words.dart';

class _PullToRefreshPageState extends State<PullToRefreshPage> {
  List<WordPair> words = generateWordPairs().take(5).toList();

  @override
  Widget build(BuildContext context) {
    return RefreshIndicator(
      onRefresh: _pullRefresh,
      child: ListView.builder(
        itemCount: words.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(words[index].asPascalCase),
          );
        },),
    );
  }

  Future<void> _pullRefresh() async {
    List<WordPair> freshWords = await WordDataSource().getFutureWords(delay: 2);
    setState(() {
      words = freshWords;
    });
    // why use freshWords var? https://stackoverflow.com/a/52992836/2301224
  }
}

class WordDataSource {
  Future<List<WordPair>> getFutureWords({int size = 5, int delay = 5}) async {
    await Future.delayed(Duration(seconds: delay));
    return generateWordPairs().take(5).toList();
  }
}

注意事项

  • 如果你的 async onRefresh 函数完成得很快,你可能想在它后面添加一个await Future.delayed(Duration(seconds: 2));,这样用户体验更愉快。
  • 这让用户有时间完成滑动/下拉手势,并让刷新指示器呈现/动画/旋转指示数据已被提取。

FutureBuilder 示例

这是另一个示例,使用 FutureBuilder,这在从数据库或 HTTP 源获取数据时很常见

class _PullToRefreshFuturePageState extends State<PullToRefreshPage> {
  Future<List<WordPair>> futureWords;

  @override
  void initState() {
    super.initState();
    futureWords = WordDataSource().getFutureWords(delay: 2);
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<WordPair>>(
      //initialData: [],
      future: futureWords,
      builder: (context, snapshot) {
        return RefreshIndicator(
          child: _listView(snapshot),
          onRefresh: _pullRefresh,
        );
      },
    );
  }

  Widget _listView(AsyncSnapshot snapshot) {
    if (snapshot.hasData) {
      return ListView.builder(
        itemCount: snapshot.data.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(snapshot.data[index].asPascalCase),
          );
        },);
    }
    else {
      return Center(
        child: Text('Loading data...'),
      );
    }
  }

  Future<void> _pullRefresh() async {
    List<WordPair> freshFutureWords = await WordDataSource().getFutureWords(delay: 2);
    setState(() {
      futureWords = Future.value(freshFutureWords);
    });
  }
}

注意事项

  • getFutureWords() 函数与上面的 基本示例 中的相同,但数据包装在 Future.value() 中,因为 FutureBuilder 需要 Future
  • 根据 Rémi、Collin 和其他 Dart/Flutter 半神所说,更新 Stateful Widget 成员变量内部 setState()(FutureBuilder 示例中为 futureWords 和 Basic 示例中为 words)是一种很好的做法, 其长期运行的异步数据获取功能已完成。
  • 如果你尝试设置 setState async,你会得到一个异常
  • 更新 setState 之外的成员变量并有一个空的 setState 闭包,将来可能会导致手打/代码分析警告

【讨论】:

  • 完美描述和原生Flutter 解决方案(无需依赖)! ?
  • 完美解决方案谢谢
  • 很好的答案。特别高兴您还使用FutureBuilder 解释了解决方案。
【解决方案2】:

不确定期货,但对于刷新指标,您必须返回一个 void 所以 使用类似的东西

RefreshIndicator(
                onRefresh: () async  {
                  await getData().then((lA) {
                    if (lA is Future) {
                      setState(() {
                        reportList = lA;
                      });
                      return;
                    } else {
                      setState(() {
                       //error
                      });
                      return;
                    }
                  });

                  return;
                },

试试这个,让我知道!

编辑:

好吧,那就在你的刷新方法里面试试这个

          setState(() {
            reportList = getReport();  
          });
          return reportList;

【讨论】:

  • 如果你需要未来,也许只是删除我写的等待异步参数。
  • onRefresh: () async { await getReport().then((lA) { if (lA is Future) { setState(() { reportList = lA; }); return; } else { setState (() { //错误 }); 返回; } });返回; }, 孩子: createDashList(context, data), ),), ) ,
  • 我已经在 getDashBody 中写了这个,但对我没有用
【解决方案3】:

试试这个:

onRefresh: () {
  setState(() {});
}}

而不是onRefresh:getReport

reportList 字段是Future,它返回一次它的值。因此,当您再次致电getReport 时,它不会有任何改变。实际上,更准确的说法是使用StreamStreamBuilder 而不是FutureFutureBuilder。但是对于这段代码,它可能是最短的解决方案

【讨论】:

  • 更改后我收到“无法命名函数表达式。尝试删除名称,或将函数表达式移动到函数声明语句中。”
  • 最初只是 getReport() ,当我添加 RefreshIndicator 时,我收到错误说该方法应该是未来的东西,所以我将 gerReport 重命名为 => Future getReport
  • 对不起。编辑了我的答案
【解决方案4】:

简单的方法:你可以使用下拉刷新包 - https://pub.dev/packages/pull_to_refresh

【讨论】:

    猜你喜欢
    • 2019-09-30
    • 1970-01-01
    • 2021-03-17
    • 1970-01-01
    • 1970-01-01
    • 2019-07-06
    • 2023-01-08
    • 1970-01-01
    • 2019-05-19
    相关资源
    最近更新 更多