xuelongqy / flutter_easy_refresh

A flutter widget that provides pull-down refresh and pull-up load.
https://xuelongqy.github.io/flutter_easy_refresh/
MIT License
3.9k stars 635 forks source link

调用easyRefreshController.callRefresh(scrollController: _scrollController)没有达到预期效果 #692

Closed 1232209 closed 1 year ago

1232209 commented 1 year ago

期待效果: https://user-images.githubusercontent.com/32189905/222321478-d64ca474-6552-47dc-bcfc-924bf46a4336.mp4 实际效果: https://user-images.githubusercontent.com/32189905/222321846-8e23a073-7020-48f8-b585-242a18cd8137.mp4

代码,改造的example:

import 'dart:async';

import 'package:example/widget/skeleton_item.dart';
import 'package:flutter/material.dart';
import 'package:easy_refresh/easy_refresh.dart';
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:get/get.dart';

class NestedScrollViewPage extends StatefulWidget {
  const NestedScrollViewPage({Key? key}) : super(key: key);

  @override
  NestedScrollViewPageState createState() {
    return NestedScrollViewPageState();
  }
}

class NestedScrollViewPageState extends State<NestedScrollViewPage> with SingleTickerProviderStateMixin {
  late TabController _tabController;
  int _tabIndex = 0;
  int _listCount = 20;
  int _gridCount = 20;

  final EasyRefreshController _easyRefreshController = EasyRefreshController();
  final ScrollController _scrollController = ScrollController();
  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 2, vsync: this);
  }

  @override
  void dispose() {
    super.dispose();
    _tabController.dispose();
  }

  _buildHomeWidget() {
    return [
      ExtendedVisibilityDetector(
        uniqueKey: const Key('Tab0'),
        child: EasyRefresh(
          controller: _easyRefreshController,
          scrollController: _scrollController,
          header: ClassicHeader(
            dragText: 'Pull to refresh'.tr,
            armedText: 'Release ready'.tr,
            readyText: 'Refreshing...'.tr,
            processingText: 'Refreshing...'.tr,
            processedText: 'Succeeded'.tr,
            noMoreText: 'No more'.tr,
            failedText: 'Failed'.tr,
            messageText: 'Last updated at %T'.tr,
            safeArea: false,
          ),
          footer: ClassicFooter(
            position: IndicatorPosition.locator,
            dragText: 'Pull to load'.tr,
            armedText: 'Release ready'.tr,
            readyText: 'Loading...'.tr,
            processingText: 'Loading...'.tr,
            processedText: 'Succeeded'.tr,
            noMoreText: 'No more'.tr,
            failedText: 'Failed'.tr,
            messageText: 'Last updated at %T'.tr,
          ),
          child: CustomScrollView(
            // controller: _scrollController,//在这里加上scrollController之后能够正常触发刷新效果,但是nestedScrollView的效果就失效了
            //不在scrollview里面加scrollController的花,调用callrefresh,就animate到了页面顶部,并且没有触发刷新效果
            slivers: [
              SliverList(
                  delegate: SliverChildBuilderDelegate((context, index) {
                return const SkeletonItem();
              }, childCount: _listCount)),
              const FooterLocator.sliver(),
            ],
          ),
          onRefresh: () async {
            await Future.delayed(const Duration(seconds: 2), () {
              if (mounted) {
                setState(() {
                  _listCount = 20;
                });
              }
            });
          },
          onLoad: () async {
            await Future.delayed(const Duration(seconds: 2), () {
              if (mounted) {
                setState(() {
                  _listCount += 10;
                });
              }
            });
          },
        ),
      ),
      ExtendedVisibilityDetector(
        uniqueKey: const Key('Tab1'),
        child: EasyRefresh(
          callRefreshOverOffset: 999999,
          header: ClassicHeader(
            dragText: 'Pull to refresh'.tr,
            armedText: 'Release ready'.tr,
            readyText: 'Refreshing...'.tr,
            processingText: 'Refreshing...'.tr,
            processedText: 'Succeeded'.tr,
            noMoreText: 'No more'.tr,
            failedText: 'Failed'.tr,
            messageText: 'Last updated at %T'.tr,
            safeArea: false,
          ),
          footer: ClassicFooter(
            position: IndicatorPosition.locator,
            dragText: 'Pull to load'.tr,
            armedText: 'Release ready'.tr,
            readyText: 'Loading...'.tr,
            processingText: 'Loading...'.tr,
            processedText: 'Succeeded'.tr,
            noMoreText: 'No more'.tr,
            failedText: 'Failed'.tr,
            messageText: 'Last updated at %T'.tr,
          ),
          child: CustomScrollView(
            slivers: [
              SliverGrid(
                  delegate: SliverChildBuilderDelegate((context, index) {
                    return const SkeletonItem(
                      direction: Axis.horizontal,
                    );
                  }, childCount: _gridCount),
                  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 2,
                    childAspectRatio: 6 / 7,
                  )),
              const FooterLocator.sliver(),
            ],
          ),
          onRefresh: () async {
            await Future.delayed(const Duration(seconds: 2), () {
              if (mounted) {
                setState(() {
                  _gridCount = 30;
                });
              }
            });
          },
          onLoad: () async {
            await Future.delayed(const Duration(seconds: 2), () {
              if (mounted) {
                setState(() {
                  _gridCount += 10;
                });
              }
            });
          },
        ),
      )
    ];
  }

  @override
  Widget build(BuildContext context) {
    final themeData = Theme.of(context);
    return Scaffold(
      body: ExtendedNestedScrollView(
        onlyOneScrollInBody: true,
        pinnedHeaderSliverHeightBuilder: () {
          return MediaQuery.of(context).padding.top + kToolbarHeight;
        },
        controller: _scrollController,
        headerSliverBuilder: (context, innerBoxIsScrolled) {
          return <Widget>[
            SliverToBoxAdapter(
              child: Container(
                height: 300,
                color: Colors.red,
                child: const Center(child: Text('首页组件1')),
              ),
            ),
            SliverToBoxAdapter(
              child: Container(
                height: 300,
                color: Colors.blue,
                child: const Center(child: Text('首页组件2')),
              ),
            ),
            SliverAppBar(
              primary: false,
              pinned: true,
              toolbarHeight: 0,
              expandedHeight: 38,
              bottom: PreferredSize(
                preferredSize: const Size(double.infinity, 48),
                child: TabBar(
                  controller: _tabController,
                  labelColor: themeData.colorScheme.primary,
                  indicatorColor: themeData.colorScheme.primary,
                  onTap: (index) {
                    setState(() {
                      _tabIndex = index;
                    });
                  },
                  tabs: const <Widget>[
                    Tab(
                      text: 'List',
                    ),
                    Tab(
                      text: 'Grid',
                    ),
                  ],
                ),
              ),
            ),
          ];
        },
        body: IndexedStack(
          index: _tabIndex,
          children: _buildHomeWidget(),
        ),
      ),
      floatingActionButton: TextButton(
        child: const Text('刷新'),
        onPressed: () {
          _easyRefreshController.callRefresh(scrollController: _scrollController);
        },
      ),
    );
  }
}

分析:我查看了其它的isuue,也提到easy_refresh和nestScrollView结合起来使用时会有一些问题@https://github.com/xuelongqy/flutter_easy_refresh/issues/616 关于提到的加不加controller实验结果如下: 在自己的customScrollView加上scrollController之后能够正常触发刷新效果,但是nestedScrollView的效果就失效了(因为nestScrollView需要自己去控制controller) 不在customScrollView里面加scrollController的话,调用callrefresh,就animate到了页面顶部,并且没有触发刷新效果

提问:是我的使用姿势不对吗?有没有好的解决方案呢? 我最开始用的nestedScrollView+Pull_to_Refresh,这个方案也是有许多问题,希望哪位大哥看到能帮帮我

xuelongqy commented 1 year ago

所以下拉刷新,在Flutter避免和NestedScrollView一起使用才是正确方式,哈哈哈。目前NestedScrollView的很多问题会得不到解决,或者需要很长时间。如果非必要的话,可以绕道而行

1232209 commented 1 year ago

所以下拉刷新,在Flutter避免和NestedScrollView一起使用才是正确方式,哈哈哈。目前NestedScrollView的很多问题会得不到解决,或者需要很长时间。如果非必要的话,可以绕道而行 这就很难受了....大佬我申请加群没人同意呢,想和大家沟通一下呢

Dabbit-Chan commented 1 year ago

所以下拉刷新,在Flutter避免和NestedScrollView一起使用才是正确方式,哈哈哈。目前NestedScrollView的很多问题会得不到解决,或者需要很长时间。如果非必要的话,可以绕道而行 这就很难受了....大佬我申请加群没人同意呢,想和大家沟通一下呢

有个办法可以基本做到你想要的效果,虽然不完美。

EasyRefresh(
          child: CustomScrollView(
            slivers: <Widget>[
              SliverPersistentHeader(
                delegate: SliverHeaderDelegate.fixedHeight(
                  height: 200,
                  child: 组件一,
                ),
                pinned: false,
              ),
              SliverPersistentHeader(
                delegate: SliverHeaderDelegate.fixedHeight(
                  height: 200,
                  child: 组件二,
                ),
                pinned: false,
              ),
              SliverPersistentHeader(
                delegate: SliverHeaderDelegate.fixedHeight(
                  height: 50,
                  child: 要pin的tabbar,
                ),
                pinned: true,
              ),
              const HeaderLocator.sliver(),
              SliverToBoxAdapter(child: 底下的list()),
              const FooterLocator.sliver(),
            ],
          ), 
        ),

其中,SliverHeaderDelegate参照Flutter实战第二版 - 6.10.2 Flutter 中常用的 Sliver