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
Custom ScrollPhysics breaks NestedScrollView #34316
Comments
same issue.I want to do Refresh under SliverAppBar with NestedScrollView,though I add this line in applyUserOffset method can make me work.
but if body has multiple positions,it will crash |
code sampleimport 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Space',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key key}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
ScrollController _scrollController;
final List<String> _tabs = ["Earth", "Mars", "Venus"];
@override
void initState() {
super.initState();
_scrollController = ScrollController();
_scrollController.addListener(() {
print(
"${_scrollController.offset} | ${_scrollController.position.maxScrollExtent}");
});
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: DefaultTabController(
length: _tabs.length, // This is the number of tabs.
child: NestedScrollView(
physics: SnapToEndsPhysics(),
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(
'Planets'), // This is the title in the app bar.
pinned: true,
expandedHeight: 350.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),
physics:
ClampingScrollPhysics(), //set this explcitly to Androids to make issue more obvious on iOS
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 ListTile(
title: Text('$name $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,
),
),
),
],
);
},
),
);
}).toList(),
),
),
)),
);
}
}
/// A snapping physics that always lands at the bottom of the scrollable only
class SnapToEndsPhysics extends ScrollPhysics {
/// Creates a scroll physics that always lands on top or bottom of scrollable.
const SnapToEndsPhysics({ScrollPhysics parent}) : super(parent: parent);
@override
ScrollPhysics applyTo(ScrollPhysics ancestor) {
return SnapToEndsPhysics(parent: buildParent(ancestor));
}
@override
Simulation createBallisticSimulation(
ScrollMetrics position, double velocity) {
print("vel: $velocity pos: $position");
final target = 0.0;
var sim = ScrollSpringSimulation(spring, position.pixels, target, 0,
tolerance: tolerance);
return sim;
}
}
flutter doctor -v[✓] Flutter (Channel stable, 1.22.5, on macOS 11.1 20C69 darwin-x64, locale en-GB)
• Flutter version 1.22.5 at /Users/tahatesser/Code/flutter_stable
• Framework revision 7891006299 (3 weeks ago), 2020-12-10 11:54:40 -0800
• Engine revision ae90085a84
• Dart version 2.10.4
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
• Android SDK at /Volumes/Extreme/sdk
• Platform android-30, build-tools 30.0.3
• ANDROID_HOME = /Volumes/Extreme/sdk
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 12.3)
• Xcode at /Volumes/Extreme/Xcode.app/Contents/Developer
• Xcode 12.3, Build version 12C33
• CocoaPods version 1.10.0
[!] Android Studio (version 4.1)
• Android Studio at /Applications/Android Studio.app/Contents
✗ Flutter plugin not installed; this adds Flutter specific functionality.
✗ Dart plugin not installed; this adds Dart specific functionality.
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
[✓] VS Code (version 1.52.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.18.0
[✓] Connected device (2 available)
• Taha’s iPad (mobile) • 00008020-000255113EE8402E • ios • iOS 14.3
• iPhone 12 (mobile) • 12D8FF8E-2815-436E-9951-B8A6A42E4ACF • ios • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
! Doctor found issues in 1 category. [✓] Flutter (Channel master, 1.26.0-2.0.pre.145, on macOS 11.1 20C69 darwin-x64, locale en-GB)
• Flutter version 1.26.0-2.0.pre.145 at /Users/tahatesser/Code/flutter_master
• Framework revision 38fe7e2b1c (9 hours ago), 2020-12-28 21:39:03 -0500
• Engine revision 892034dc6a
• Dart version 2.12.0 (build 2.12.0-179.0.dev)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
• Android SDK at /Volumes/Extreme/sdk
• Platform android-30, build-tools 30.0.3
• ANDROID_HOME = /Volumes/Extreme/sdk
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 12.3)
• Xcode at /Volumes/Extreme/Xcode.app/Contents/Developer
• Xcode 12.3, Build version 12C33
• CocoaPods version 1.10.0
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 4.1)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
[✓] VS Code (version 1.52.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.18.0
[✓] Connected device (4 available)
• Taha’s iPad (mobile) • 00008020-000255113EE8402E • ios • iOS 14.3
• iPhone 12 (mobile) • 12D8FF8E-2815-436E-9951-B8A6A42E4ACF • ios • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
• macOS (desktop) • macos • darwin-x64 • macOS 11.1 20C69 darwin-x64
• Chrome (web) • chrome • web-javascript • Google Chrome 87.0.4280.88
• No issues found! |
Same issue for me and relative to other issues |
Reproduces on stable 3.3 and master 3.7, Spring simulation doesn't work. Screen.Recording.2022-12-14.at.8.05.47.PM.movflutter doctor -v (mac)
|
Steps to Reproduce
I have a small app here: https://github.com/maks/flutter-space
that demonstrates the issue.
Its basically just the example code from the NestedScrollView documentation but using a custom Physics class that just uses the ScrollSpringSimulation to try to force the AppBar to always snap back to the fully expanded position.
What I expect to happen:
is that the Appbar always snaps back to the fully expanded position, using:
What actually happens:
When the user fling is low velocity or scroll gesture is slow, the Appbar behaves as expected.
BUT if there is a fast enough fling or rapid scroll gesture, it stops the processing of the physics simulation for some reason, making the Appbar "stuck" in a partially expanded state. Touching anywhere causes the simulation to be run again and then it correctly scrolls back to the fully expanded position.
I've observed this on Android and iOS, and explicitly set the physics for the "inner" CustomScrollView to ClampingScrollPhysics just to demonstrate the issue more easily on iOS without running into other issues such as #33367, though that issue may be somewhat related to this one.
Logs
The text was updated successfully, but these errors were encountered: