New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RefreshIndicator syncs scrollposition across tabs when using TabbarView with AutomaticKeepAliveClientMixin inside NestedScrollView #62833
Comments
you can try to use this packge |
Unfortunately, it seems does not solve these problems, am I right ?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> with SingleTickerProviderStateMixin {
final List<String> _tabs = [
"AAA",
"BBB",
"CCC",
"DDD",
"EEE",
"FFF",
];
TabController tcl;
@override
void initState() {
super.initState();
tcl = TabController(length: _tabs.length, vsync: this);
}
@override
void dispose() {
tcl.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final double statusBarHeight = MediaQuery.of(context).padding.top;
final double pinnedHeaderHeight = statusBarHeight + kToolbarHeight;
return extended.NestedScrollView(
pinnedHeaderSliverHeightBuilder: () {
return pinnedHeaderHeight;
},
innerScrollPositionKeyBuilder: () => Key('${_tabs[tcl.index]}'),
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,
snap: true,
// 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(
controller: tcl,
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(
controller: tcl,
// 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(key: Key(name)),
),
],
), onRefresh: () => Future.delayed(Duration(seconds: 5)));
},
),
);
}).toList(),
),
);
}
}
class TabPage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _TabPageState();
}
TabPage({Key key}) : super(key: key);
}
class _TabPageState extends State<TabPage> with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
super.build(context);
return extended.NestedScrollViewInnerScrollPositionKeyWidget(
widget.key,
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,
),
));
}
} |
Code Sample
flutter doctor -v
|
Any solution or workaround at this moment? |
There is this post on StackOverflow which is two years old and doesn't seem to fix the problem... https://stackoverflow.com/questions/52264270/refreshindicator-with-nestedscrollview |
Bump this |
Any fix for this ? |
any update ? |
anyone? |
Have the same issue. fixed already? |
but for retain each page inside tabbarview, I use AutomaticKeepAliveClientMixin and wantKeepAlive set to true, this is my problem been debugging checking hash codes are all unique its definitely a problem with using tabs auto keep alive and refresh indicator |
很困扰,希望可以尽快修复或者给出解决方案。 |
Hi, |
Initialized RefreshController & onRefresh function inside the Widget build(Context context)... . like this
@OverRide
... It works for me. |
Facing exactly same issue. All the refreshindicators are triggered together |
Hello @tamzidpeace what's a RefreshController? Were you facing the same issue, does pulling a refresh on one of the children of TabBarView trigger a refresh on all the other children? |
In smart refresh, you need a RefreshController. |
Any update on the solution or work around. I have exactly same issue as it is mentioned in the problem title above "RefreshIndicator has issues when using TabbarView with AutomaticKeepAliveClientMixin inside NestedScrollView". It calls all the refresh indicator at once no matter from which page you are refreshing. |
Any Update? |
1 similar comment
Any Update? |
Developers we need an update. |
Reproducible on stable 3.3 and master 3.7
code sampleimport 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required 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'),
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(
children: _tabs.map((String name) {
return SafeArea(
top: false,
bottom: false,
child: Builder(
builder: (BuildContext context) {
return RefreshIndicator(
child: CustomScrollView(
key: PageStorageKey<String>(name),
slivers: const <Widget>[
// SliverOverlapInjector(
// // This is the flip side of the SliverOverlapAbsorber
// // above.
// handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
// ),
SliverPadding(
padding: EdgeInsets.all(0),
sliver: TabPage(),
),
],
),
onRefresh: () =>
Future.delayed(const Duration(seconds: 5)));
},
),
);
}).toList(),
),
),
);
}
}
class TabPage extends StatefulWidget {
const TabPage({super.key});
@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(
itemExtent: 48.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Material(
child: ListTile(
title: Text('Item $index'),
));
},
childCount: 30,
),
);
}
}
flutter doctor -v (mac)
|
Referring to OP here:
This should be fixed by #127718
The root of this issue is #40740, so I am going to close this issue to refer there for updates, we try to consolidate dupes to focus everything in one issue. Thanks for reporting and providing updates! Let's follow up over there. :) |
#### (plus some more docs) Fixes #76760 Fixes #82391 Fixes #45619 Fixes #117316 Fixes #110956 Fixes #127282 Fixes #32563 Fixes #46089 Fixes #79077 Part of fixing #62833 This fixes (a bunch of) issues that have been reported differently over the years, but all have the same root cause. Sometimes the NestedScrollView would incorrectly calculate whether or not there is enough content to allow scrolling. This would only apply to drag scrolling (not mouse wheel scrolling for example). This did not relate to how the extent of the NestedScrollView is computed, but just the logic that enabled the actual drag gestures. This fixes that. :)
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of |
Use doc sample code for NestedScrollView , but have many problem.
The text was updated successfully, but these errors were encountered: