Skip to content

Commit

Permalink
Lift SheetViewport above sheet widget
Browse files Browse the repository at this point in the history
  • Loading branch information
fujidaiti committed Nov 23, 2024
1 parent 1166df3 commit 24d518d
Show file tree
Hide file tree
Showing 17 changed files with 276 additions and 340 deletions.
12 changes: 6 additions & 6 deletions lib/src/draggable/draggable_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,22 +75,22 @@ class _DraggableSheetState extends State<DraggableSheet>
final gestureTamper = SheetGestureProxy.maybeOf(context);
final controller =
widget.controller ?? SheetControllerScope.maybeOf(context);
final viewport = SheetViewport.of(context);

return DraggableSheetPositionScope(
context: this,
key: viewport.positionOwnerKey,
controller: controller,
initialPosition: widget.initialPosition,
minPosition: widget.minPosition,
maxPosition: widget.maxPosition,
physics: physics,
gestureTamperer: gestureTamper,
debugLabel: kDebugMode ? 'DraggableSheet' : null,
child: SheetViewport(
child: SheetContentViewport(
child: SheetDraggable(
behavior: widget.hitTestBehavior,
child: widget.child,
),
child: SheetContentViewport(
child: SheetDraggable(
behavior: widget.hitTestBehavior,
child: widget.child,
),
),
);
Expand Down
6 changes: 4 additions & 2 deletions lib/src/draggable/draggable_sheet_position_scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import '../foundation/sheet_position_scope.dart';
import 'draggable_sheet_position.dart';

@internal
class DraggableSheetPositionScope extends SheetPositionScope {
class DraggableSheetPositionScope
extends SheetPositionScope<DraggableSheetPosition> {
const DraggableSheetPositionScope({
super.key,
super.controller,
Expand All @@ -28,7 +29,8 @@ class DraggableSheetPositionScope extends SheetPositionScope {
final String? debugLabel;

@override
SheetPositionScopeState createState() {
SheetPositionScopeState<DraggableSheetPosition,
SheetPositionScope<DraggableSheetPosition>> createState() {
return _DraggableSheetPositionScopeState();
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/src/foundation/foundation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,4 @@ export 'sheet_position.dart'
export 'sheet_position_driven_animation.dart' show SheetPositionDrivenAnimation;
export 'sheet_status.dart' show SheetStatus;
export 'sheet_theme.dart' show SheetTheme, SheetThemeData;
export 'sheet_viewport.dart' show SheetViewport;
19 changes: 10 additions & 9 deletions lib/src/foundation/sheet_activity.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,16 @@ abstract class SheetActivity<T extends SheetPosition> {

void didChangeContentSize(Size? oldSize) {}

void didChangeViewportDimensions(Size? oldSize, EdgeInsets? oldInsets) {}
void didChangeViewportDimensions(Size? oldSize) {}

void didChangeViewportInsets(EdgeInsets? oldInsets) {}

void didChangeBoundaryConstraints(
SheetAnchor? oldMinPosition,
SheetAnchor? oldMaxPosition,
) {}

/// Called when all relevant metrics of the sheet are finalized
/// for the current frame.
/// Finalizes the sheet position for the current frame.
///
/// The [oldContentSize], [oldViewportSize], and [oldViewportInsets] will be
/// `null` if the [SheetMetrics.contentSize], [SheetMetrics.viewportSize], and
Expand All @@ -88,7 +89,7 @@ abstract class SheetActivity<T extends SheetPosition> {
/// By default, this method updates [SheetMetrics.pixels] to maintain the
/// visual position of the sheet when the viewport insets change, typically
/// due to the appearance or disappearance of the on-screen keyboard.
void didFinalizeDimensions(
void finalizePosition(
Size? oldContentSize,
Size? oldViewportSize,
EdgeInsets? oldViewportInsets,
Expand Down Expand Up @@ -193,7 +194,7 @@ class AnimatedSheetActivity extends SheetActivity
}

@override
void didFinalizeDimensions(
void finalizePosition(
Size? oldContentSize,
Size? oldViewportSize,
EdgeInsets? oldViewportInsets,
Expand Down Expand Up @@ -244,7 +245,7 @@ class BallisticSheetActivity extends SheetActivity
}

@override
void didFinalizeDimensions(
void finalizePosition(
Size? oldContentSize,
Size? oldViewportSize,
EdgeInsets? oldViewportInsets,
Expand Down Expand Up @@ -379,7 +380,7 @@ class SettlingSheetActivity extends SheetActivity {
}

@override
void didFinalizeDimensions(
void finalizePosition(
Size? oldContentSize,
Size? oldViewportSize,
EdgeInsets? oldViewportInsets,
Expand Down Expand Up @@ -427,7 +428,7 @@ class IdleSheetActivity extends SheetActivity {
/// is determined by [SheetPhysics.findSettledPosition] using the metrics of
/// the previous frame.
@override
void didFinalizeDimensions(
void finalizePosition(
Size? oldContentSize,
Size? oldViewportSize,
EdgeInsets? oldViewportInsets,
Expand Down Expand Up @@ -588,7 +589,7 @@ mixin UserControlledSheetActivityMixin<T extends SheetPosition>
SheetStatus get status => SheetStatus.dragging;

@override
void didFinalizeDimensions(
void finalizePosition(
Size? oldContentSize,
Size? oldViewportSize,
EdgeInsets? oldViewportInsets,
Expand Down
1 change: 1 addition & 0 deletions lib/src/foundation/sheet_physics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ class SnappingSheetPhysics extends SheetPhysics with SheetPhysicsMixin {
SheetPhysics copyWith({
SheetPhysics? parent,
SpringDescription? spring,
// TODO: Rename to 'behavior'
SnappingSheetBehavior? snappingBehavior,
}) {
return SnappingSheetPhysics(
Expand Down
98 changes: 27 additions & 71 deletions lib/src/foundation/sheet_position.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import 'sheet_status.dart';
/// proportionally to the sheet's content height.
/// - [FixedSheetAnchor], which defines the position
/// using a fixed value in pixels.
// TODO: Rename to SheetPosition.
abstract interface class SheetAnchor {
/// {@macro FixedSheetAnchor}
const factory SheetAnchor.pixels(double pixels) = FixedSheetAnchor;
Expand Down Expand Up @@ -134,6 +135,9 @@ class FixedSheetAnchor implements SheetAnchor {
/// lifecycle and exposes it to the descendant widgets.
@internal
@optionalTypeArgs
// TODO: Rename to SheetGeometryController.
// ignore: lines_longer_than_80_chars
// TODO: Implement ValueListenable<SheetGeometry> instead of ValueListenable<double?>.
abstract class SheetPosition extends ChangeNotifier
with SheetMetrics
implements ValueListenable<double?> {
Expand Down Expand Up @@ -263,10 +267,8 @@ abstract class SheetPosition extends ChangeNotifier
correctPixels(pixels);
}
applyNewBoundaryConstraints(other.minPosition, other.maxPosition);
applyNewViewportDimensions(
other.viewportSize,
other.viewportInsets,
);
applyNewViewportSize(other.viewportSize);
applyNewViewportInsets(other.viewportInsets);
applyNewContentSize(other.contentSize);
}

Expand Down Expand Up @@ -294,21 +296,28 @@ abstract class SheetPosition extends ChangeNotifier
}

@mustCallSuper
void applyNewViewportDimensions(Size size, EdgeInsets insets) {
if (maybeViewportSize != size || maybeViewportInsets != insets) {
void applyNewViewportSize(Size size) {
if (maybeViewportSize != size) {
_oldViewportSize = maybeViewportSize;
_updateMetrics(viewportSize: size);
activity.didChangeViewportDimensions(_oldViewportSize);
}
}

@mustCallSuper
void applyNewViewportInsets(EdgeInsets insets) {
if (maybeViewportInsets != insets) {
_oldViewportInsets = maybeViewportInsets;
_updateMetrics(viewportSize: size, viewportInsets: insets);
activity.didChangeViewportDimensions(
_oldViewportSize,
_oldViewportInsets,
);
_updateMetrics(viewportInsets: insets);
activity.didChangeViewportInsets(_oldViewportInsets);
}
}

@mustCallSuper
void applyNewBoundaryConstraints(
SheetAnchor minPosition, SheetAnchor maxPosition) {
SheetAnchor minPosition,
SheetAnchor maxPosition,
) {
if (minPosition != this.minPosition || maxPosition != this.maxPosition) {
final oldMinPosition = maybeMinPosition;
final oldMaxPosition = maybeMaxPosition;
Expand All @@ -320,74 +329,18 @@ abstract class SheetPosition extends ChangeNotifier
Size? _oldContentSize;
Size? _oldViewportSize;
EdgeInsets? _oldViewportInsets;
int _markAsDimensionsWillChangeCallCount = 0;

@mustCallSuper
void markAsDimensionsWillChange() {
assert(() {
if (_markAsDimensionsWillChangeCallCount == 0) {
// Ensure that the number of calls to markAsDimensionsWillChange()
// matches the number of calls to markAsDimensionsChanged().
WidgetsBinding.instance.addPostFrameCallback((_) {
assert(
_markAsDimensionsWillChangeCallCount == 0,
_markAsDimensionsWillChangeCallCount > 0
? _debugMessage(
'markAsDimensionsWillChange() was called more times '
'than markAsDimensionsChanged() in a frame.',
)
: _debugMessage(
'markAsDimensionsChanged() was called more times '
'than markAsDimensionsWillChange() in a frame.',
),
);
});
}
return true;
}());

if (_markAsDimensionsWillChangeCallCount == 0) {
_oldContentSize = null;
_oldViewportSize = null;
_oldViewportInsets = null;
}

_markAsDimensionsWillChangeCallCount++;
}

@mustCallSuper
void markAsDimensionsChanged() {
assert(
_markAsDimensionsWillChangeCallCount > 0,
_debugMessage(
'markAsDimensionsChanged() called without '
'a matching call to markAsDimensionsWillChange().',
),
);

_markAsDimensionsWillChangeCallCount--;
if (_markAsDimensionsWillChangeCallCount == 0) {
onDimensionsFinalized();
}
}

@mustCallSuper
void onDimensionsFinalized() {
assert(
_markAsDimensionsWillChangeCallCount == 0,
_debugMessage(
'Do not call this method until all dimensions changes are finalized.',
),
);
void finalizePosition() {
assert(
hasDimensions,
_debugMessage(
'All the dimension values must be finalized '
'at the time onDimensionsFinalized() is called.',
'at the time finalizePosition() is called.',
),
);

_activity!.didFinalizeDimensions(
_activity!.finalizePosition(
_oldContentSize,
_oldViewportSize,
_oldViewportInsets,
Expand Down Expand Up @@ -596,6 +549,8 @@ abstract class SheetPosition extends ChangeNotifier
}

/// The metrics of a sheet.
// TODO: Rename to SheetGeometry.
// TODO: Add `baseline` property of type double.
mixin SheetMetrics {
/// An empty metrics object with all values set to null.
static const SheetMetrics empty = SheetMetricsSnapshot(
Expand Down Expand Up @@ -648,6 +603,7 @@ mixin SheetMetrics {
};

/// The current position of the sheet in pixels.
// TODO: Rename to `offset`.
double get pixels {
assert(_debugAssertHasProperty('pixels', maybePixels));
return maybePixels!;
Expand Down
45 changes: 24 additions & 21 deletions lib/src/foundation/sheet_position_scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,28 @@ import 'sheet_position.dart';
@optionalTypeArgs
class SheetPositionScopeKey<T extends SheetPosition>
extends LabeledGlobalKey<SheetPositionScopeState> {
SheetPositionScopeKey({String? debugLabel}) : super(debugLabel);
SheetPositionScopeKey({this.debugLabel}) : super(debugLabel);

final String? debugLabel;

final List<VoidCallback> _onCreatedListeners = [];

T? get maybeCurrentPosition => switch (currentState?._position) {
final T position => position,
_ => null
};
T? get maybeCurrentPosition {
final position = currentState?._position;
assert(
position is T?,
'SheetPositionScopeKey<$T>${debugLabel != null ? "#$debugLabel" : ""} '
'cannot be attached to a SheetPositionScope that creates a SheetPosition '
'of type ${position.runtimeType}, because it is not a type of or '
'a subtype of $T.',
);
return position as T?;
}

T get currentPosition => maybeCurrentPosition!;

// ignore: lines_longer_than_80_chars
// TODO: Change the listener's signature to `void Function(SheetPosition? prev, SheetPosition new)`.
void addOnCreatedListener(VoidCallback listener) {
_onCreatedListeners.add(listener);
// Immediately notify the listener if the position is already created.
Expand Down Expand Up @@ -51,8 +62,9 @@ class SheetPositionScopeKey<T extends SheetPosition>
/// A widget that creates a [SheetPosition], manages its lifecycle,
/// and exposes it to the descendant widgets.
@internal
@optionalTypeArgs
abstract class SheetPositionScope extends StatefulWidget {
// TODO: Rename to SheetPositionOwner
abstract class SheetPositionScope<E extends SheetPosition>
extends StatefulWidget {
/// Creates a widget that hosts a [SheetPosition].
const SheetPositionScope({
super.key,
Expand Down Expand Up @@ -91,7 +103,7 @@ abstract class SheetPositionScope extends StatefulWidget {
final Widget child;

@override
SheetPositionScopeState createState();
SheetPositionScopeState<E, SheetPositionScope<E>> createState();

/// Retrieves a [SheetPosition] from the closest [SheetPositionScope]
/// that encloses the given context, if any.
Expand Down Expand Up @@ -131,19 +143,9 @@ abstract class SheetPositionScopeState<E extends SheetPosition,
late E _position;
SheetController? _controller;

SheetPositionScopeKey<E>? get _scopeKey {
assert(() {
if (widget.key != null && widget.key is! SheetPositionScopeKey<E>) {
throw FlutterError(
'The key for a SheetPositionScope<$E> must be a '
'SheetPositionScopeKey<$E>, but got a ${widget.key.runtimeType}.',
);
}
return true;
}());

SheetPositionScopeKey? get _scopeKey {
return switch (widget.key) {
final SheetPositionScopeKey<E> key => key,
final SheetPositionScopeKey key => key,
_ => null,
};
}
Expand Down Expand Up @@ -255,7 +257,8 @@ abstract class SheetPositionScopeState<E extends SheetPosition,
}
}

@internal
// TODO: Rename to SheetPositionScope
@visibleForTesting
class InheritedSheetPositionScope extends InheritedWidget {
const InheritedSheetPositionScope({
super.key,
Expand Down
Loading

0 comments on commit 24d518d

Please sign in to comment.