Toggle navigation
Toggle navigation
This project
Loading...
Sign in
flutter_package
/
modal_bottom_sheet
Go to a project
Toggle navigation
Projects
Groups
Snippets
Help
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
Jaime Blasco
2020-07-05 17:03:09 +0200
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
5e46ad244888791cf7efd131d98c064d4a3d462d
5e46ad24
1 parent
200f2082
Support scroll to top when tapped on the status bar
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
313 additions
and
232 deletions
example/lib/main.dart
lib/src/bottom_sheet.dart
lib/src/bottom_sheet_route.dart
lib/src/bottom_sheets/cupertino_bottom_sheet.dart
lib/src/utils/primary_scroll_status_bar.dart
example/lib/main.dart
View file @
5e46ad2
...
...
@@ -98,147 +98,153 @@ class _MyHomePageState extends State<MyHomePage> {
Widget
build
(
BuildContext
context
)
{
print
(
MediaQuery
.
of
(
context
).
size
.
height
);
return
Material
(
child:
CupertinoPageScaffold
(
backgroundColor:
Colors
.
white
,
navigationBar:
CupertinoNavigationBar
(
transitionBetweenRoutes:
false
,
middle:
Text
(
'iOS13 Modal Presentation'
),
trailing:
GestureDetector
(
child:
Icon
(
Icons
.
arrow_forward
),
onTap:
()
=>
Navigator
.
of
(
context
).
pushNamed
(
'ss'
),
child:
Scaffold
(
body:
CupertinoPageScaffold
(
backgroundColor:
Colors
.
white
,
navigationBar:
CupertinoNavigationBar
(
transitionBetweenRoutes:
false
,
middle:
Text
(
'iOS13 Modal Presentation'
),
trailing:
GestureDetector
(
child:
Icon
(
Icons
.
arrow_forward
),
onTap:
()
=>
Navigator
.
of
(
context
).
pushNamed
(
'ss'
),
),
),
),
child:
SizedBox
.
expand
(
child:
SingleChildScrollView
(
child:
SafeArea
(
bottom:
false
,
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
stretch
,
mainAxisSize:
MainAxisSize
.
min
,
children:
<
Widget
>[
ListTile
(
title:
Text
(
'Cupertino Photo Share Example'
),
onTap:
()
=>
Navigator
.
of
(
context
).
push
(
MaterialWithModalsPageRoute
(
builder:
(
context
)
=>
CupertinoSharePage
()))),
section
(
'STYLES'
),
ListTile
(
title:
Text
(
'Material fit'
),
onTap:
()
=>
showMaterialModalBottomSheet
(
expand:
false
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalFit
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Bar Modal'
),
onTap:
()
=>
showBarModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalInsideModal
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Avatar Modal'
),
onTap:
()
=>
showAvatarModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalInsideModal
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Float Modal'
),
onTap:
()
=>
showFloatingModalBottomSheet
(
context:
context
,
builder:
(
context
,
scrollController
)
=>
ModalFit
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Cupertino Modal fit'
),
onTap:
()
=>
showCupertinoModalBottomSheet
(
expand:
false
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalFit
(
scrollController:
scrollController
),
)),
section
(
'COMPLEX CASES'
),
ListTile
(
title:
Text
(
'Cupertino Small Modal forced to expand'
),
onTap:
()
=>
showCupertinoModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalFit
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Reverse list'
),
onTap:
()
=>
showBarModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalInsideModal
(
scrollController:
scrollController
,
reverse:
true
),
)),
ListTile
(
title:
Text
(
'Cupertino Modal inside modal'
),
onTap:
()
=>
showCupertinoModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalInsideModal
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Cupertino Modal with inside navigation'
),
onTap:
()
=>
showCupertinoModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalWithNavigator
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Cupertino Navigator + Scroll + WillPopScope'
),
onTap:
()
=>
showCupertinoModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ComplexModal
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Modal with WillPopScope'
),
onTap:
()
=>
showCupertinoModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalWillScope
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Modal with Nested Scroll'
),
onTap:
()
=>
showCupertinoModalBottomSheet
(
expand:
true
,
context:
context
,
builder:
(
context
,
scrollController
)
=>
NestedScrollModal
(
scrollController:
scrollController
),
)),
],
child:
SizedBox
.
expand
(
child:
SingleChildScrollView
(
primary:
true
,
child:
SafeArea
(
bottom:
false
,
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
stretch
,
mainAxisSize:
MainAxisSize
.
min
,
children:
<
Widget
>[
ListTile
(
title:
Text
(
'Cupertino Photo Share Example'
),
onTap:
()
=>
Navigator
.
of
(
context
).
push
(
MaterialWithModalsPageRoute
(
builder:
(
context
)
=>
CupertinoSharePage
()))),
section
(
'STYLES'
),
ListTile
(
title:
Text
(
'Material fit'
),
onTap:
()
=>
showMaterialModalBottomSheet
(
expand:
false
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalFit
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Bar Modal'
),
onTap:
()
=>
showBarModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalInsideModal
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Avatar Modal'
),
onTap:
()
=>
showAvatarModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalInsideModal
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Float Modal'
),
onTap:
()
=>
showFloatingModalBottomSheet
(
context:
context
,
builder:
(
context
,
scrollController
)
=>
ModalFit
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Cupertino Modal fit'
),
onTap:
()
=>
showCupertinoModalBottomSheet
(
expand:
false
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalFit
(
scrollController:
scrollController
),
)),
section
(
'COMPLEX CASES'
),
ListTile
(
title:
Text
(
'Cupertino Small Modal forced to expand'
),
onTap:
()
=>
showCupertinoModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalFit
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Reverse list'
),
onTap:
()
=>
showBarModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalInsideModal
(
scrollController:
scrollController
,
reverse:
true
),
)),
ListTile
(
title:
Text
(
'Cupertino Modal inside modal'
),
onTap:
()
=>
showCupertinoModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalInsideModal
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Cupertino Modal with inside navigation'
),
onTap:
()
=>
showCupertinoModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalWithNavigator
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Cupertino Navigator + Scroll + WillPopScope'
),
onTap:
()
=>
showCupertinoModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ComplexModal
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Modal with WillPopScope'
),
onTap:
()
=>
showCupertinoModalBottomSheet
(
expand:
true
,
context:
context
,
backgroundColor:
Colors
.
transparent
,
builder:
(
context
,
scrollController
)
=>
ModalWillScope
(
scrollController:
scrollController
),
)),
ListTile
(
title:
Text
(
'Modal with Nested Scroll'
),
onTap:
()
=>
showCupertinoModalBottomSheet
(
expand:
true
,
context:
context
,
builder:
(
context
,
scrollController
)
=>
NestedScrollModal
(
scrollController:
scrollController
),
)),
SizedBox
(
height:
60
,
)
],
),
),
),
),
...
...
lib/src/bottom_sheet.dart
View file @
5e46ad2
...
...
@@ -10,6 +10,7 @@ import 'package:flutter/gestures.dart';
import
'package:flutter/material.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:modal_bottom_sheet/src/utils/primary_scroll_status_bar.dart'
;
const
Duration
_bottomSheetDuration
=
Duration
(
milliseconds:
400
);
const
double
_minFlingVelocity
=
500.0
;
...
...
@@ -249,7 +250,7 @@ class _ModalBottomSheetState extends State<ModalBottomSheet>
if
(
scrollPosition
.
axis
==
Axis
.
horizontal
)
return
;
//Check if scrollController is used
if
(!
_scrollController
.
hasClients
)
return
;
if
(!
_scrollController
.
hasClients
)
return
;
final
isScrollReversed
=
scrollPosition
.
axisDirection
==
AxisDirection
.
down
;
final
offset
=
isScrollReversed
...
...
@@ -334,36 +335,40 @@ class _ModalBottomSheetState extends State<ModalBottomSheet>
curve:
widget
.
animationCurve
??
Curves
.
linear
,
);
return
AnimatedBuilder
(
animation:
widget
.
animationController
,
builder:
(
context
,
_
)
=>
ClipRect
(
child:
CustomSingleChildLayout
(
delegate:
_ModalBottomSheetLayout
(
containerAnimation
.
value
,
widget
.
expanded
),
child:
!
widget
.
enableDrag
?
child
:
KeyedSubtree
(
key:
_childKey
,
child:
AnimatedBuilder
(
animation:
bounceAnimation
,
builder:
(
context
,
_
)
=>
CustomSingleChildLayout
(
delegate:
_CustomBottomSheetLayout
(
bounceAnimation
.
value
),
child:
GestureDetector
(
onVerticalDragUpdate:
(
details
)
=>
_handleDragUpdate
(
details
.
primaryDelta
),
onVerticalDragEnd:
(
details
)
=>
_handleDragEnd
(
details
.
primaryVelocity
),
child:
NotificationListener
<
ScrollNotification
>(
onNotification:
(
ScrollNotification
notification
)
{
_handleScrollUpdate
(
notification
);
return
false
;
},
child:
child
,
),
return
PrimaryScrollStatusBarHandler
(
scrollController:
_scrollController
,
child:
AnimatedBuilder
(
animation:
widget
.
animationController
,
builder:
(
context
,
_
)
=>
ClipRect
(
child:
CustomSingleChildLayout
(
delegate:
_ModalBottomSheetLayout
(
containerAnimation
.
value
,
widget
.
expanded
),
child:
!
widget
.
enableDrag
?
child
:
KeyedSubtree
(
key:
_childKey
,
child:
AnimatedBuilder
(
animation:
bounceAnimation
,
builder:
(
context
,
_
)
=>
CustomSingleChildLayout
(
delegate:
_CustomBottomSheetLayout
(
bounceAnimation
.
value
),
child:
GestureDetector
(
onVerticalDragUpdate:
(
details
)
=>
_handleDragUpdate
(
details
.
primaryDelta
),
onVerticalDragEnd:
(
details
)
=>
_handleDragEnd
(
details
.
primaryVelocity
),
child:
NotificationListener
<
ScrollNotification
>(
onNotification:
(
ScrollNotification
notification
)
{
_handleScrollUpdate
(
notification
);
return
false
;
},
child:
RepaintBoundary
(
child:
child
),
)),
),
),
),
),
),
),
),
);
...
...
lib/src/bottom_sheet_route.dart
View file @
5e46ad2
...
...
@@ -99,6 +99,7 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
builder:
widget
.
route
.
builder
,
enableDrag:
widget
.
enableDrag
,
bounce:
widget
.
bounce
,
scrollController:
widget
.
scrollController
,
animationCurve:
widget
.
animationCurve
,
),
);
...
...
lib/src/bottom_sheets/cupertino_bottom_sheet.dart
View file @
5e46ad2
...
...
@@ -10,8 +10,8 @@ import 'package:flutter/gestures.dart';
import
'package:flutter/material.dart'
show
Colors
,
Theme
,
MaterialLocalizations
,
Theme
,
debugCheckHasMaterialLocalizations
;
import
'package:flutter/services.dart'
;
import
'package:flutter/widgets.dart'
;
...
...
@@ -99,31 +99,32 @@ Future<T> showCupertinoModalBottomSheet<T>({
?
MaterialLocalizations
.
of
(
context
).
modalBarrierDismissLabel
:
''
;
final
result
=
await
Navigator
.
of
(
context
,
rootNavigator:
useRootNavigator
)
.
push
(
CupertinoModalBottomSheetRoute
<
T
>(
builder:
builder
,
containerBuilder:
(
context
,
_
,
child
)
=>
_CupertinoBottomSheetContainer
(
child:
child
,
backgroundColor:
backgroundColor
,
topRadius:
topRadius
,
),
secondAnimationController:
secondAnimation
,
expanded:
expand
,
barrierLabel:
barrierLabel
,
elevation:
elevation
,
bounce:
bounce
,
shape:
shape
,
clipBehavior:
clipBehavior
,
isDismissible:
isDismissible
??
expand
==
false
?
true
:
false
,
modalBarrierColor:
barrierColor
??
Colors
.
black12
,
enableDrag:
enableDrag
,
topRadius:
topRadius
,
animationCurve:
animationCurve
,
previousRouteAnimationCurve:
previousRouteAnimationCurve
,
duration:
duration
,
settings:
settings
,
transitionBackgroundColor:
transitionBackgroundColor
??
Colors
.
black
));
final
result
=
await
Navigator
.
of
(
context
,
rootNavigator:
useRootNavigator
).
push
(
CupertinoModalBottomSheetRoute
<
T
>(
builder:
builder
,
containerBuilder:
(
context
,
_
,
child
)
=>
_CupertinoBottomSheetContainer
(
child:
child
,
backgroundColor:
backgroundColor
,
topRadius:
topRadius
,
),
secondAnimationController:
secondAnimation
,
expanded:
expand
,
barrierLabel:
barrierLabel
,
elevation:
elevation
,
bounce:
bounce
,
shape:
shape
,
clipBehavior:
clipBehavior
,
isDismissible:
isDismissible
??
expand
==
false
?
true
:
false
,
modalBarrierColor:
barrierColor
??
Colors
.
black12
,
enableDrag:
enableDrag
,
topRadius:
topRadius
,
animationCurve:
animationCurve
,
previousRouteAnimationCurve:
previousRouteAnimationCurve
,
duration:
duration
,
settings:
settings
,
transitionBackgroundColor:
transitionBackgroundColor
??
Colors
.
black
),
);
return
result
;
}
...
...
@@ -151,6 +152,7 @@ class CupertinoModalBottomSheetRoute<T> extends ModalBottomSheetRoute<T> {
@required
bool
expanded
,
Duration
duration
,
RouteSettings
settings
,
ScrollController
scrollController
,
this
.
transitionBackgroundColor
,
this
.
topRadius
=
_default_top_radius
,
this
.
previousRouteAnimationCurve
,
...
...
@@ -158,6 +160,7 @@ class CupertinoModalBottomSheetRoute<T> extends ModalBottomSheetRoute<T> {
assert
(
isDismissible
!=
null
),
assert
(
enableDrag
!=
null
),
super
(
scrollController:
scrollController
,
containerBuilder:
containerBuilder
,
builder:
builder
,
bounce:
bounce
,
...
...
@@ -184,17 +187,18 @@ class CupertinoModalBottomSheetRoute<T> extends ModalBottomSheetRoute<T> {
(
paddingTop
+
_behind_widget_visible_height
)
*
0.9
;
final
offsetY
=
secondaryAnimation
.
value
*
(
paddingTop
-
distanceWithScale
);
final
scale
=
1
-
secondaryAnimation
.
value
/
10
;
return
AnimatedBuilder
(
builder:
(
context
,
child
)
=>
Transform
.
translate
(
offset:
Offset
(
0
,
offsetY
),
child:
Transform
.
scale
(
scale:
scale
,
child:
child
,
alignment:
Alignment
.
topCenter
,
return
AnimatedBuilder
(
builder:
(
context
,
child
)
=>
Transform
.
translate
(
offset:
Offset
(
0
,
offsetY
),
child:
Transform
.
scale
(
scale:
scale
,
child:
child
,
alignment:
Alignment
.
topCenter
,
),
),
),
child:
child
,
animation:
secondaryAnimation
,
child:
child
,
animation:
secondaryAnimation
,
);
}
...
...
@@ -243,37 +247,39 @@ class _CupertinoModalTransition extends StatelessWidget {
);
return
AnnotatedRegion
<
SystemUiOverlayStyle
>(
value:
SystemUiOverlayStyle
.
light
,
child:
AnimatedBuilder
(
animation:
curvedAnimation
,
child:
body
,
builder:
(
context
,
child
)
{
final
progress
=
curvedAnimation
.
value
;
final
yOffset
=
progress
*
paddingTop
;
final
scale
=
1
-
progress
/
10
;
final
radius
=
progress
==
0
?
0.0
:
(
1
-
progress
)
*
startRoundCorner
+
progress
*
topRadius
.
x
;
return
Stack
(
children:
<
Widget
>[
Container
(
color:
backgroundColor
),
Transform
.
translate
(
offset:
Offset
(
0
,
yOffset
),
child:
Transform
.
scale
(
scale:
scale
,
alignment:
Alignment
.
topCenter
,
child:
ClipRRect
(
borderRadius:
BorderRadius
.
circular
(
radius
),
child:
child
),
),
)
],
);
},
));
value:
SystemUiOverlayStyle
.
light
,
child:
AnimatedBuilder
(
animation:
curvedAnimation
,
child:
body
,
builder:
(
context
,
child
)
{
final
progress
=
curvedAnimation
.
value
;
final
yOffset
=
progress
*
paddingTop
;
final
scale
=
1
-
progress
/
10
;
final
radius
=
progress
==
0
?
0.0
:
(
1
-
progress
)
*
startRoundCorner
+
progress
*
topRadius
.
x
;
return
Stack
(
children:
<
Widget
>[
Container
(
color:
backgroundColor
),
Transform
.
translate
(
offset:
Offset
(
0
,
yOffset
),
child:
Transform
.
scale
(
scale:
scale
,
alignment:
Alignment
.
topCenter
,
child:
ClipRRect
(
borderRadius:
BorderRadius
.
circular
(
radius
),
child:
child
),
),
),
],
);
},
),
);
}
}
class
_CupertinoScaffold
extends
InheritedWidget
{
final
AnimationController
animation
;
...
...
lib/src/utils/primary_scroll_status_bar.dart
0 → 100644
View file @
5e46ad2
import
'package:flutter/widgets.dart'
;
/// Creates a primary scroll controller that will
/// scroll to the top when tapped on the status bar
///
class
PrimaryScrollStatusBarHandler
extends
StatefulWidget
{
final
ScrollController
scrollController
;
final
Widget
child
;
const
PrimaryScrollStatusBarHandler
(
{
Key
key
,
this
.
child
,
this
.
scrollController
})
:
super
(
key:
key
);
@override
_PrimaryScrollWidgetState
createState
()
=>
_PrimaryScrollWidgetState
();
}
class
_PrimaryScrollWidgetState
extends
State
<
PrimaryScrollStatusBarHandler
>
{
ScrollController
controller
;
@override
void
initState
()
{
controller
=
widget
.
scrollController
??
ScrollController
();
super
.
initState
();
}
@override
Widget
build
(
BuildContext
context
)
{
return
PrimaryScrollController
(
controller:
controller
,
child:
Stack
(
fit:
StackFit
.
expand
,
children:
[
widget
.
child
,
Positioned
(
top:
0
,
left:
0
,
right:
0
,
height:
MediaQuery
.
of
(
context
).
padding
.
top
,
child:
Builder
(
builder:
(
context
)
=>
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
=>
_handleStatusBarTap
(
context
),
// iOS accessibility automatically adds scroll-to-top to the clock in the status bar
excludeFromSemantics:
true
,
),
),
),
],
),
);
}
void
_handleStatusBarTap
(
BuildContext
context
)
{
final
controller
=
PrimaryScrollController
.
of
(
context
);
if
(
controller
.
hasClients
)
{
controller
.
animateTo
(
0.0
,
duration:
const
Duration
(
milliseconds:
300
),
curve:
Curves
.
linear
,
// TODO(ianh): Use a more appropriate curve.
);
}
}
}
...
...
Please
register
or
login
to post a comment