1+ import 'dart:async' ;
12import 'package:flutter/material.dart' ;
23
34class GFBottomSheet extends StatefulWidget {
45 GFBottomSheet ({
56 Key key,
6- @required this .stickyHeader,
77 @required this .contentBody,
8+ this .stickyHeader,
89 this .stickyFooter,
910 this .controller,
1011 this .minContentHeight = 0 ,
1112 this .maxContentHeight = 300 ,
1213 this .elevation = 0.0 ,
13- this .stickyFooterHeight,
14+ this .stickyFooterHeight = 0.0 ,
15+ this .stickyHeaderHeight = 0.0 ,
16+ this .animationDuration = 300 ,
17+ this .enableExpandableContent = false ,
1418 }) : assert (elevation >= 0.0 ),
1519 assert (minContentHeight >= 0.0 ),
1620 super (key: key) {
1721 controller.height = minContentHeight;
18- controller.smoothness = 500 ;
22+ controller.animationDuration = animationDuration ;
1923 }
2024
2125 /// [minContentHeight] controls the minimum height of the content body.
@@ -31,35 +35,49 @@ class GFBottomSheet extends StatefulWidget {
3135 final Widget stickyHeader;
3236
3337 /// [contentBody] is the body of GFBottomSheet.
34- /// User can interact by tapping the [contentBody]
38+ /// User can interact by swiping or tapping the [contentBody]
3539 final Widget contentBody;
3640
3741 /// [stickyFooter] is the footer of GFBottomSheet.
3842 /// User can interact by swiping or tapping the [stickyFooter]
3943 final Widget stickyFooter;
4044
41- /// [stickyFooterHeight] defines the height of GFBottokSheet footer .
45+ /// [stickyFooterHeight] defines the height of GFBottomSheet's [stickyFooter] .
4246 final double stickyFooterHeight;
4347
48+ /// [stickyHeaderHeight] defines the height of GFBottomSheet's [stickyHeader] .
49+ final double stickyHeaderHeight;
50+
4451 /// [elevation] controls shadow below the GFBottomSheet material.
45- /// Must be greater or equalto 0. Default value is 0.
52+ /// Must be greater or equal to 0. Default value is 0.
4653 final double elevation;
4754
55+ ///[enableExpandableContent] allows [contentBody] to expand.
56+ /// Default value is false.
57+ final bool enableExpandableContent;
58+
4859 /// [controller] used to control GFBottomSheet behavior like hide/show
4960 final GFBottomSheetController controller;
5061
62+ /// Defines the drag animation duration of the GFBottomSheet
63+ /// Default value is 300.
64+ final int animationDuration;
65+
5166 @override
5267 _GFBottomSheetState createState () => _GFBottomSheetState ();
5368}
5469
5570class _GFBottomSheetState extends State <GFBottomSheet >
5671 with TickerProviderStateMixin {
72+ final StreamController <double > controller = StreamController .broadcast ();
5773 bool isDragDirectionUp;
5874 bool showBottomSheet = false ;
5975 Function _controllerListener;
76+ double position;
77+ bool showContent = false ;
6078
6179 void _onVerticalDragUpdate (data) {
62- _setSmoothness ();
80+ _setAnimationDuration ();
6381 if (((widget.controller.height - data.delta.dy) >
6482 widget.minContentHeight) &&
6583 ((widget.controller.height - data.delta.dy) <
@@ -70,7 +88,7 @@ class _GFBottomSheetState extends State<GFBottomSheet>
7088 }
7189
7290 void _onVerticalDragEnd (data) {
73- _setSmoothness ();
91+ _setAnimationDuration ();
7492 if (isDragDirectionUp && widget.controller.value) {
7593 _showBottomSheet ();
7694 } else if (! isDragDirectionUp && ! widget.controller.value) {
@@ -89,7 +107,7 @@ class _GFBottomSheetState extends State<GFBottomSheet>
89107 @override
90108 void initState () {
91109 super .initState ();
92-
110+ position = widget.minContentHeight;
93111 widget.controller.value = showBottomSheet;
94112 _controllerListener = () {
95113 widget.controller.value ? _showBottomSheet () : _hideBottomSheet ();
@@ -113,28 +131,62 @@ class _GFBottomSheetState extends State<GFBottomSheet>
113131 onVerticalDragUpdate: _onVerticalDragUpdate,
114132 onVerticalDragEnd: _onVerticalDragEnd,
115133 onTap: _onTap,
116- child: widget.stickyHeader,
134+ child: Container (
135+ height: widget.stickyHeaderHeight,
136+ child: widget.stickyHeader,
137+ ),
117138 ),
118- AnimatedBuilder (
119- animation: widget.controller,
120- builder: (_, Widget child) => AnimatedContainer (
121- curve: Curves .easeOut,
122- duration: Duration (milliseconds: widget.controller.smoothness),
123- height: widget.controller.height,
124- child: GestureDetector (
125- onVerticalDragUpdate: _onVerticalDragUpdate,
126- onVerticalDragEnd: _onVerticalDragEnd,
127- onTap: _onTap,
128- child: widget.contentBody),
129- ),
130- ),
131- widget.stickyFooter != null
139+ ! widget.enableExpandableContent
132140 ? AnimatedBuilder (
133141 animation: widget.controller,
134142 builder: (_, Widget child) => AnimatedContainer (
135143 curve: Curves .easeOut,
136- duration:
137- Duration (milliseconds: widget.controller.smoothness),
144+ duration: Duration (
145+ milliseconds: widget.controller.animationDuration),
146+ height: widget.controller.height,
147+ child: GestureDetector (
148+ onVerticalDragUpdate: _onVerticalDragUpdate,
149+ onVerticalDragEnd: _onVerticalDragEnd,
150+ onTap: _onTap,
151+ child: widget.contentBody),
152+ ),
153+ )
154+ : showContent
155+ ? StreamBuilder (
156+ stream: controller.stream,
157+ initialData: widget.controller.height,
158+ builder: (context, snapshot) => GestureDetector (
159+ onVerticalDragUpdate: (DragUpdateDetails details) {
160+ if (((widget.controller.height - details.delta.dy) >
161+ widget.minContentHeight) &&
162+ ((widget.controller.height - details.delta.dy) <
163+ (MediaQuery .of (context).size.height * 0.8 -
164+ widget.stickyFooterHeight -
165+ widget.stickyHeaderHeight))) {
166+ isDragDirectionUp = details.delta.dy <= 0 ;
167+ widget.controller.height -= details.delta.dy;
168+ }
169+ controller.add (widget.controller.height);
170+ },
171+ onVerticalDragEnd: _onVerticalDragEnd,
172+ onTap: _onTap,
173+ behavior: HitTestBehavior .translucent,
174+ child: Container (
175+ height: snapshot.hasData == null
176+ ? widget.minContentHeight
177+ : snapshot.data,
178+ child: widget.contentBody,
179+ )),
180+ )
181+ : Container (),
182+ widget.stickyFooter == null
183+ ? Container ()
184+ : AnimatedBuilder (
185+ animation: widget.controller,
186+ builder: (_, Widget child) => AnimatedContainer (
187+ curve: Curves .easeOut,
188+ duration: Duration (
189+ milliseconds: widget.controller.animationDuration),
138190 height: widget.controller.height != widget.minContentHeight
139191 ? widget.stickyFooterHeight
140192 : 0.0 ,
@@ -145,8 +197,7 @@ class _GFBottomSheetState extends State<GFBottomSheet>
145197 child: widget.stickyFooter,
146198 ),
147199 ),
148- )
149- : Container (),
200+ ),
150201 ],
151202 );
152203 return Material (
@@ -156,10 +207,16 @@ class _GFBottomSheetState extends State<GFBottomSheet>
156207 }
157208
158209 void _hideBottomSheet () {
210+ setState (() {
211+ showContent = false ;
212+ });
159213 widget.controller.height = widget.minContentHeight;
160214 }
161215
162216 void _showBottomSheet () {
217+ setState (() {
218+ showContent = true ;
219+ });
163220 widget.controller.height = widget.maxContentHeight;
164221 }
165222
@@ -169,8 +226,8 @@ class _GFBottomSheetState extends State<GFBottomSheet>
169226 super .dispose ();
170227 }
171228
172- void _setSmoothness () {
173- widget.controller.smoothness = 500 ;
229+ void _setAnimationDuration () {
230+ widget.controller.animationDuration = widget.animationDuration ;
174231 }
175232}
176233
@@ -180,8 +237,8 @@ class GFBottomSheetController extends ValueNotifier<bool> {
180237 /// Defines the height of the GFBottomSheet's contentBody
181238 double _height;
182239
183- /// Defines the drag animation smoothness of the GFBottomSheet
184- int smoothness ;
240+ /// Defines the drag animation duration of the GFBottomSheet
241+ int animationDuration ;
185242
186243 // ignore: unnecessary_getters_setters
187244 set height (double value) => _height = value;
0 commit comments