首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在NestedScrollView中实现listview lazyload?

如何在NestedScrollView中实现listview lazyload?
EN

Stack Overflow用户
提问于 2020-09-01 17:37:43
回答 1查看 1K关注 0票数 2

我有一个应用程序,它有一个作为入口点的页面,并显示一个包含3个或更多页面的TabView。当用户滚动视图时,它使用NestedScrollViewSliverAppBar提供一些动画。

我希望实现分页列表的延迟加载,但由于它不允许我在CustomScrollView中使用控制器,这一点在文档中有提到:

代码语言:javascript
运行
复制
builder: (BuildContext context) {
  return CustomScrollView(
    // The "controller" and "primary" members should be left
    // unset, so that the NestedScrollView can control this
    // inner scroll view.
    // If the "controller" property is set, then this scroll
    // view will not be associated with the NestedScrollView.
    // The PageStorageKey should be unique to this ScrollView;
    // it allows the list to remember its scroll position when
    // the tab view is not on the screen.
    key: PageStorageKey<String>(name),
    slivers: <Widget>[

我不能使用子页面中的ScrollController来获取滚动值来触发loadMore函数。幸运的是,有一个类似的小部件可以侦听名为ScrollNotification的滚动事件。但是我不知道哪个属性保存了最大滚动限制的值。

尝试通过以下方法比较可用的属性:

代码语言:javascript
运行
复制
bool _onScrollNotification(ScrollNotification notification) {
  if (notification is! ScrollEndNotification) return false;

  print('extentBefore: ${notification.metrics.extentBefore}');
  print('extentAfter: ${notification.metrics.extentAfter}');
  print('maxScrollExtent: ${notification.metrics.maxScrollExtent}');
  return true;
}

但他们似乎不像我所需要的那样持有固定价值。它总是独立地改变它的价值。

I也不能在父页( tabview_holder)上使用 ScrollController ,因为每个选项卡中的每个页面都有独立的bloceventsdata和抓取算法。考虑到这一点,我如何才能达到这一要求?

请看我的剧本:

tabview_holder.dart (不是一个真正的文件名,只是为了说明它)

代码语言:javascript
运行
复制
class EventPage extends StatefulWidget {
  EventPage({Key key}) : super(key: key);

  @override
  _EventPageState createState() => _EventPageState();
}

class _EventPageState extends State<EventPage>
    with SingleTickerProviderStateMixin {
  final ScrollController _scrollController = ScrollController();
  final List<Widget> _tabs = [
    Tab(text: 'My Events'),
    Tab(text: "Private Events"),
    Tab(text: "Division Events"),
    Tab(text: "Department Events"),
    Tab(text: "Public Events"),
  ];

  double _bottomNavigatorPosition = 0.0;
  double _gradientStop = 0.2;
  TabController _tabController;

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(_scrollListener);
    _tabController = TabController(
      initialIndex: 0,
      length: _tabs.length,
      vsync: this,
    );
  }

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

  void _scrollListener() {
    ScrollDirection direction = _scrollController.position.userScrollDirection;
    switch (direction) {
      case ScrollDirection.reverse:
        setState(() {
          _gradientStop = 0.0;
          _bottomNavigatorPosition = -100.0;
        });
        return;
        break;

      case ScrollDirection.forward:
      case ScrollDirection.idle:
        setState(() {
          _gradientStop = 0.2;
          _bottomNavigatorPosition = 0.0;
        });
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Stack(
          children: [
            NestedScrollView(
              controller: _scrollController,
              headerSliverBuilder:
                  (BuildContext context, bool innerBoxIsScrolled) {
                return <Widget>[
                  SliverOverlapAbsorber(
                    handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
                        context),
                    sliver: SliverAppBar(
                      backgroundColor:
                          Theme.of(context).scaffoldBackgroundColor,
                      automaticallyImplyLeading: false,
                      floating: true,
                      expandedHeight: 100,
                      flexibleSpace: FlexibleSpaceBar(
                        background: Container(
                          child: Stack(
                            children: [
                              Positioned(
                                left: 30.0,
                                bottom: 10,
                                child: PageHeader(title: 'Events'),
                              ),
                            ],
                          ),
                        ),
                      ),
                    ),
                  ),
                  SliverPersistentHeader(
                    pinned: true,
                    delegate: _SliverAppBarDelegate(
                      TabBar(
                        controller: _tabController,
                        isScrollable: true,
                        indicator: BubbleTabIndicator(
                          indicatorHeight: 35.0,
                          indicatorColor: Theme.of(context).primaryColor,
                          tabBarIndicatorSize: TabBarIndicatorSize.tab,
                        ),
                        tabs: _tabs,
                      ),
                    ),
                  ),
                ];
              },
              body: TabBarView(
                controller: _tabController,
                children: [
                  MyEventsPage(),
                  PrivateEventsPage(),
                  MyEventsPage(),
                  MyEventsPage(),
                  MyEventsPage(),
                ],
              ),
            ),
            _buildBottomGradient(),
            _buildBottomNavigator(),
          ],
        ),
      ),
    );
  }

  Widget _buildBottomGradient() {
    return IgnorePointer(
      child: AnimatedContainer(
        duration: Duration(milliseconds: 200),
        decoration: BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.bottomCenter,
            end: Alignment.topCenter,
            stops: [_gradientStop / 2, _gradientStop],
            colors: [
              Color(0xFF121212),
              Colors.transparent,
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildBottomNavigator() {
    return AnimatedPositioned(
      duration: Duration(milliseconds: 200),
      left: 0.0,
      right: 0.0,
      bottom: _bottomNavigatorPosition,
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 20.0),
        child: PageNavigator(
          primaryButtonText: 'Create new event',
          onPressedPrimaryButton: () {
            Navigator.of(context).pushNamed(Routes.EVENT_CREATE);
          },
        ),
      ),
    );
  }
}

tabview_item.dart

代码语言:javascript
运行
复制
class MyEventsPage extends StatefulWidget {
  MyEventsPage({Key key}) : super(key: key);

  @override
  _MyEventsPageState createState() => _MyEventsPageState();
}

class _MyEventsPageState extends State<MyEventsPage>
    with AutomaticKeepAliveClientMixin<MyEventsPage> {
  Completer<void> _refreshCompleter;
  PaginatedEvent _paginated;
  MyEventsBloc _myEventsBloc;
  bool _isFetchingMoreInBackground;

  @override
  void initState() {
    super.initState();
    _myEventsBloc = BlocProvider.of<MyEventsBloc>(context);
    _myEventsBloc.add(MyEventsPageInitialized());
    _refreshCompleter = Completer<void>();
    _isFetchingMoreInBackground = false;
  }

  void _set(PaginatedEvent paginated) {
    setState(() {
      _paginated = paginated;
    });
    _refreshCompleter?.complete();
    _refreshCompleter = Completer();
  }

  void _add(Event data) {
    setState(() {
      _paginated.data.add(data);
    });
  }

  void _update(Event data) {
    final int index = _paginated.data.indexWhere((leave) {
      return leave.id == data.id;
    });

    setState(() {
      _paginated.data[index] = data;
    });
  }

  void _destroy(Event data) {
    final int index = _paginated.data.indexWhere((leave) {
      return leave.id == data.id;
    });

    setState(() {
      _paginated.data.removeAt(index);
    });
  }

  void _append(PaginatedEvent paginated) {
    setState(() {
      _paginated.currentPage = paginated.currentPage;
      _paginated.data.addAll(paginated.data);
    });
  }

  bool _onScrollNotification(ScrollNotification notification) {
    if (notification is! ScrollEndNotification) return false;

    print('extentBefore: ${notification.metrics.extentBefore}');
    print('extentAfter: ${notification.metrics.extentAfter}');
    print('maxScrollExtent: ${notification.metrics.maxScrollExtent}');
    return true;
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return RefreshIndicator(
      onRefresh: () {
        _myEventsBloc.add(MyEventsRefreshRequested());
        return _refreshCompleter.future;
      },
      child: NotificationListener<ScrollNotification>(
        onNotification: _onScrollNotification,
        child: CustomScrollView(
          slivers: [
            SliverOverlapInjector(
              handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
            ),
            SliverToBoxAdapter(
              child: BlocConsumer<MyEventsBloc, MyEventsState>(
                listener: (context, state) {
                  if (state is MyEventsLoadSuccess) {
                    _set(state.data);
                  }

                  if (state is MyEventsCreateSuccess) {
                    _add(state.data);
                  }

                  if (state is MyEventsUpdateSuccess) {
                    _update(state.data);
                  }

                  if (state is MyEventsDestroySuccess) {
                    _destroy(state.data);
                  }

                  if (state is MyEventsLoadMoreSuccess) {
                    _append(state.data);
                  }
                },
                builder: (context, state) {
                  if (state is MyEventsLoadSuccess) {
                    return EventList(data: _paginated.data);
                  }

                  return ListLoading();
                },
              ),
            ),
          ],
        ),
      ),
    );
  }

  @override
  bool get wantKeepAlive => true;
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-09-01 23:58:21

在做了一些研究之后,终于找到了自己的答案。这不是一个完美的解决方案,但有效。

代码语言:javascript
运行
复制
bool _onScrollNotification(UserScrollNotification notification) {
  /// Make sure it listening to the nearest depth of scrollable views
  /// and ignore notifications if scroll axis is not vertical.
  if (notification.depth == 0 && notification.metrics.axis == Axis.vertical) {
    ScrollDirection direction = notification.direction;
    if (direction == ScrollDirection.reverse && !_isFetchingMoreData) {
      /// Check if the user is scrolling the list downward to prevent
      /// function call on upward. Also check if there is any fetch
      /// queues, if it still fetching, skip this step and do nothing.
      /// It was necessary to prevent the notification to bubble up
      /// the widget with `_loadMoreData()` call.
      if (_paginated.currentPage < _paginated.lastPage)
        /// If the conditions above are passed, we are safe to load more.
        return _loadMoreData();
    }
  }
  return true;
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63692774

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档