Skip to content

RefreshIndicator syncs scrollposition across tabs when using TabbarView with AutomaticKeepAliveClientMixin inside NestedScrollView #62833

@progratient

Description

@progratient

Use doc sample code for NestedScrollView , but have many problem.

  • If you don't set SliderAppBar's expandedHeight, listview inside tabbarview can't scroll vertical.
  • so I comment SliverOverlapAbsorber and SliverOverlapInjector code and can scroll vertical,
  • but for retain each page inside tabbarview, I use AutomaticKeepAliveClientMixin and wantKeepAlive set to true,
  • every page have a RefreshIndicator , when swip to refresh one page, other all page trigger onRefresh and show refresh progress at the same time and also all page scroll position all sync to the same position.
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  final List<String> _tabs = [
    "AAA",
    "BBB",
    "CCC",
    "DDD",
    "EEE",
    "FFF",
  ];

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: _tabs.length, // This is the number of tabs.
      child: 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: false,
                snap: false,
                // 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(
                  isScrollable: true,
                  // 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 RefreshIndicator(child: 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(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: TabPage(),
                      ),
                    ],
                  ), onRefresh: () => Future.delayed(Duration(seconds: 5)));
                },
              ),
            );
          }).toList(),
        ),
      ),
    );

  }
}

class TabPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _TabPageState();
  }

}

class _TabPageState extends State<TabPage> with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return 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 Material(child: ListTile(
            title: Text('Item $index'),
          ));
        },
        // 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: 30,
      ),
    );
  }
}

```shell
D:\flutter-projects\my_flutter_app>flutter doctor -v
[√] Flutter (Channel stable, v1.17.5, on Microsoft Windows [Version 10.0.18363.959], locale zh-CN)
    • Flutter version 1.17.5 at D:\flutter
    • Framework revision 8af6b2f038 (5 weeks ago), 2020-06-30 12:53:55 -0700Engine revision ee76268252
    • Dart version 2.8.4


[√] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
    • Android SDK at D:\Android\SdkPlatform android-29, build-tools 29.0.3ANDROID_HOME = D:\Android\SdkJava binary at: D:\Android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)
    • All Android licenses accepted.

[√] Android Studio (version 4.0)
    • Android Studio at D:\Android\Android StudioFlutter plugin version 48.0.2Dart plugin version 193.7361Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)

[√] Connected device (1 available)
    • SM G973U1R38M202PE2A • android-arm64 • Android 10 (API 29)

• No issues found!

D:\flutter-projects\my_flutter_app>

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listf: material designflutter/packages/flutter/material repository.f: scrollingViewports, list views, slivers, etc.found in release: 3.3Found to occur in 3.3found in release: 3.7Found to occur in 3.7frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work on

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions