SliverChildBuilderDelegate 内的 PageView 快速滚动时从最后一页开始

问题描述 投票:0回答:1

我需要一个带有 TabBarView 的浮动 SliverAppBar。每个选项卡都有一个 CustomScrollView 来滚动它的子项。如果我将 PageView 作为子项并且快速滚动,则 PageView 从最后一页而不是第一页开始。我在 flutter 文档中找到了示例代码:

https://api.flutter.dev/flutter/widgets/NestedScrollView-class.html

我刚刚将

ListTile
内的
SliverChildBuilderDelegate
更改为
PageView
。但是当我测试这段代码并快速滚动时,pageView 从最后一页开始。

这是一个演示

https://gyazo.com/79709286236fa3dbf1f88b1e92f5cee3

这是我的代码:


// This example shows a [NestedScrollView] whose header is the combination of a
// [TabBar] in a [SliverAppBar] and whose body is a [TabBarView]. It uses a
// [SliverOverlapAbsorber]/[SliverOverlapInjector] pair to make the inner lists
// align correctly, and it uses [SafeArea] to avoid any horizontal disturbances
// (e.g. the "notch" on iOS when the phone is horizontal). In addition,
// [PageStorageKey]s are used to remember the scroll position of each tab's
// list.

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

/// This is the main application widget.
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: _title,
      home: MyStatelessWidget(),
    );
  }
}

/// This is the stateless widget that the main application instantiates.
class MyStatelessWidget extends StatelessWidget {
  const MyStatelessWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final List<String> _tabs = <String>['Tab 1', 'Tab 2'];
    return DefaultTabController(
      length: _tabs.length, // This is the number of tabs.
      child: Scaffold(
        body: NestedScrollView(
          headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
            // These are the slivers that show up in the "outer" scroll view.
            return <Widget>[
              SliverOverlapAbsorber(
                // This widget takes the overlapping behavior of the SliverAppBar,
                // and redirects it to the SliverOverlapInjector below. If it is
                // missing, then it is possible for the nested "inner" scroll view
                // below to end up under the SliverAppBar even when the inner
                // scroll view thinks it has not been scrolled.
                // This is not necessary if the "headerSliverBuilder" only builds
                // widgets that do not overlap the next sliver.
                handle:
                    NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                sliver: SliverAppBar(
                  title:
                      const Text('Books'), // This is the title in the app bar.
                  pinned: true,
                  floating: true,
                  expandedHeight: 150.0,
                  // The "forceElevated" property causes the SliverAppBar to show
                  // a shadow. The "innerBoxIsScrolled" parameter is true when the
                  // inner scroll view is scrolled beyond its "zero" point, i.e.
                  // when it appears to be scrolled below the SliverAppBar.
                  // Without this, there are cases where the shadow would appear
                  // or not appear inappropriately, because the SliverAppBar is
                  // not actually aware of the precise position of the inner
                  // scroll views.
                  forceElevated: innerBoxIsScrolled,
                  bottom: TabBar(
                    // These are the widgets to put in each tab in the tab bar.
                    tabs: _tabs.map((String name) => Tab(text: name)).toList(),
                  ),
                ),
              ),
            ];
          },
          body: TabBarView(
            // These are the contents of the tab views, below the tabs.
            children: _tabs.map((String name) {
              return SafeArea(
                top: false,
                bottom: false,
                child: Builder(
                  // This Builder is needed to provide a BuildContext that is
                  // "inside" the NestedScrollView, so that
                  // sliverOverlapAbsorberHandleFor() can find the
                  // NestedScrollView.
                  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>[
                        SliverOverlapInjector(
                          // This is the flip side of the SliverOverlapAbsorber
                          // above.
                          handle:
                              NestedScrollView.sliverOverlapAbsorberHandleFor(
                                  context),
                        ),
                        SliverPadding(
                          padding: const EdgeInsets.all(8.0),
                          // In this example, the inner scroll view has
                          // fixed-height list items, hence the use of
                          // SliverFixedExtentList. However, one could use any
                          // sliver widget here, e.g. SliverList or SliverGrid.
                          sliver: SliverFixedExtentList(
                            // The items in this example are fixed to 48 pixels
                            // high. This matches the Material Design spec for
                            // ListTile widgets.
                            itemExtent: 48.0,
                            delegate: SliverChildBuilderDelegate(
                              (BuildContext context, int index) {
                                // This builder is called for each child.
                                // In this example, we just number each list item.
                                return PageView(
                                  /// [PageView.scrollDirection] defaults to [Axis.horizontal].
                                  /// Use [Axis.vertical] to scroll vertically.
                                  scrollDirection: Axis.horizontal,

                                  children: const <Widget>[
                                    Center(
                                      child: Text('First Page'),
                                    ),
                                    Center(
                                      child: Text('Second Page'),
                                    ),
                                    Center(
                                      child: Text('Third Page'),
                                    )
                                  ],
                                );
                              },
                              // The childCount of the SliverChildBuilderDelegate
                              // specifies how many children this inner list
                              // has. In this example, each tab has a list of
                              // exactly 30 items, but this is arbitrary.
                              childCount: 100,
                            ),
                          ),
                        ),
                      ],
                    );
                  },
                ),
              );
            }).toList(),
          ),
        ),
      ),
    );
  }
}

如何使 pageView 从第一页开始?我尝试将其设置为 pageView

controller: PageController(initialPage: 0),
但它不起作用。

flutter flutter-layout
1个回答
0
投票

我有同样的问题,我找到了解决方案。您只需将

PageStorageKey 
添加到所有滚动视图即可。对我来说效果很好

CustomScrollView(
              key: PageStorageKey<String>(S.current.news),
              controller: _scrollController,

里面ff

PageView.builder(
                    key: PageStorageKey<String>('feed-post-${widget.feedItem.id}'),
                    scrollDirection: Axis.horizontal,
                    controller: _pageController,
                    itemCount: widget.feedItem.imageLinks.length,
                    onPageChanged: (index) => _currentIndex.value = index,
                    itemBuilder: (ctx, i) => BaseImage(
                      key: Key(widget.feedItem.imageLinks[i]),
                      width: double.infinity,
                      img: widget.feedItem.imageLinks[i],
                      fit: BoxFit.fitHeight,
                    ),
                  ),
ListView.builder(
                        key: PageStorageKey<String>('post-position-${widget.feedItem.id}'),
                        scrollDirection: Axis.horizontal,
                        controller: _scrollController,
                        itemCount: widget.feedItem.imageLinks.length,
                        shrinkWrap: true,
                        physics: const NeverScrollableScrollPhysics(),
                        itemBuilder: (context, index) => Padding(
                          padding: const EdgeInsets.only(right: 3),
                          child: AnimatedContainer(
                            duration: const Duration(milliseconds: 300),
                            curve: Curves.slowMiddle,
                            width: 7,
                            height: 7,
                            decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(50),
                              color:
                                  value == index ? _layout.theme.primary : _layout.theme.lightText,
                            ),
                          ),
                        ),
                      ),
© www.soinside.com 2019 - 2024. All rights reserved.