This commit is contained in:
2026-07-12 04:33:48 +08:00
parent f8a90ad305
commit e7ce0f7bae
151 changed files with 2765 additions and 2947 deletions

View File

@@ -30,7 +30,7 @@ class SlidingUpPanel extends StatefulWidget {
/// otherwise, [collapsed] will be displayed overtop
/// of this Widget. If [panel] and [panelBuilder] are both non-null,
/// [panel] will be used.
final Widget panel;
final Widget? panel;
/// WARNING: This feature is still in beta and is subject to change without
/// notice. Stability is not gauranteed. Provides a [ScrollController] and
@@ -38,28 +38,28 @@ class SlidingUpPanel extends StatefulWidget {
/// the panel position with the scroll position. Useful for implementing an
/// infinite scroll behavior. If [panel] and [panelBuilder] are both non-null,
/// [panel] will be used.
final Widget Function(ScrollController sc) panelBuilder;
final Widget? Function(ScrollController sc)? panelBuilder;
/// The Widget displayed overtop the [panel] when collapsed.
/// This fades out as the panel is opened.
final Widget collapsed;
final Widget? collapsed;
/// The Widget that lies underneath the sliding panel.
/// This Widget automatically sizes itself
/// to fill the screen.
final Widget body;
final Widget? body;
/// Optional persistent widget that floats above the [panel] and attaches
/// to the top of the [panel]. Content at the top of the panel will be covered
/// by this widget. Add padding to the bottom of the `panel` to
/// avoid coverage.
final Widget header;
final Widget? header;
/// Optional persistent widget that floats above the [panel] and
/// attaches to the bottom of the [panel]. Content at the bottom of the panel
/// will be covered by this widget. Add padding to the bottom of the `panel`
/// to avoid coverage.
final Widget footer;
final Widget? footer;
/// The height of the sliding panel when fully collapsed.
final double minHeight;
@@ -72,13 +72,13 @@ class SlidingUpPanel extends StatefulWidget {
/// and go directly to the open/close position. This value is represented as a
/// percentage of the total animation distance ([maxHeight] - [minHeight]),
/// so it must be between 0.0 and 1.0, exclusive.
final double snapPoint;
final double? snapPoint;
/// A border to draw around the sliding panel sheet.
final Border border;
final Border? border;
/// If non-null, the corners of the sliding panel sheet are rounded by this [BorderRadiusGeometry].
final BorderRadiusGeometry borderRadius;
final BorderRadiusGeometry? borderRadius;
/// A list of shadows cast behind the sliding panel sheet.
final List<BoxShadow> boxShadow;
@@ -87,10 +87,10 @@ class SlidingUpPanel extends StatefulWidget {
final Color color;
/// The amount to inset the children of the sliding panel sheet.
final EdgeInsetsGeometry padding;
final EdgeInsetsGeometry? padding;
/// Empty space surrounding the sliding panel sheet.
final EdgeInsetsGeometry margin;
final EdgeInsetsGeometry? margin;
/// Set to false to not to render the sheet the [panel] sits upon.
/// This means that only the [body], [collapsed], and the [panel]
@@ -104,7 +104,7 @@ class SlidingUpPanel extends StatefulWidget {
final bool panelSnapping;
/// If non-null, this can be used to control the state of the panel.
final PanelController controller;
final PanelController? controller;
/// If non-null, shows a darkening shadow over the [body] as the panel slides open.
final bool backdropEnabled;
@@ -125,15 +125,15 @@ class SlidingUpPanel extends StatefulWidget {
/// is called as the panel slides around with the
/// current position of the panel. The position is a double
/// between 0.0 and 1.0 where 0.0 is fully collapsed and 1.0 is fully open.
final void Function(double position) onPanelSlide;
final void Function(double position)? onPanelSlide;
/// If non-null, this callback is called when the
/// panel is fully opened
final VoidCallback onPanelOpened;
final VoidCallback? onPanelOpened;
/// If non-null, this callback is called when the panel
/// is fully collapsed.
final VoidCallback onPanelClosed;
final VoidCallback? onPanelClosed;
/// If non-null and true, the SlidingUpPanel exhibits a
/// parallax effect as the panel slides up. Essentially,
@@ -164,7 +164,7 @@ class SlidingUpPanel extends StatefulWidget {
final PanelState defaultPanelState;
SlidingUpPanel({
Key key,
Key? key,
this.panel,
this.panelBuilder,
this.body,
@@ -211,9 +211,9 @@ class SlidingUpPanel extends StatefulWidget {
class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProviderStateMixin{
AnimationController _ac;
AnimationController? _ac;
ScrollController _sc;
ScrollController? _sc;
bool _scrollingEnabled = false;
VelocityTracker _vt = VelocityTracker.withKind(PointerDeviceKind.touch);
@@ -228,19 +228,19 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
duration: const Duration(milliseconds: 300),
value: widget.defaultPanelState == PanelState.CLOSED ? 0.0 : 1.0 //set the default panel state (i.e. set initial value of _ac)
)..addListener((){
if(widget.onPanelSlide != null) widget.onPanelSlide(_ac.value);
if(widget.onPanelSlide != null) widget.onPanelSlide!(_ac!.value);
if(widget.onPanelOpened != null && _ac.value == 1.0) widget.onPanelOpened();
if(widget.onPanelOpened != null && _ac!.value == 1.0) widget.onPanelOpened!();
if(widget.onPanelClosed != null && _ac.value == 0.0) widget.onPanelClosed();
if(widget.onPanelClosed != null && _ac!.value == 0.0) widget.onPanelClosed!();
});
// prevent the panel content from being scrolled only if the widget is
// draggable and panel scrolling is enabled
_sc = new ScrollController();
_sc.addListener((){
if(widget.isDraggable && !_scrollingEnabled)
_sc.jumpTo(0);
_sc!.addListener((){
if(widget.isDraggable! && !_scrollingEnabled)
_sc!.jumpTo(0);
});
widget.controller?._addState(this);
@@ -254,11 +254,11 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
//make the back widget take up the entire back side
widget.body != null ? AnimatedBuilder(
animation: _ac,
animation: _ac!,
builder: (context, child){
return Positioned(
top: widget.parallaxEnabled ? _getParallax() : 0.0,
child: child,
top: widget.parallaxEnabled! ? _getParallax() : 0.0,
child: child!,
);
},
child: Container(
@@ -278,7 +278,7 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
} : null,
onTap: widget.backdropTapClosesPanel ? () => _close() : null,
child: AnimatedBuilder(
animation: _ac,
animation: _ac!,
builder: (context, _) {
return Container(
height: MediaQuery.of(context).size.height,
@@ -287,7 +287,7 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
//set color to null so that touch events pass through
//to the body when the panel is closed, otherwise,
//if a color exists, then touch events won't go through
color: _ac.value == 0.0 ? null : widget.backdropColor.withOpacity(widget.backdropOpacity * _ac.value),
color: _ac!.value == 0.0 ? null : widget.backdropColor.withOpacity(widget.backdropOpacity * _ac!.value),
);
}
),
@@ -296,10 +296,10 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
//the actual sliding part
!_isPanelVisible ? Container() : _gestureHandler(
child: AnimatedBuilder(
animation: _ac,
animation: _ac!,
builder: (context, child) {
return Container(
height: _ac.value * (widget.maxHeight - widget.minHeight) + widget.minHeight,
height: _ac!.value * (widget.maxHeight - widget.minHeight) + widget.minHeight,
margin: widget.margin,
padding: widget.padding,
decoration: widget.renderPanelSheet ? BoxDecoration(
@@ -320,13 +320,13 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
top: widget.slideDirection == SlideDirection.UP ? 0.0 : null,
bottom: widget.slideDirection == SlideDirection.DOWN ? 0.0 : null,
width: MediaQuery.of(context).size.width -
(widget.margin != null ? widget.margin.horizontal : 0) -
(widget.padding != null ? widget.padding.horizontal : 0),
(widget.margin != null ? widget.margin!.horizontal : 0) -
(widget.padding != null ? widget.padding!.horizontal : 0),
child: Container(
height: widget.maxHeight,
child: widget.panel != null
? widget.panel
: widget.panelBuilder(_sc),
: widget.panelBuilder!(_sc!),
),
),
@@ -334,14 +334,14 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
widget.header != null ? Positioned(
top: widget.slideDirection == SlideDirection.UP ? 0.0 : null,
bottom: widget.slideDirection == SlideDirection.DOWN ? 0.0 : null,
child: widget.header,
child: widget.header!,
) : Container(),
// footer
widget.footer != null ? Positioned(
top: widget.slideDirection == SlideDirection.UP ? null : 0.0,
bottom: widget.slideDirection == SlideDirection.DOWN ? null : 0.0,
child: widget.footer
child: widget.footer!
) : Container(),
// collapsed panel
@@ -349,12 +349,12 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
top: widget.slideDirection == SlideDirection.UP ? 0.0 : null,
bottom: widget.slideDirection == SlideDirection.DOWN ? 0.0 : null,
width: MediaQuery.of(context).size.width -
(widget.margin != null ? widget.margin.horizontal : 0) -
(widget.padding != null ? widget.padding.horizontal : 0),
(widget.margin != null ? widget.margin!.horizontal : 0) -
(widget.padding != null ? widget.padding!.horizontal : 0),
child: Container(
height: widget.minHeight,
child: widget.collapsed == null ? Container() : FadeTransition(
opacity: Tween(begin: 1.0, end: 0.0).animate(_ac),
opacity: Tween(begin: 1.0, end: 0.0).animate(_ac!),
// if the panel is open ignore pointers (touch events) on the collapsed
// child so that way touch events go through to whatever is underneath
@@ -375,22 +375,22 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
@override
void dispose(){
_ac.dispose();
_ac?.dispose();
super.dispose();
}
double _getParallax(){
if(widget.slideDirection == SlideDirection.UP)
return -_ac.value * (widget.maxHeight - widget.minHeight) * widget.parallaxOffset;
return -_ac!.value * (widget.maxHeight - widget.minHeight) * widget.parallaxOffset;
else
return _ac.value * (widget.maxHeight - widget.minHeight) * widget.parallaxOffset;
return _ac!.value * (widget.maxHeight - widget.minHeight) * widget.parallaxOffset;
}
// returns a gesture detector if panel is used
// and a listener if panelBuilder is used.
// this is because the listener is designed only for use with linking the scrolling of
// panels and using it for panels that don't want to linked scrolling yields odd results
Widget _gestureHandler({Widget child}){
Widget _gestureHandler({required Widget child}){
if (!widget.isDraggable) return child;
if (widget.panel != null){
@@ -418,15 +418,15 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
// only slide the panel if scrolling is not enabled
if(!_scrollingEnabled){
if(widget.slideDirection == SlideDirection.UP)
_ac.value -= dy / (widget.maxHeight - widget.minHeight);
_ac!.value -= dy / (widget.maxHeight - widget.minHeight);
else
_ac.value += dy / (widget.maxHeight - widget.minHeight);
_ac!.value += dy / (widget.maxHeight - widget.minHeight);
}
// if the panel is open and the user hasn't scrolled, we need to determine
// whether to enable scrolling if the user swipes up, or disable closing and
// begin to close the panel if the user swipes down
if(_isPanelOpen && _sc.hasClients && _sc.offset <= 0){
if(_isPanelOpen && _sc!.hasClients && _sc!.offset <= 0){
setState(() {
if(dy < 0){
_scrollingEnabled = true;
@@ -443,7 +443,7 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
double kSnap = 8;
//let the current animation finish before starting a new one
if(_ac.isAnimating) return;
if(_ac!.isAnimating) return;
// if scrolling is allowed and the panel is open, we don't want to close
// the panel if they swipe up on the scrollable
@@ -458,9 +458,9 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
// get minimum distances to figure out where the panel is at
double d2Close = _ac.value;
double d2Open = 1 - _ac.value;
double d2Snap = ((widget.snapPoint ?? 3) -_ac.value).abs(); // large value if null results in not every being the min
double d2Close = _ac!.value;
double d2Open = 1 - _ac!.value;
double d2Snap = ((widget.snapPoint ?? 3) -_ac!.value).abs(); // large value if null results in not every being the min
double minDistance = min(d2Close, min(d2Snap, d2Open));
// check if velocity is sufficient for a fling
@@ -469,18 +469,18 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
// snapPoint exists
if(widget.panelSnapping && widget.snapPoint != null){
if(v.pixelsPerSecond.dy.abs() >= kSnap*minFlingVelocity || minDistance == d2Snap)
_ac.fling(velocity: visualVelocity);
_ac!.fling(velocity: visualVelocity);
else
_flingPanelToPosition(widget.snapPoint, visualVelocity);
_flingPanelToPosition(widget.snapPoint!, visualVelocity);
// no snap point exists
}else if(widget.panelSnapping){
_ac.fling(velocity: visualVelocity);
_ac!.fling(velocity: visualVelocity);
// panel snapping disabled
}else{
_ac.animateTo(
_ac.value + visualVelocity * 0.16,
_ac!.animateTo(
_ac!.value + visualVelocity * 0.16,
duration: Duration(milliseconds: 410),
curve: Curves.decelerate,
);
@@ -495,7 +495,7 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
if(minDistance == d2Close){
_close();
}else if(minDistance == d2Snap){
_flingPanelToPosition(widget.snapPoint, visualVelocity);
_flingPanelToPosition(widget.snapPoint!, visualVelocity);
}else{
_open();
}
@@ -510,12 +510,12 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
stiffness: 500.0,
ratio: 1.0,
),
_ac.value,
_ac!.value,
targetPos,
velocity
);
_ac.animateWith(simulation);
_ac!.animateWith(simulation);
}
//---------------------------------
@@ -524,17 +524,17 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
//close the panel
Future<void> _close(){
return _ac.fling(velocity: -1.0);
return _ac!.fling(velocity: -1.0);
}
//open the panel
Future<void> _open(){
return _ac.fling(velocity: 1.0);
return _ac!.fling(velocity: 1.0);
}
//hide the panel (completely offscreen)
Future<void> _hide(){
return _ac.fling(velocity: -1.0).then((x){
return _ac!.fling(velocity: -1.0).then((x){
setState(() {
_isPanelVisible = false;
});
@@ -543,7 +543,7 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
//show the panel (in collapsed mode)
Future<void> _show(){
return _ac.fling(velocity: -1.0).then((x){
return _ac!.fling(velocity: -1.0).then((x){
setState(() {
_isPanelVisible = true;
});
@@ -552,41 +552,41 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
//animate the panel position to value - must
//be between 0.0 and 1.0
Future<void> _animatePanelToPosition(double value, {Duration duration, Curve curve = Curves.linear}){
Future<void> _animatePanelToPosition(double value, {required Duration duration, Curve curve = Curves.linear}){
assert(0.0 <= value && value <= 1.0);
return _ac.animateTo(value, duration: duration, curve: curve);
return _ac!.animateTo(value, duration: duration, curve: curve);
}
//animate the panel position to the snap point
//REQUIRES that widget.snapPoint != null
Future<void> _animatePanelToSnapPoint({Duration duration, Curve curve = Curves.linear}){
Future<void> _animatePanelToSnapPoint({required Duration duration, Curve curve = Curves.linear}){
assert(widget.snapPoint != null);
return _ac.animateTo(widget.snapPoint, duration: duration, curve: curve);
return _ac!.animateTo(widget.snapPoint!, duration: duration, curve: curve);
}
//set the panel position to value - must
//be between 0.0 and 1.0
set _panelPosition(double value){
assert(0.0 <= value && value <= 1.0);
_ac.value = value;
_ac!.value = value;
}
//get the current panel position
//returns the % offset from collapsed state
//as a decimal between 0.0 and 1.0
double get _panelPosition => _ac.value;
double get _panelPosition => _ac!.value;
//returns whether or not
//the panel is still animating
bool get _isPanelAnimating => _ac.isAnimating;
bool get _isPanelAnimating => _ac!.isAnimating;
//returns whether or not the
//panel is open
bool get _isPanelOpen => _ac.value == 1.0;
bool get _isPanelOpen => _ac!.value == 1.0;
//returns whether or not the
//panel is closed
bool get _isPanelClosed => _ac.value == 0.0;
bool get _isPanelClosed => _ac!.value == 0.0;
//returns whether or not the
//panel is shown/hidden
@@ -602,7 +602,7 @@ class _SlidingUpPanelState extends State<SlidingUpPanel> with SingleTickerProvid
class PanelController{
_SlidingUpPanelState _panelState;
_SlidingUpPanelState? _panelState;
void _addState(_SlidingUpPanelState panelState){
this._panelState = panelState;
@@ -616,27 +616,27 @@ class PanelController{
/// Closes the sliding panel to its collapsed state (i.e. to the minHeight)
Future<void> close(){
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
return _panelState._close();
return _panelState!._close();
}
/// Opens the sliding panel fully
/// (i.e. to the maxHeight)
Future<void> open(){
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
return _panelState._open();
return _panelState!._open();
}
/// Hides the sliding panel (i.e. is invisible)
Future<void> hide(){
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
return _panelState._hide();
return _panelState!._hide();
}
/// Shows the sliding panel in its collapsed state
/// (i.e. "un-hide" the sliding panel)
Future<void> show(){
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
return _panelState._show();
return _panelState!._show();
}
/// Animates the panel position to the value.
@@ -644,20 +644,20 @@ class PanelController{
/// where 0.0 is fully collapsed and 1.0 is completely open.
/// (optional) duration specifies the time for the animation to complete
/// (optional) curve specifies the easing behavior of the animation.
Future<void> animatePanelToPosition(double value, {Duration duration, Curve curve = Curves.linear}){
Future<void> animatePanelToPosition(double value, {required Duration duration, Curve curve = Curves.linear}){
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
assert(0.0 <= value && value <= 1.0);
return _panelState._animatePanelToPosition(value, duration: duration, curve: curve);
return _panelState!._animatePanelToPosition(value, duration: duration, curve: curve);
}
/// Animates the panel position to the snap point
/// Requires that the SlidingUpPanel snapPoint property is not null
/// (optional) duration specifies the time for the animation to complete
/// (optional) curve specifies the easing behavior of the animation.
Future<void> animatePanelToSnapPoint({Duration duration, Curve curve = Curves.linear}){
Future<void> animatePanelToSnapPoint({required Duration duration, Curve curve = Curves.linear}){
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
assert(_panelState.widget.snapPoint != null, "SlidingUpPanel snapPoint property must not be null");
return _panelState._animatePanelToSnapPoint(duration: duration, curve: curve);
assert(_panelState!.widget.snapPoint != null, "SlidingUpPanel snapPoint property must not be null");
return _panelState!._animatePanelToSnapPoint(duration: duration, curve: curve);
}
/// Sets the panel position (without animation).
@@ -666,7 +666,7 @@ class PanelController{
set panelPosition(double value){
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
assert(0.0 <= value && value <= 1.0);
_panelState._panelPosition = value;
_panelState!._panelPosition = value;
}
/// Gets the current panel position.
@@ -677,35 +677,35 @@ class PanelController{
/// 1.0 is full open.
double get panelPosition{
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
return _panelState._panelPosition;
return _panelState!._panelPosition;
}
/// Returns whether or not the panel is
/// currently animating.
bool get isPanelAnimating{
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
return _panelState._isPanelAnimating;
return _panelState!._isPanelAnimating;
}
/// Returns whether or not the
/// panel is open.
bool get isPanelOpen{
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
return _panelState._isPanelOpen;
return _panelState!._isPanelOpen;
}
/// Returns whether or not the
/// panel is closed.
bool get isPanelClosed{
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
return _panelState._isPanelClosed;
return _panelState!._isPanelClosed;
}
/// Returns whether or not the
/// panel is shown/hidden.
bool get isPanelShown{
assert(isAttached, "PanelController must be attached to a SlidingUpPanel");
return _panelState._isPanelShown;
return _panelState!._isPanelShown;
}
}