Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
0f0cc449
Unverified
Commit
0f0cc449
authored
Jun 12, 2018
by
Hans Muller
Committed by
GitHub
Jun 12, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Scaffold bottomSheet parameter: persistent, not a route (#18379)
parent
aecb7d96
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
242 additions
and
73 deletions
+242
-73
bottom_sheet.dart
packages/flutter/lib/src/material/bottom_sheet.dart
+36
-20
scaffold.dart
packages/flutter/lib/src/material/scaffold.dart
+148
-53
persistent_bottom_sheet_test.dart
...s/flutter/test/material/persistent_bottom_sheet_test.dart
+58
-0
No files found.
packages/flutter/lib/src/material/bottom_sheet.dart
View file @
0f0cc449
...
...
@@ -24,7 +24,8 @@ const double _kCloseProgressThreshold = 0.5;
/// supplements the primary content of the app. A persistent bottom sheet
/// remains visible even when the user interacts with other parts of the app.
/// Persistent bottom sheets can be created and displayed with the
/// [ScaffoldState.showBottomSheet] function.
/// [ScaffoldState.showBottomSheet] function or by specifying the
/// [Scaffold.bottomSheet] constructor parameter.
///
/// * _Modal_. A modal bottom sheet is an alternative to a menu or a dialog and
/// prevents the user from interacting with the rest of the app. Modal bottom
...
...
@@ -32,8 +33,8 @@ const double _kCloseProgressThreshold = 0.5;
/// function.
///
/// The [BottomSheet] widget itself is rarely used directly. Instead, prefer to
/// create a persistent bottom sheet with [ScaffoldState.showBottomSheet]
and a modal
/// bottom sheet with [showModalBottomSheet].
/// create a persistent bottom sheet with [ScaffoldState.showBottomSheet]
or
///
[Scaffold.bottomSheet], and a modal
bottom sheet with [showModalBottomSheet].
///
/// See also:
///
...
...
@@ -49,9 +50,11 @@ class BottomSheet extends StatefulWidget {
const
BottomSheet
({
Key
key
,
this
.
animationController
,
this
.
enableDrag
=
true
,
@required
this
.
onClosing
,
@required
this
.
builder
})
:
assert
(
onClosing
!=
null
),
})
:
assert
(
enableDrag
!=
null
),
assert
(
onClosing
!=
null
),
assert
(
builder
!=
null
),
super
(
key:
key
);
...
...
@@ -74,6 +77,12 @@ class BottomSheet extends StatefulWidget {
/// [Material] widget.
final
WidgetBuilder
builder
;
/// If true, the bottom sheet can dragged up and down and dismissed by swiping
/// downards.
///
/// Default is true.
final
bool
enableDrag
;
@override
_BottomSheetState
createState
()
=>
new
_BottomSheetState
();
...
...
@@ -124,13 +133,14 @@ class _BottomSheetState extends State<BottomSheet> {
@override
Widget
build
(
BuildContext
context
)
{
return
new
GestureDetector
(
final
Widget
bottomSheet
=
new
Material
(
key:
_childKey
,
child:
widget
.
builder
(
context
),
);
return
!
widget
.
enableDrag
?
bottomSheet
:
new
GestureDetector
(
onVerticalDragUpdate:
_handleDragUpdate
,
onVerticalDragEnd:
_handleDragEnd
,
child:
new
Material
(
key:
_childKey
,
child:
widget
.
builder
(
context
)
)
child:
bottomSheet
,
);
}
}
...
...
@@ -289,24 +299,30 @@ Future<T> showModalBottomSheet<T>({
/// Shows a persistent material design bottom sheet in the nearest [Scaffold].
///
/// Returns a controller that can be used to close and otherwise manipulate the
/// bottom sheet.
///
/// To rebuild the bottom sheet (e.g. if it is stateful), call
/// [PersistentBottomSheetController.setState] on the controller returned by
/// this method.
///
/// The new bottom sheet becomes a [LocalHistoryEntry] for the enclosing
/// [ModalRoute] and a back button is added to the appbar of the [Scaffold]
/// that closes the bottom sheet.
///
/// To create a persistent bottom sheet that is not a [LocalHistoryEntry] and
/// does not add a back button to the enclosing Scaffold's appbar, use the
/// [Scaffold.bottomSheet] constructor parameter.
///
/// A persistent bottom sheet shows information that supplements the primary
/// content of the app. A persistent bottom sheet remains visible even when the
/// user interacts with other parts of the app. A [Scaffold] is required in the
/// given `context`; its [ScaffoldState.showBottomSheet] method is used to
/// actually show the bottom sheet.
/// content of the app. A persistent bottom sheet remains visible even when
/// the user interacts with other parts of the app.
///
/// A closely related widget is a modal bottom sheet, which is an alternative
/// to a menu or a dialog and prevents the user from interacting with the rest
/// of the app. Modal bottom sheets can be created and displayed with the
/// [showModalBottomSheet] function.
///
/// Returns a controller that can be used to close and otherwise manipulate the
/// bottom sheet.
///
/// To rebuild the bottom sheet (e.g. if it is stateful), call
/// [PersistentBottomSheetController.setState] on the value returned from this
/// method.
///
/// The `context` argument is used to look up the [Scaffold] for the bottom
/// sheet. It is only used when the method is called. Its corresponding widget
/// can be safely removed from the tree before the bottom sheet is closed.
...
...
packages/flutter/lib/src/material/scaffold.dart
View file @
0f0cc449
...
...
@@ -763,6 +763,7 @@ class Scaffold extends StatefulWidget {
this
.
drawer
,
this
.
endDrawer
,
this
.
bottomNavigationBar
,
this
.
bottomSheet
,
this
.
backgroundColor
,
this
.
resizeToAvoidBottomPadding
=
true
,
this
.
primary
=
true
,
...
...
@@ -853,6 +854,37 @@ class Scaffold extends StatefulWidget {
/// and the [body].
final
Widget
bottomNavigationBar
;
/// The persistent bottom sheet to display.
///
/// A persistent bottom sheet shows information that supplements the primary
/// content of the app. A persistent bottom sheet remains visible even when
/// the user interacts with other parts of the app.
///
/// A closely related widget is a modal bottom sheet, which is an alternative
/// to a menu or a dialog and prevents the user from interacting with the rest
/// of the app. Modal bottom sheets can be created and displayed with the
/// [showModalBottomSheet] function.
///
/// Unlike the persistent bottom sheet displayed by [showBottomSheet]
/// this bottom sheet is not a [LocalHistoryEntry] and cannot be dismissed
/// with the scaffold appbar's back button.
///
/// If a persistent bottom sheet created with [showBottomSheet] is already
/// visible, it must be closed before building the Scaffold with a new
/// [bottomSheet].
///
/// The value of [bottomSheet] can be any widget at all. It's unlikely to
/// actually be a [BottomSheet], which is used by the implementations of
/// [showBottomSheet] and [showModalBottomSheet]. Typically it's a widget
/// that includes [Material].
///
/// See also:
///
/// * [showBottomSheet], which displays a bottom sheet as a route that can
/// be dismissed with the scaffold's back button.
/// * [showModalBottomSheet], which displays a modal bottom sheet.
final
Widget
bottomSheet
;
/// Whether the [body] (and other floating widgets) should size themselves to
/// avoid the window's bottom padding.
///
...
...
@@ -1214,44 +1246,32 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
final
List
<
_PersistentBottomSheet
>
_dismissedBottomSheets
=
<
_PersistentBottomSheet
>[];
PersistentBottomSheetController
<
dynamic
>
_currentBottomSheet
;
/// Shows a persistent material design bottom sheet.
///
/// A persistent bottom sheet shows information that supplements the primary
/// content of the app. A persistent bottom sheet remains visible even when
/// the user interacts with other parts of the app.
///
/// A closely related widget is a modal bottom sheet, which is an alternative
/// to a menu or a dialog and prevents the user from interacting with the rest
/// of the app. Modal bottom sheets can be created and displayed with the
/// [showModalBottomSheet] function.
///
/// Returns a controller that can be used to close and otherwise manipulate the
/// bottom sheet.
///
/// To rebuild the bottom sheet (e.g. if it is stateful), call
/// [PersistentBottomSheetController.setState] on the value returned from this
/// method.
///
/// See also:
///
/// * [BottomSheet], which is the widget typically returned by the `builder`.
/// * [showBottomSheet], which calls this method given a [BuildContext].
/// * [showModalBottomSheet], which can be used to display a modal bottom
/// sheet.
/// * [Scaffold.of], for information about how to obtain the [ScaffoldState].
/// * <https://material.google.com/components/bottom-sheets.html#bottom-sheets-persistent-bottom-sheets>
PersistentBottomSheetController
<
T
>
showBottomSheet
<
T
>(
WidgetBuilder
builder
)
{
void
_maybeBuildCurrentBottomSheet
()
{
if
(
widget
.
bottomSheet
!=
null
)
{
// The new _currentBottomSheet is not a local history entry so a "back" button
// will not be added to the Scaffold's appbar and the bottom sheet will not
// support drag or swipe to dismiss.
_currentBottomSheet
=
_buildBottomSheet
<
void
>(
(
BuildContext
context
)
=>
widget
.
bottomSheet
,
BottomSheet
.
createAnimationController
(
this
)
..
value
=
1.0
,
false
,
);
}
}
void
_closeCurrentBottomSheet
()
{
if
(
_currentBottomSheet
!=
null
)
{
_currentBottomSheet
.
close
();
assert
(
_currentBottomSheet
==
null
);
}
}
PersistentBottomSheetController
<
T
>
_buildBottomSheet
<
T
>(
WidgetBuilder
builder
,
AnimationController
controller
,
bool
isLocalHistoryEntry
)
{
final
Completer
<
T
>
completer
=
new
Completer
<
T
>();
final
GlobalKey
<
_PersistentBottomSheetState
>
bottomSheetKey
=
new
GlobalKey
<
_PersistentBottomSheetState
>();
final
AnimationController
controller
=
BottomSheet
.
createAnimationController
(
this
)
..
forward
();
_PersistentBottomSheet
bottomSheet
;
final
LocalHistoryEntry
entry
=
new
LocalHistoryEntry
(
onRemove:
()
{
void
_removeCurrentBottomSheet
()
{
assert
(
_currentBottomSheet
.
_widget
==
bottomSheet
);
assert
(
bottomSheetKey
.
currentState
!=
null
);
bottomSheetKey
.
currentState
.
close
();
...
...
@@ -1262,13 +1282,21 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
});
completer
.
complete
();
}
);
final
LocalHistoryEntry
entry
=
isLocalHistoryEntry
?
new
LocalHistoryEntry
(
onRemove:
_removeCurrentBottomSheet
)
:
null
;
bottomSheet
=
new
_PersistentBottomSheet
(
key:
bottomSheetKey
,
animationController:
controller
,
enableDrag:
isLocalHistoryEntry
,
onClosing:
()
{
assert
(
_currentBottomSheet
.
_widget
==
bottomSheet
);
if
(
isLocalHistoryEntry
)
entry
.
remove
();
else
_removeCurrentBottomSheet
();
},
onDismissed:
()
{
if
(
_dismissedBottomSheets
.
contains
(
bottomSheet
))
{
...
...
@@ -1280,14 +1308,59 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
},
builder:
builder
);
if
(
isLocalHistoryEntry
)
ModalRoute
.
of
(
context
).
addLocalHistoryEntry
(
entry
);
setState
(()
{
_currentBottomSheet
=
new
PersistentBottomSheetController
<
T
>.
_
(
return
new
PersistentBottomSheetController
<
T
>.
_
(
bottomSheet
,
completer
,
entry
.
remove
,
(
VoidCallback
fn
)
{
bottomSheetKey
.
currentState
?.
setState
(
fn
);
}
isLocalHistoryEntry
?
entry
.
remove
:
_removeCurrentBottomSheet
,
(
VoidCallback
fn
)
{
bottomSheetKey
.
currentState
?.
setState
(
fn
);
},
isLocalHistoryEntry
,
);
}
/// Shows a persistent material design bottom sheet in the nearest [Scaffold].
///
/// Returns a controller that can be used to close and otherwise manipulate the
/// bottom sheet.
///
/// To rebuild the bottom sheet (e.g. if it is stateful), call
/// [PersistentBottomSheetController.setState] on the controller returned by
/// this method.
///
/// The new bottom sheet becomes a [LocalHistoryEntry] for the enclosing
/// [ModalRoute] and a back button is added to the appbar of the [Scaffold]
/// that closes the bottom sheet.
///
/// To create a persistent bottom sheet that is not a [LocalHistoryEntry] and
/// does not add a back button to the enclosing Scaffold's appbar, use the
/// [Scaffold.bottomSheet] constructor parameter.
///
/// A persistent bottom sheet shows information that supplements the primary
/// content of the app. A persistent bottom sheet remains visible even when
/// the user interacts with other parts of the app.
///
/// A closely related widget is a modal bottom sheet, which is an alternative
/// to a menu or a dialog and prevents the user from interacting with the rest
/// of the app. Modal bottom sheets can be created and displayed with the
/// [showModalBottomSheet] function.
///
/// See also:
///
/// * [BottomSheet], which is the widget typically returned by the `builder`.
/// * [showBottomSheet], which calls this method given a [BuildContext].
/// * [showModalBottomSheet], which can be used to display a modal bottom
/// sheet.
/// * [Scaffold.of], for information about how to obtain the [ScaffoldState].
/// * <https://material.google.com/components/bottom-sheets.html#bottom-sheets-persistent-bottom-sheets>
PersistentBottomSheetController
<
T
>
showBottomSheet
<
T
>(
WidgetBuilder
builder
)
{
_closeCurrentBottomSheet
();
final
AnimationController
controller
=
BottomSheet
.
createAnimationController
(
this
)
..
forward
();
setState
(()
{
_currentBottomSheet
=
_buildBottomSheet
<
T
>(
builder
,
controller
,
true
);
});
return
_currentBottomSheet
;
}
...
...
@@ -1355,6 +1428,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
value:
1.0
,
duration:
kFloatingActionButtonSegue
*
2
,
);
_maybeBuildCurrentBottomSheet
();
}
@override
...
...
@@ -1366,10 +1440,24 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
if
(
widget
.
floatingActionButtonLocation
!=
oldWidget
.
floatingActionButtonLocation
)
{
_moveFloatingActionButton
(
widget
.
floatingActionButtonLocation
??
_kDefaultFloatingActionButtonLocation
);
}
if
(
widget
.
bottomSheet
!=
oldWidget
.
bottomSheet
)
{
assert
(()
{
if
(
widget
.
bottomSheet
!=
null
&&
_currentBottomSheet
?.
_isLocalHistoryEntry
==
true
)
{
throw
new
FlutterError
(
'Scaffold.bottomSheet cannot be specified while a bottom sheet displayed '
'with showBottomSheet() is still visible.
\n
Use the PersistentBottomSheetController '
'returned by showBottomSheet() to close the old bottom sheet before creating '
'a Scaffold with a (non null) bottomSheet.'
);
}
return
true
;
}());
_closeCurrentBottomSheet
();
_maybeBuildCurrentBottomSheet
();
}
super
.
didUpdateWidget
(
oldWidget
);
}
@override
void
dispose
()
{
_snackBarController
?.
dispose
();
...
...
@@ -1659,12 +1747,14 @@ class _PersistentBottomSheet extends StatefulWidget {
const
_PersistentBottomSheet
({
Key
key
,
this
.
animationController
,
this
.
enableDrag
=
true
,
this
.
onClosing
,
this
.
onDismissed
,
this
.
builder
})
:
super
(
key:
key
);
final
AnimationController
animationController
;
// we control it, but it must be disposed by whoever created it
final
bool
enableDrag
;
final
VoidCallback
onClosing
;
final
VoidCallback
onDismissed
;
final
WidgetBuilder
builder
;
...
...
@@ -1677,7 +1767,8 @@ class _PersistentBottomSheetState extends State<_PersistentBottomSheet> {
@override
void
initState
()
{
super
.
initState
();
assert
(
widget
.
animationController
.
status
==
AnimationStatus
.
forward
);
assert
(
widget
.
animationController
.
status
==
AnimationStatus
.
forward
||
widget
.
animationController
.
status
==
AnimationStatus
.
completed
);
widget
.
animationController
.
addStatusListener
(
_handleStatusChange
);
}
...
...
@@ -1711,6 +1802,7 @@ class _PersistentBottomSheetState extends State<_PersistentBottomSheet> {
container:
true
,
child:
new
BottomSheet
(
animationController:
widget
.
animationController
,
enableDrag:
widget
.
enableDrag
,
onClosing:
widget
.
onClosing
,
builder:
widget
.
builder
)
...
...
@@ -1728,8 +1820,11 @@ class PersistentBottomSheetController<T> extends ScaffoldFeatureController<_Pers
_PersistentBottomSheet
widget
,
Completer
<
T
>
completer
,
VoidCallback
close
,
StateSetter
setState
StateSetter
setState
,
this
.
_isLocalHistoryEntry
,
)
:
super
.
_
(
widget
,
completer
,
close
,
setState
);
final
bool
_isLocalHistoryEntry
;
}
class
_ScaffoldScope
extends
InheritedWidget
{
...
...
packages/flutter/test/material/persistent_bottom_sheet_test.dart
View file @
0f0cc449
...
...
@@ -133,4 +133,62 @@ void main() {
),
);
});
testWidgets
(
'Scaffold.bottomSheet'
,
(
WidgetTester
tester
)
async
{
final
Key
bottomSheetKey
=
new
UniqueKey
();
await
tester
.
pumpWidget
(
new
MaterialApp
(
home:
new
Scaffold
(
body:
const
Placeholder
(),
bottomSheet:
new
Container
(
key:
bottomSheetKey
,
alignment:
Alignment
.
center
,
height:
200.0
,
child:
new
Builder
(
builder:
(
BuildContext
context
)
{
return
new
RaisedButton
(
child:
const
Text
(
'showModalBottomSheet'
),
onPressed:
()
{
showModalBottomSheet
<
void
>(
context:
context
,
builder:
(
BuildContext
context
)
=>
const
Text
(
'modal bottom sheet'
),
);
},
);
},
),
),
),
),
);
expect
(
find
.
text
(
'showModalBottomSheet'
),
findsOneWidget
);
expect
(
tester
.
getSize
(
find
.
byKey
(
bottomSheetKey
)),
const
Size
(
800.0
,
200.0
));
expect
(
tester
.
getTopLeft
(
find
.
byKey
(
bottomSheetKey
)),
const
Offset
(
0.0
,
400.0
));
// Show the modal bottomSheet
await
tester
.
tap
(
find
.
text
(
'showModalBottomSheet'
));
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'modal bottom sheet'
),
findsOneWidget
);
// Dismiss the modal bottomSheet
await
tester
.
tap
(
find
.
text
(
'modal bottom sheet'
));
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'modal bottom sheet'
),
findsNothing
);
expect
(
find
.
text
(
'showModalBottomSheet'
),
findsOneWidget
);
// Remove the persistent bottomSheet
await
tester
.
pumpWidget
(
new
MaterialApp
(
home:
const
Scaffold
(
bottomSheet:
null
,
body:
const
Placeholder
(),
),
),
);
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'showModalBottomSheet'
),
findsNothing
);
expect
(
find
.
byKey
(
bottomSheetKey
),
findsNothing
);
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment