Skip to content

Commit

Permalink
Manually revert #102
Browse files Browse the repository at this point in the history
  • Loading branch information
fujidaiti committed Jun 3, 2024
1 parent 0042448 commit 72d8953
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 114 deletions.
81 changes: 81 additions & 0 deletions cookbook/lib/issue/issue151.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import 'package:flutter/material.dart';
import 'package:smooth_sheets/smooth_sheets.dart';

/// Issue [#151](https://github.com/fujidaiti/smooth_sheets/issues/151):
/// Attaching SheetController to NavigationSheet causes
/// "Null check operator used on a null value"
void main() {
runApp(const Issue151());
}

class Issue151 extends StatelessWidget {
const Issue151({super.key});

@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Stack(
children: [
Scaffold(),
_Sheet(),
],
),
);
}
}

class _Sheet extends StatefulWidget {
const _Sheet();

@override
State<_Sheet> createState() => _SheetState();
}

class _SheetState extends State<_Sheet> {
NavigationSheetTransitionObserver? _transitionObserver;
late final SheetController _sheetController;

@override
void initState() {
super.initState();
_transitionObserver = NavigationSheetTransitionObserver();
_sheetController = SheetController()
..addListener(() {
debugPrint('extent: ${_sheetController.value.maybePixels}');
});
}

@override
void dispose() {
_transitionObserver = null;
_sheetController.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
final nestedNavigator = Navigator(
observers: [_transitionObserver!],
onGenerateInitialRoutes: (navigator, initialRoute) {
return [
DraggableNavigationSheetRoute(
builder: (context) => Container(
color: Colors.indigoAccent,
height: 500,
width: double.infinity,
),
),
];
},
);

return NavigationSheet(
controller: _sheetController,
transitionObserver: _transitionObserver!,
child: Material(
color: Colors.indigo,
child: nestedNavigator,
),
);
}
}
39 changes: 17 additions & 22 deletions package/lib/src/draggable/draggable_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,30 +73,25 @@ class DraggableSheet extends StatelessWidget {
this.keyboardDismissBehavior ?? theme?.keyboardDismissBehavior;
final gestureTamper = TamperSheetGesture.maybeOf(context);

Widget result = ImplicitSheetControllerScope(
Widget result = SheetExtentScope(
controller: controller,
builder: (context, controller) {
return SheetExtentScope(
controller: controller,
factory: const DraggableSheetExtentFactory(),
config: DraggableSheetExtentConfig(
initialExtent: initialExtent,
minExtent: minExtent,
maxExtent: maxExtent,
physics: physics,
gestureTamperer: gestureTamper,
debugLabel: 'DraggableSheet',
factory: const DraggableSheetExtentFactory(),
config: DraggableSheetExtentConfig(
initialExtent: initialExtent,
minExtent: minExtent,
maxExtent: maxExtent,
physics: physics,
gestureTamperer: gestureTamper,
debugLabel: 'DraggableSheet',
),
child: SheetViewport(
child: SheetContentViewport(
child: SheetDraggable(
behavior: hitTestBehavior,
child: child,
),
child: SheetViewport(
child: SheetContentViewport(
child: SheetDraggable(
behavior: hitTestBehavior,
child: child,
),
),
),
);
},
),
),
);

if (keyboardDismissBehavior != null) {
Expand Down
51 changes: 15 additions & 36 deletions package/lib/src/foundation/sheet_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,21 @@ class SheetControllerScope extends InheritedWidget {
}

static SheetController of(BuildContext context) {
return maybeOf(context)!;
final controller = maybeOf(context);

assert((() {
if (controller == null) {
throw FlutterError(
'No $SheetControllerScope ancestor could be found starting '
'from the context that was passed to $SheetControllerScope.of(). '
'The context used was:\n'
'$context',
);
}
return true;
})());

return controller!;
}

@override
Expand All @@ -114,41 +128,6 @@ class SheetControllerScope extends InheritedWidget {
}
}

/// A widget that ensures that a [SheetController] is available in the subtree.
///
/// The [builder] callback will be called with the [controller] if it is
/// explicitly provided and is not null, or a [SheetController] that is hosted
/// in the nearest ancestor [SheetControllerScope]. If neither is found, a newly
/// created [SheetController] hosted in a [DefaultSheetController] will be
/// used as a fallback.
@internal
// TODO: Remove this.
class ImplicitSheetControllerScope extends StatelessWidget {
const ImplicitSheetControllerScope({
super.key,
this.controller,
required this.builder,
});

final SheetController? controller;
final Widget Function(BuildContext, SheetController) builder;

@override
Widget build(BuildContext context) {
return switch (controller ?? DefaultSheetController.maybeOf(context)) {
final controller? => builder(context, controller),
null => DefaultSheetController(
child: Builder(
builder: (context) {
final controller = DefaultSheetController.of(context);
return builder(context, controller);
},
),
),
};
}
}

class DefaultSheetController extends StatefulWidget {
const DefaultSheetController({
super.key,
Expand Down
42 changes: 27 additions & 15 deletions package/lib/src/foundation/sheet_extent.dart
Original file line number Diff line number Diff line change
Expand Up @@ -855,15 +855,15 @@ class SheetExtentScope<C extends SheetExtentConfig, E extends SheetExtent<C>>
/// Creates a widget that hosts a [SheetExtent].
const SheetExtentScope({
super.key,
required this.controller,
this.controller,
required this.config,
required this.factory,
this.isPrimary = true,
required this.child,
});

/// The [SheetController] that will be attached to the created [SheetExtent].
final SheetController controller;
/// The [SheetController] attached to the [SheetExtent].
final SheetController? controller;

final C config;

Expand Down Expand Up @@ -921,6 +921,7 @@ class _SheetExtentScopeState<C extends SheetExtentConfig,
with TickerProviderStateMixin
implements SheetContext {
late E _extent;
SheetController? _controller;

@override
TickerProvider get vsync => this;
Expand All @@ -942,28 +943,31 @@ class _SheetExtentScopeState<C extends SheetExtentConfig,

@override
void dispose() {
_discard(_extent);
_disposeExtent(_extent);
_controller = null;
super.dispose();
}

@override
void didChangeDependencies() {
super.didChangeDependencies();
_invalidateExtentOwnership();
_rewireControllerAndScope();
_rewireControllerAndExtent();
}

@override
void didUpdateWidget(SheetExtentScope<C, E> oldWidget) {
super.didUpdateWidget(oldWidget);
_rewireControllerAndScope();
final oldExtent = _extent;
if (widget.factory != oldWidget.factory) {
_extent = _createExtent()..takeOver(oldExtent);
_scopeKey?._notifySheetExtentCreation();
_discard(oldExtent);
_disposeExtent(oldExtent);
_rewireControllerAndExtent();
} else if (widget.config != oldWidget.config) {
_extent.applyNewConfig(widget.config);
}
_invalidateExtentOwnership();
}

E _createExtent() {
Expand All @@ -973,22 +977,30 @@ class _SheetExtentScopeState<C extends SheetExtentConfig,
);
}

void _discard(E extent) {
widget.controller.detach(extent);
void _disposeExtent(E extent) {
_controller?.detach(extent);
extent.dispose();
}

void _invalidateExtentOwnership() {
assert(_debugAssertExtentOwnership());
void _rewireControllerAndScope() {
final newController =
widget.controller ?? SheetControllerScope.maybeOf(context);
if (_controller != newController) {
_controller?.detach(_extent);
_controller = newController?..attach(_extent);
}
}

void _rewireControllerAndExtent() {
assert(_debugAssertPrimaryScopeNotNested());
if (widget.isPrimary) {
widget.controller.attach(_extent);
_controller?.attach(_extent);
} else {
widget.controller.detach(_extent);
_controller?.detach(_extent);
}
}

bool _debugAssertExtentOwnership() {
bool _debugAssertPrimaryScopeNotNested() {
assert(
() {
final parentScope = context
Expand All @@ -1000,7 +1012,7 @@ class _SheetExtentScopeState<C extends SheetExtentConfig,
}

throw FlutterError(
'Nesting SheetExtentScope widgets that are marked as primary '
'Nesting $SheetExtentScope widgets that are marked as primary '
'is not allowed. Typically, this error occurs when you try to nest '
'sheet widgets such as DraggableSheet or ScrollableSheet.',
);
Expand Down
2 changes: 0 additions & 2 deletions package/lib/src/navigation/navigation_route.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';

import '../foundation/sheet_controller.dart';
import '../foundation/sheet_extent.dart';
import '../foundation/sheet_viewport.dart';
import 'navigation_sheet.dart';
Expand Down Expand Up @@ -136,7 +135,6 @@ class NavigationSheetRouteContent extends StatelessWidget {
isPrimary: false,
factory: factory,
config: config,
controller: SheetControllerScope.of(context),
child: NavigationSheetRouteViewport(
child: SheetContentViewport(child: child),
),
Expand Down
35 changes: 15 additions & 20 deletions package/lib/src/navigation/navigation_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,27 +65,22 @@ class _NavigationSheetState extends State<NavigationSheet>
widget.keyboardDismissBehavior ?? theme?.keyboardDismissBehavior;
final gestureTamper = TamperSheetGesture.maybeOf(context);

Widget result = ImplicitSheetControllerScope(
Widget result = SheetExtentScope(
key: _scopeKey,
controller: widget.controller,
builder: (context, controller) {
return SheetExtentScope(
key: _scopeKey,
controller: controller,
factory: this,
isPrimary: true,
config: SheetExtentConfig(
minExtent: const Extent.pixels(0),
maxExtent: const Extent.proportional(1),
// TODO: Use more appropriate physics.
physics: const ClampingSheetPhysics(),
gestureTamperer: gestureTamper,
debugLabel: kDebugMode ? 'NavigationSheet' : null,
),
child: NavigationSheetViewport(
child: SheetContentViewport(child: widget.child),
),
);
},
factory: this,
isPrimary: true,
config: SheetExtentConfig(
minExtent: const Extent.pixels(0),
maxExtent: const Extent.proportional(1),
// TODO: Use more appropriate physics.
physics: const ClampingSheetPhysics(),
gestureTamperer: gestureTamper,
debugLabel: kDebugMode ? 'NavigationSheet' : null,
),
child: NavigationSheetViewport(
child: SheetContentViewport(child: widget.child),
),
);

if (keyboardDismissBehavior != null) {
Expand Down
Loading

0 comments on commit 72d8953

Please sign in to comment.