Skip to content

Commit

Permalink
updated bs open method
Browse files Browse the repository at this point in the history
  • Loading branch information
ottodaempfle committed Nov 15, 2024
1 parent 35b198b commit 9c4ee4c
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 68 deletions.
80 changes: 80 additions & 0 deletions example/lib/home_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:tapped_bottom_sheet/scrollable_bottom_sheet.dart';

class HomePage extends StatefulWidget {
const HomePage({super.key});

@override
State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
ScrollController? _scrollController;
final _scrollableBSKey = GlobalKey<ScrollableBottomSheetState>();

@override
Widget build(BuildContext context) {
final maxHeight = MediaQuery.of(context).size.height;

return Scaffold(
appBar: AppBar(
title: const Text('Bottom Sheet Example'),
),
body: Stack(
children: [
Positioned.fill(
child: Container(
color: Theme.of(context).colorScheme.primary,
),
),
Align(
alignment: Alignment.bottomCenter,
child: ScrollableBottomSheet(
key: _scrollableBSKey,
snapPositions: [maxHeight / 2],
initialPosition: maxHeight / 2,
maxHeight: maxHeight,
minHeight: 100,
builder: (context, scrollController) {
_scrollController = scrollController;

return ListView.builder(
controller: scrollController,
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(
title: ElevatedButton(
child: Text('Element $index'),
onPressed: index == 0 ? _open : () {},
),
);
},
);
},
borderRadiusTop: 15,
borderColor: Colors.black,
backgroundColor: Colors.white,
),
),
],
),
floatingActionButton:
FloatingActionButton(onPressed: _scrollToLastElementOfList, child: const Icon(Icons.arrow_downward_rounded)),
);
}

Future<void> _open() async {
await _scrollableBSKey.currentState!.open();
}

Future<void> _scrollToLastElementOfList() async {
if (_scrollController == null || _scrollableBSKey.currentState == null) return;

unawaited(_scrollableBSKey.currentState!.open());

final maxScrollExtent = _scrollController!.position.maxScrollExtent;
await _scrollController?.animateTo(maxScrollExtent, duration: const Duration(seconds: 1), curve: Curves.bounceIn);
}
}
59 changes: 20 additions & 39 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:example/home_page.dart';
import 'package:flutter/material.dart';
import 'package:tapped_bottom_sheet/scrollable_bottom_sheet.dart';

void main() {
runApp(const MainApp());
Expand All @@ -10,44 +10,25 @@ class MainApp extends StatelessWidget {

@override
Widget build(BuildContext context) {
final maxHeight = MediaQuery.of(context).size.height - kToolbarHeight - 100;
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Bottom Sheet Example'),
),
body: Stack(
children: [
Positioned.fill(
child: Container(
color: Colors.green,
),
),
Align(
alignment: Alignment.bottomCenter,
child: ScrollableBottomSheet(
snapPositions: [maxHeight / 2],
initialPosition: maxHeight / 2,
maxHeight: maxHeight,
minHeight: 100,
builder: (context, scrollController) {
return ListView.builder(
controller: scrollController,
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(
title: Text('Element $index'),
);
},
);
},
borderRadiusTop: 15,
borderColor: Colors.black,
backgroundColor: Colors.white,
),
),
],
),
return const MaterialApp(
home: HomePage(),
);
}
}

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

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Test"),
),
body: ListView.builder(
itemBuilder: (context, index) {
return ElevatedButton(onPressed: () {}, child: Text(index.toString()));
},
),
);
}
Expand Down
48 changes: 19 additions & 29 deletions lib/scrollable_bottom_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ class ScrollableBottomSheet extends StatefulWidget {
}
}

class ScrollableBottomSheetState extends State<ScrollableBottomSheet>
with SingleTickerProviderStateMixin {
class ScrollableBottomSheetState extends State<ScrollableBottomSheet> with SingleTickerProviderStateMixin {
final _scrollController = ScrollController();
late final AnimationController _animationController;
var _isScrollingEnabled = false;
Expand All @@ -80,8 +79,7 @@ class ScrollableBottomSheetState extends State<ScrollableBottomSheet>

ScrollHoldController? _hold;

Tween<double> get _sizeTween =>
Tween(begin: widget.minHeight, end: widget.maxHeight);
Tween<double> get _sizeTween => Tween(begin: widget.minHeight, end: widget.maxHeight);

bool get _isPanelOpen => _animationController.value == 1.0;

Expand All @@ -96,9 +94,7 @@ class ScrollableBottomSheetState extends State<ScrollableBottomSheet>
_animationController = AnimationController(
vsync: this,
duration: widget.animationDuration,
value: widget.initialPosition == null
? 0.0
: _pixelToValue(widget.initialPosition!),
value: widget.initialPosition == null ? 0.0 : _pixelToValue(widget.initialPosition!),
)..addListener(_notifyScrollListeners);
}

Expand All @@ -114,8 +110,7 @@ class ScrollableBottomSheetState extends State<ScrollableBottomSheet>
super.didUpdateWidget(oldWidget);

if (!widget.maintainPositionOnConstraintChange) return;
if (oldWidget.maxHeight == widget.maxHeight &&
oldWidget.minHeight == widget.minHeight) return;
if (oldWidget.maxHeight == widget.maxHeight && oldWidget.minHeight == widget.minHeight) return;

final previousPositionPixels = Tween(
begin: oldWidget.minHeight,
Expand All @@ -127,8 +122,7 @@ class ScrollableBottomSheetState extends State<ScrollableBottomSheet>

@override
Widget build(BuildContext context) {
final borderRadius =
BorderRadius.vertical(top: Radius.circular(widget.borderRadiusTop));
final borderRadius = BorderRadius.vertical(top: Radius.circular(widget.borderRadiusTop));

return GestureListener(
canDrag: widget.canDrag,
Expand Down Expand Up @@ -163,8 +157,7 @@ class ScrollableBottomSheetState extends State<ScrollableBottomSheet>
);
},
child: Builder(
builder: (context) =>
widget.builder(context, _scrollController),
builder: (context) => widget.builder(context, _scrollController),
),
),
),
Expand All @@ -184,9 +177,7 @@ class ScrollableBottomSheetState extends State<ScrollableBottomSheet>
// _drag might be null if the drag activity ended and called _disposeDrag.
assert(_hold == null || _drag == null);
_drag?.update(details);
if (_scrollController.hasClients &&
_scrollController.position.pixels <= 0 &&
details.primaryDelta! > 0) {
if (_scrollController.hasClients && _scrollController.position.pixels <= 0 && details.primaryDelta! > 0) {
setState(() => _isScrollingEnabled = false);
_handleDragCancel();
if (_scrollController.position.pixels != 0.0) {
Expand All @@ -198,16 +189,13 @@ class ScrollableBottomSheetState extends State<ScrollableBottomSheet>

// only slide the panel if scrolling is not enabled
if (!_isScrollingEnabled && !_isScrollingBlocked) {
_animationController.value -=
primaryDelta / (widget.maxHeight - widget.minHeight);
_animationController.value -= primaryDelta / (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 &&
_scrollController.hasClients &&
_scrollController.offset <= 0) {
if (_isPanelOpen && _scrollController.hasClients && _scrollController.offset <= 0) {
final scrollingEnabled = primaryDelta < 0;

setState(() => _isScrollingEnabled = scrollingEnabled);
Expand All @@ -232,7 +220,9 @@ class ScrollableBottomSheetState extends State<ScrollableBottomSheet>
// by default without any calls from our implementation.
if (!widget.canDrag) return;

if (!_isScrollingEnabled || _isScrollingBlocked) {
final isScrollingAllowed = _isPanelOpen ? true : _isScrollingEnabled;

if (!isScrollingAllowed || _isScrollingBlocked) {
_scrollController.jumpTo(0);
}
}
Expand All @@ -257,11 +247,9 @@ class ScrollableBottomSheetState extends State<ScrollableBottomSheet>
}

final scrollPixelPerSeconds = details.velocity.pixelsPerSecond.dy;
final flingVelocity =
-scrollPixelPerSeconds / (widget.maxHeight - widget.minHeight);
final flingVelocity = -scrollPixelPerSeconds / (widget.maxHeight - widget.minHeight);

final nearestSnapPoint =
_findNearestRelativeSnapPoint(target: _animationController.value);
final nearestSnapPoint = _findNearestRelativeSnapPoint(target: _animationController.value);

if (scrollPixelPerSeconds > widget.completeFlingVelocity) {
if (flingVelocity.isNegative) {
Expand Down Expand Up @@ -317,13 +305,15 @@ class ScrollableBottomSheetState extends State<ScrollableBottomSheet>
return _animationController.fling(velocity: -1.0);
}

Future<void> open() {
Future<void> open() async {
if (_isPanelOpen) return;

setState(() => _isScrollingEnabled = true);
return _animationController.fling(velocity: 1.0);
}

Future<void> animateToNearestSnapPoint() {
final newPosition =
_findNearestRelativeSnapPoint(target: _animationController.value);
final newPosition = _findNearestRelativeSnapPoint(target: _animationController.value);
return animateTo(
pixels: _sizeTween.transform(newPosition),
duration: widget.animationDuration,
Expand Down

0 comments on commit 9c4ee4c

Please sign in to comment.