Skip to content

Commit

Permalink
Move sheet context implementation to sheet states
Browse files Browse the repository at this point in the history
  • Loading branch information
fujidaiti committed Jul 19, 2024
1 parent f920746 commit f9bf42d
Show file tree
Hide file tree
Showing 12 changed files with 103 additions and 40 deletions.
25 changes: 17 additions & 8 deletions package/lib/src/draggable/draggable_sheet.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

import '../foundation/sheet_context.dart';
import '../foundation/sheet_controller.dart';
import '../foundation/sheet_extent.dart';
import '../foundation/sheet_gesture_tamperer.dart';
Expand All @@ -15,7 +16,7 @@ import 'sheet_draggable.dart';
///
/// Note that this widget does not work with scrollable widgets.
/// Instead, use [ScrollableSheet] for this usecase.
class DraggableSheet extends StatelessWidget {
class DraggableSheet extends StatefulWidget {
/// Creates a sheet that can be dragged.
///
/// The maximum height will be equal to the [child]'s height.
Expand Down Expand Up @@ -61,26 +62,34 @@ class DraggableSheet extends StatelessWidget {
/// This value will be passed to the constructor of internal [SheetDraggable].
final HitTestBehavior hitTestBehavior;

@override
State<DraggableSheet> createState() => _DraggableSheetState();
}

class _DraggableSheetState extends State<DraggableSheet>
with TickerProviderStateMixin, SheetContextStateMixin<DraggableSheet> {
@override
Widget build(BuildContext context) {
final theme = SheetTheme.maybeOf(context);
final physics = this.physics ?? theme?.physics ?? kDefaultSheetPhysics;
final physics = widget.physics ?? theme?.physics ?? kDefaultSheetPhysics;
final gestureTamper = TamperSheetGesture.maybeOf(context);
final controller = this.controller ?? SheetControllerScope.maybeOf(context);
final controller =
widget.controller ?? SheetControllerScope.maybeOf(context);

return DraggableSheetExtentScope(
context: this,
controller: controller,
initialExtent: initialExtent,
minExtent: minExtent,
maxExtent: maxExtent,
initialExtent: widget.initialExtent,
minExtent: widget.minExtent,
maxExtent: widget.maxExtent,
physics: physics,
gestureTamperer: gestureTamper,
debugLabel: kDebugMode ? 'DraggableSheet' : null,
child: SheetViewport(
child: SheetContentViewport(
child: SheetDraggable(
behavior: hitTestBehavior,
child: child,
behavior: widget.hitTestBehavior,
child: widget.child,
),
),
),
Expand Down
2 changes: 2 additions & 0 deletions package/lib/src/draggable/draggable_sheet_extent_scope.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:meta/meta.dart';

import '../foundation/sheet_context.dart';
import '../foundation/sheet_extent.dart';
import '../foundation/sheet_extent_scope.dart';
import 'draggable_sheet_extent.dart';
Expand All @@ -10,6 +11,7 @@ class DraggableSheetExtentScope extends SheetExtentScope {
super.key,
super.controller,
super.isPrimary,
required super.context,
required this.initialExtent,
required super.minExtent,
required super.maxExtent,
Expand Down
23 changes: 23 additions & 0 deletions package/lib/src/foundation/sheet_context.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';

import 'sheet_extent.dart';

/// An interface that provides a set of dependencies required by [SheetExtent].
@internal
abstract class SheetContext {
TickerProvider get vsync;
BuildContext? get notificationContext;
}

@internal
@optionalTypeArgs
mixin SheetContextStateMixin<T extends StatefulWidget>
on State<T>, TickerProviderStateMixin<T>
implements SheetContext {
@override
TickerProvider get vsync => this;

@override
BuildContext? get notificationContext => mounted ? context : null;
}
10 changes: 1 addition & 9 deletions package/lib/src/foundation/sheet_extent.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:meta/meta.dart';

import '../internal/double_utils.dart';
import 'sheet_activity.dart';
import 'sheet_context.dart';
import 'sheet_controller.dart';
import 'sheet_drag.dart';
import 'sheet_extent_scope.dart';
Expand Down Expand Up @@ -783,12 +784,3 @@ class SheetMetrics {
viewportInsets: maybeViewportInsets,
).toString();
}

/// An interface that provides the necessary context to a [SheetExtent].
///
/// Typically, [State]s that host a [SheetExtent] will implement this interface.
@internal
abstract class SheetContext {
TickerProvider get vsync;
BuildContext? get notificationContext;
}
22 changes: 10 additions & 12 deletions package/lib/src/foundation/sheet_extent_scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';

import 'sheet_context.dart';
import 'sheet_controller.dart';
import 'sheet_extent.dart';
import 'sheet_gesture_tamperer.dart';
Expand Down Expand Up @@ -53,6 +54,7 @@ abstract class SheetExtentScope extends StatefulWidget {
/// Creates a widget that hosts a [SheetExtent].
const SheetExtentScope({
super.key,
required this.context,
this.controller,
this.isPrimary = true,
required this.minExtent,
Expand All @@ -62,6 +64,9 @@ abstract class SheetExtentScope extends StatefulWidget {
required this.child,
});

/// The context the extent object belongs to.
final SheetContext context;

/// The [SheetController] attached to the [SheetExtent].
final SheetController? controller;

Expand Down Expand Up @@ -120,18 +125,10 @@ abstract class SheetExtentScope extends StatefulWidget {

@internal
abstract class SheetExtentScopeState<E extends SheetExtent,
W extends SheetExtentScope> extends State<W>
with TickerProviderStateMixin
implements SheetContext {
W extends SheetExtentScope> extends State<W> {
late E _extent;
SheetController? _controller;

@override
TickerProvider get vsync => this;

@override
BuildContext? get notificationContext => mounted ? context : null;

SheetExtentScopeKey<E>? get _scopeKey {
assert(() {
if (widget.key != null && widget.key is! SheetExtentScopeKey<E>) {
Expand All @@ -152,7 +149,7 @@ abstract class SheetExtentScopeState<E extends SheetExtent,
@override
void initState() {
super.initState();
_extent = buildExtent(this);
_extent = buildExtent(widget.context);
_scopeKey?._notifySheetExtentCreation();
}

Expand All @@ -176,7 +173,7 @@ abstract class SheetExtentScopeState<E extends SheetExtent,
_rewireControllerAndScope();
if (shouldRebuildExtent(_extent)) {
final oldExtent = _extent;
_extent = buildExtent(this)..takeOver(oldExtent);
_extent = buildExtent(widget.context)..takeOver(oldExtent);
_scopeKey?._notifySheetExtentCreation();
_disposeExtent(oldExtent);
_rewireControllerAndExtent();
Expand All @@ -198,7 +195,8 @@ abstract class SheetExtentScopeState<E extends SheetExtent,
E buildExtent(SheetContext context);

@protected
bool shouldRebuildExtent(E oldExtent) => false;
@mustCallSuper
bool shouldRebuildExtent(E oldExtent) => widget.context != oldExtent.context;

void _disposeExtent(E extent) {
_controller?.detach(extent);
Expand Down
20 changes: 19 additions & 1 deletion package/lib/src/navigation/navigation_route.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';

import '../foundation/sheet_context.dart';
import '../foundation/sheet_extent.dart';
import '../foundation/sheet_extent_scope.dart';
import '../foundation/sheet_viewport.dart';
Expand Down Expand Up @@ -125,6 +126,7 @@ abstract class NavigationSheetRoute<T, E extends SheetExtent>
}

typedef ExtentScopeBuilder = SheetExtentScope Function(
SheetContext context,
SheetExtentScopeKey key,
Widget child,
);
Expand All @@ -143,10 +145,15 @@ class NavigationSheetRouteContent extends StatelessWidget {
Widget build(BuildContext context) {
assert(_debugAssertDependencies(context));
final parentRoute = ModalRoute.of(context)! as NavigationSheetRoute;
final globalExtent = SheetExtentScope.of<NavigationSheetExtent>(context);
final routeViewport = NavigationSheetRouteViewport(
child: SheetContentViewport(child: child),
);
final localScope = scopeBuilder(parentRoute.scopeKey, routeViewport);
final localScope = scopeBuilder(
globalExtent.context,
parentRoute.scopeKey,
routeViewport,
);
assert(_debugAssertScope(localScope, parentRoute.scopeKey, routeViewport));
return localScope;
}
Expand Down Expand Up @@ -185,6 +192,17 @@ class NavigationSheetRouteContent extends StatelessWidget {
bool _debugAssertDependencies(BuildContext context) {
assert(
() {
final globalExtent =
SheetExtentScope.maybeOf<NavigationSheetExtent>(context);
if (globalExtent == null) {
throw FlutterError(
'A $SheetExtentScope that hosts a $NavigationSheetExtent '
'is not found in the given context. This is likely because '
'this $NavigationSheetRouteContent is not in the subtree of '
'the navigator enclosed by a $NavigationSheet.',
);
}

final parentRoute = ModalRoute.of(context);
if (parentRoute is NavigationSheetRoute) {
return true;
Expand Down
6 changes: 4 additions & 2 deletions package/lib/src/navigation/navigation_routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ class _ScrollableNavigationSheetRouteContent extends StatelessWidget {
final gestureTamper = TamperSheetGesture.maybeOf(context);

return NavigationSheetRouteContent(
scopeBuilder: (key, child) {
scopeBuilder: (context, key, child) {
return ScrollableSheetExtentScope(
key: key,
context: context,
isPrimary: false,
initialExtent: initialExtent,
minExtent: minExtent,
Expand Down Expand Up @@ -78,9 +79,10 @@ class _DraggableNavigationSheetRouteContent extends StatelessWidget {
final gestureTamper = TamperSheetGesture.maybeOf(context);

return NavigationSheetRouteContent(
scopeBuilder: (key, child) {
scopeBuilder: (context, key, child) {
return DraggableSheetExtentScope(
key: key,
context: context,
isPrimary: false,
initialExtent: initialExtent,
minExtent: minExtent,
Expand Down
7 changes: 6 additions & 1 deletion package/lib/src/navigation/navigation_sheet.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

import '../foundation/sheet_context.dart';
import '../foundation/sheet_controller.dart';
import '../foundation/sheet_extent_scope.dart';
import '../foundation/sheet_gesture_tamperer.dart';
Expand Down Expand Up @@ -31,7 +32,10 @@ class NavigationSheet extends StatefulWidget with TransitionAwareWidgetMixin {
}

class _NavigationSheetState extends State<NavigationSheet>
with TransitionAwareStateMixin {
with
TransitionAwareStateMixin,
TickerProviderStateMixin,
SheetContextStateMixin {
final _scopeKey = SheetExtentScopeKey<NavigationSheetExtent>(
debugLabel: kDebugMode ? 'NavigationSheet' : null,
);
Expand All @@ -49,6 +53,7 @@ class _NavigationSheetState extends State<NavigationSheet>

return NavigationSheetExtentScope(
key: _scopeKey,
context: this,
controller: controller,
gestureTamperer: gestureTamper,
debugLabel: kDebugMode ? 'NavigationSheet' : null,
Expand Down
2 changes: 2 additions & 0 deletions package/lib/src/navigation/navigation_sheet_extent_scope.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:meta/meta.dart';

import '../foundation/sheet_context.dart';
import '../foundation/sheet_extent.dart';
import '../foundation/sheet_extent_scope.dart';
import '../foundation/sheet_physics.dart';
Expand All @@ -11,6 +12,7 @@ class NavigationSheetExtentScope extends SheetExtentScope {
super.key,
super.controller,
super.gestureTamperer,
required super.context,
this.debugLabel,
required super.child,
}) : super(
Expand Down
23 changes: 16 additions & 7 deletions package/lib/src/scrollable/scrollable_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';

import '../foundation/sheet_context.dart';
import '../foundation/sheet_controller.dart';
import '../foundation/sheet_extent.dart';
import '../foundation/sheet_gesture_tamperer.dart';
Expand All @@ -12,7 +13,7 @@ import '../foundation/sheet_viewport.dart';
import 'scrollable_sheet_extent_scope.dart';
import 'sheet_scrollable.dart';

class ScrollableSheet extends StatelessWidget {
class ScrollableSheet extends StatefulWidget {
const ScrollableSheet({
super.key,
this.initialExtent = const Extent.proportional(1),
Expand Down Expand Up @@ -41,24 +42,32 @@ class ScrollableSheet extends StatelessWidget {
/// The content of the sheet.
final Widget child;

@override
State<ScrollableSheet> createState() => _ScrollableSheetState();
}

class _ScrollableSheetState extends State<ScrollableSheet>
with TickerProviderStateMixin, SheetContextStateMixin {
@override
Widget build(BuildContext context) {
final theme = SheetTheme.maybeOf(context);
final physics = this.physics ?? theme?.physics ?? kDefaultSheetPhysics;
final physics = widget.physics ?? theme?.physics ?? kDefaultSheetPhysics;
final gestureTamper = TamperSheetGesture.maybeOf(context);
final controller = this.controller ?? SheetControllerScope.maybeOf(context);
final controller =
widget.controller ?? SheetControllerScope.maybeOf(context);

return ScrollableSheetExtentScope(
context: this,
controller: controller,
initialExtent: initialExtent,
minExtent: minExtent,
maxExtent: maxExtent,
initialExtent: widget.initialExtent,
minExtent: widget.minExtent,
maxExtent: widget.maxExtent,
physics: physics,
gestureTamperer: gestureTamper,
debugLabel: kDebugMode ? 'ScrollableSheet' : null,
child: SheetViewport(
child: SheetContentViewport(
child: ScrollableSheetContent(child: child),
child: ScrollableSheetContent(child: widget.child),
),
),
);
Expand Down
2 changes: 2 additions & 0 deletions package/lib/src/scrollable/scrollable_sheet_extent_scope.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:meta/meta.dart';

import '../foundation/sheet_context.dart';
import '../foundation/sheet_extent.dart';
import '../foundation/sheet_extent_scope.dart';
import 'scrollable_sheet_extent.dart';
Expand All @@ -10,6 +11,7 @@ class ScrollableSheetExtentScope extends SheetExtentScope {
super.key,
super.controller,
super.isPrimary,
required super.context,
required this.initialExtent,
required super.minExtent,
required super.maxExtent,
Expand Down
1 change: 1 addition & 0 deletions package/test/foundation/sheet_viewport_test.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:smooth_sheets/src/foundation/sheet_activity.dart';
import 'package:smooth_sheets/src/foundation/sheet_context.dart';
import 'package:smooth_sheets/src/foundation/sheet_extent.dart';
import 'package:smooth_sheets/src/foundation/sheet_extent_scope.dart';
import 'package:smooth_sheets/src/foundation/sheet_physics.dart';
Expand Down

0 comments on commit f9bf42d

Please sign in to comment.