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
98333cb2
Commit
98333cb2
authored
Nov 20, 2015
by
Ian Hickson
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #508 from Hixie/yak3-bottomsheets
Convert Persistent Bottom Sheets to a Scaffold API
parents
6a2bd421
03e094aa
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
310 additions
and
144 deletions
+310
-144
stock_home.dart
examples/stocks/lib/stock_home.dart
+9
-13
stock_symbol_viewer.dart
examples/stocks/lib/stock_symbol_viewer.dart
+2
-2
bottom_sheet.dart
packages/flutter/lib/src/material/bottom_sheet.dart
+73
-101
scaffold.dart
packages/flutter/lib/src/material/scaffold.dart
+162
-7
snack_bar.dart
packages/flutter/lib/src/material/snack_bar.dart
+4
-4
framework.dart
packages/flutter/lib/src/widgets/framework.dart
+4
-1
navigator.dart
packages/flutter/lib/src/widgets/navigator.dart
+22
-0
bottom_sheet_test.dart
packages/unit/test/widget/bottom_sheet_test.dart
+33
-15
widget_tester.dart
packages/unit/test/widget/widget_tester.dart
+1
-1
No files found.
examples/stocks/lib/stock_home.dart
View file @
98333cb2
...
...
@@ -20,7 +20,6 @@ class StockHome extends StatefulComponent {
class
StockHomeState
extends
State
<
StockHome
>
{
final
GlobalKey
scaffoldKey
=
new
GlobalKey
();
final
GlobalKey
<
PlaceholderState
>
_bottomSheetPlaceholderKey
=
new
GlobalKey
<
PlaceholderState
>();
bool
_isSearching
=
false
;
String
_searchQuery
;
...
...
@@ -202,11 +201,7 @@ class StockHomeState extends State<StockHome> {
Navigator
.
of
(
context
).
pushNamed
(
'/stock/
${stock.symbol}
'
,
mostValuableKeys:
mostValuableKeys
);
},
onShow:
(
Stock
stock
,
Key
arrowKey
)
{
showBottomSheet
(
placeholderKey:
_bottomSheetPlaceholderKey
,
context:
context
,
child:
new
StockSymbolBottomSheet
(
stock:
stock
)
);
scaffoldKey
.
currentState
.
showBottomSheet
((
BuildContext
context
)
=>
new
StockSymbolBottomSheet
(
stock:
stock
));
}
);
}
...
...
@@ -256,12 +251,14 @@ class StockHomeState extends State<StockHome> {
showModalBottomSheet
(
// TODO(ianh): Fill this out.
context:
context
,
child:
new
Column
([
new
Input
(
key:
companyNameKey
,
placeholder:
'Company Name'
),
])
builder:
(
BuildContext
context
)
{
return
new
Column
([
new
Input
(
key:
companyNameKey
,
placeholder:
'Company Name'
),
]);
}
);
}
...
...
@@ -278,7 +275,6 @@ class StockHomeState extends State<StockHome> {
key:
scaffoldKey
,
toolBar:
_isSearching
?
buildSearchBar
()
:
buildToolBar
(),
body:
buildTabNavigator
(),
bottomSheet:
new
Placeholder
(
key:
_bottomSheetPlaceholderKey
),
floatingActionButton:
buildFloatingActionButton
()
);
}
...
...
examples/stocks/lib/stock_symbol_viewer.dart
View file @
98333cb2
...
...
@@ -80,11 +80,11 @@ class StockSymbolBottomSheet extends StatelessComponent {
Widget
build
(
BuildContext
context
)
{
return
new
Container
(
child:
new
StockSymbolView
(
stock:
stock
),
padding:
new
EdgeDims
.
all
(
10.0
),
decoration:
new
BoxDecoration
(
border:
new
Border
(
top:
new
BorderSide
(
color:
Colors
.
black26
,
width:
1.0
))
)
),
child:
new
StockSymbolView
(
stock:
stock
)
);
}
}
packages/flutter/lib/src/material/bottom_sheet.dart
View file @
98333cb2
...
...
@@ -18,17 +18,31 @@ const double _kCloseProgressThreshold = 0.5;
const
Color
_kTransparent
=
const
Color
(
0x00000000
);
const
Color
_kBarrierColor
=
Colors
.
black54
;
class
_BottomSheetDragController
extends
StatelessComponent
{
_BottomSheetDragController
({
class
BottomSheet
extends
StatelessComponent
{
BottomSheet
({
Key
key
,
this
.
performance
,
this
.
child
,
this
.
childHeight
})
:
super
(
key:
key
);
this
.
onClosing
,
this
.
childHeight
,
this
.
builder
})
:
super
(
key:
key
)
{
assert
(
onClosing
!=
null
);
}
/// The performance that controls the bottom sheet's position. The BottomSheet
/// widget will manipulate the position of this performance, it is not just a
/// passive observer.
final
Performance
performance
;
final
Widget
child
;
final
VoidCallback
onClosing
;
final
double
childHeight
;
final
WidgetBuilder
builder
;
static
Performance
createPerformance
()
{
return
new
Performance
(
duration:
_kBottomSheetDuration
,
debugLabel:
'BottomSheet'
);
}
bool
get
_dismissUnderway
=>
performance
.
direction
==
AnimationDirection
.
reverse
;
...
...
@@ -42,13 +56,11 @@ class _BottomSheetDragController extends StatelessComponent {
if
(
_dismissUnderway
)
return
;
if
(
velocity
.
dy
>
_kMinFlingVelocity
)
{
performance
.
fling
(
velocity:
-
velocity
.
dy
/
childHeight
).
then
((
_
)
{
Navigator
.
of
(
context
).
pop
();
});
performance
.
fling
(
velocity:
-
velocity
.
dy
/
childHeight
);
onClosing
();
}
else
if
(
performance
.
progress
<
_kCloseProgressThreshold
)
{
performance
.
fling
(
velocity:
-
1.0
).
then
((
_
)
{
Navigator
.
of
(
context
).
pop
();
});
performance
.
fling
(
velocity:
-
1.0
);
onClosing
();
}
else
{
performance
.
forward
();
}
...
...
@@ -58,46 +70,19 @@ class _BottomSheetDragController extends StatelessComponent {
return
new
GestureDetector
(
onVerticalDragUpdate:
_handleDragUpdate
,
onVerticalDragEnd:
(
Offset
velocity
)
{
_handleDragEnd
(
velocity
,
context
);
},
child:
child
child:
new
Material
(
child:
builder
(
context
)
)
);
}
}
class
_BottomSheetRoute
extends
OverlayRoute
{
_BottomSheetRoute
({
this
.
completer
,
this
.
child
});
final
Completer
completer
;
final
Widget
child
;
Performance
performance
;
void
didPush
(
OverlayState
overlay
,
OverlayEntry
insertionPoint
)
{
performance
=
new
Performance
(
duration:
_kBottomSheetDuration
,
debugLabel:
debugLabel
)
..
forward
();
super
.
didPush
(
overlay
,
insertionPoint
);
}
void
didPop
(
dynamic
result
)
{
void
finish
()
{
super
.
didPop
(
result
);
// clear the overlay entries
completer
.
complete
(
result
);
}
if
(
performance
.
isDismissed
)
finish
();
else
performance
.
reverse
().
then
((
_
)
{
finish
();
});
}
String
get
debugLabel
=>
'
$runtimeType
'
;
String
toString
()
=>
'
$runtimeType
(performance:
$performance
)'
;
}
// PERSISTENT BOTTOM SHEETS
class
_ModalBottomSheet
extends
StatefulComponent
{
_ModalBottomSheet
({
Key
key
,
this
.
route
})
:
super
(
key:
key
);
// See scaffold.dart
final
_ModalBottomSheetRoute
route
;
_ModalBottomSheetState
createState
()
=>
new
_ModalBottomSheetState
();
}
// MODAL BOTTOM SHEETS
class
_ModalBottomSheetLayout
extends
OneChildLayoutDelegate
{
// The distance from the bottom of the parent to the top of the BottomSheet child.
...
...
@@ -118,6 +103,14 @@ class _ModalBottomSheetLayout extends OneChildLayoutDelegate {
}
}
class
_ModalBottomSheet
extends
StatefulComponent
{
_ModalBottomSheet
({
Key
key
,
this
.
route
})
:
super
(
key:
key
);
final
_ModalBottomSheetRoute
route
;
_ModalBottomSheetState
createState
()
=>
new
_ModalBottomSheetState
();
}
class
_ModalBottomSheetState
extends
State
<
_ModalBottomSheet
>
{
final
_ModalBottomSheetLayout
_layout
=
new
_ModalBottomSheetLayout
();
...
...
@@ -133,10 +126,11 @@ class _ModalBottomSheetState extends State<_ModalBottomSheet> {
child:
new
CustomOneChildLayout
(
delegate:
_layout
,
token:
_layout
.
childTop
.
value
,
child:
new
_BottomSheetDragController
(
child:
new
BottomSheet
(
performance:
config
.
route
.
performance
,
child:
new
Material
(
child:
config
.
route
.
child
),
childHeight:
_layout
.
childTop
.
end
onClosing:
()
{
Navigator
.
of
(
context
).
pop
();
},
childHeight:
_layout
.
childTop
.
end
,
builder:
config
.
route
.
builder
)
)
);
...
...
@@ -146,9 +140,30 @@ class _ModalBottomSheetState extends State<_ModalBottomSheet> {
}
}
class
_ModalBottomSheetRoute
extends
_BottomSheetRoute
{
_ModalBottomSheetRoute
({
Completer
completer
,
Widget
child
})
:
super
(
completer:
completer
,
child:
child
);
class
_ModalBottomSheetRoute
extends
OverlayRoute
{
_ModalBottomSheetRoute
({
this
.
completer
,
this
.
builder
});
final
Completer
completer
;
final
WidgetBuilder
builder
;
Performance
performance
;
void
didPush
(
OverlayState
overlay
,
OverlayEntry
insertionPoint
)
{
performance
=
BottomSheet
.
createPerformance
()
..
forward
();
super
.
didPush
(
overlay
,
insertionPoint
);
}
void
_finish
(
dynamic
result
)
{
super
.
didPop
(
result
);
// clear the overlay entries
completer
.
complete
(
result
);
}
void
didPop
(
dynamic
result
)
{
if
(
performance
.
isDismissed
)
_finish
(
result
);
else
performance
.
reverse
().
then
((
_
)
{
_finish
(
result
);
});
}
Widget
_buildModalBarrier
(
BuildContext
context
)
{
return
new
AnimatedModalBarrier
(
...
...
@@ -168,61 +183,18 @@ class _ModalBottomSheetRoute extends _BottomSheetRoute {
_buildModalBarrier
,
_buildBottomSheet
,
];
String
get
debugLabel
=>
'
$runtimeType
'
;
String
toString
()
=>
'
$runtimeType
(performance:
$performance
)'
;
}
Future
showModalBottomSheet
(
{
BuildContext
context
,
Widget
child
})
{
assert
(
child
!=
null
);
Future
showModalBottomSheet
(
{
BuildContext
context
,
WidgetBuilder
builder
})
{
assert
(
context
!=
null
);
assert
(
builder
!=
null
);
final
Completer
completer
=
new
Completer
();
Navigator
.
of
(
context
).
pushEphemeral
(
new
_ModalBottomSheetRoute
(
completer:
completer
,
child:
child
builder:
builder
));
return
completer
.
future
;
}
class
_PersistentBottomSheet
extends
StatefulComponent
{
_PersistentBottomSheet
({
Key
key
,
this
.
route
})
:
super
(
key:
key
);
final
_BottomSheetRoute
route
;
_PersistentBottomSheetState
createState
()
=>
new
_PersistentBottomSheetState
();
}
class
_PersistentBottomSheetState
extends
State
<
_PersistentBottomSheet
>
{
double
_childHeight
;
void
_updateChildHeight
(
Size
newSize
)
{
setState
(()
{
_childHeight
=
newSize
.
height
;
});
}
Widget
build
(
BuildContext
context
)
{
return
new
AlignTransition
(
performance:
config
.
route
.
performance
,
alignment:
new
AnimatedValue
<
FractionalOffset
>(
const
FractionalOffset
(
0.0
,
0.0
)),
heightFactor:
new
AnimatedValue
<
double
>(
0.0
,
end:
1.0
),
child:
new
_BottomSheetDragController
(
performance:
config
.
route
.
performance
,
childHeight:
_childHeight
,
child:
new
Material
(
child:
new
SizeObserver
(
child:
config
.
route
.
child
,
onSizeChanged:
_updateChildHeight
)
)
)
);
}
}
Future
showBottomSheet
(
{
BuildContext
context
,
GlobalKey
<
PlaceholderState
>
placeholderKey
,
Widget
child
})
{
assert
(
child
!=
null
);
assert
(
placeholderKey
!=
null
);
final
Completer
completer
=
new
Completer
();
_BottomSheetRoute
route
=
new
_BottomSheetRoute
(
child:
child
,
completer:
completer
);
placeholderKey
.
currentState
.
child
=
new
_PersistentBottomSheet
(
route:
route
);
Navigator
.
of
(
context
).
pushEphemeral
(
route
);
return
completer
.
future
.
then
((
_
)
{
// If our overlay has been obscured by an opaque OverlayEntry then currentState
// will have been cleared already.
placeholderKey
.
currentState
?.
child
=
null
;
});
}
packages/flutter/lib/src/material/scaffold.dart
View file @
98333cb2
...
...
@@ -11,9 +11,10 @@ import 'package:flutter/animation.dart';
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
import
'bottom_sheet.dart'
;
import
'material.dart'
;
import
'tool_bar.dart'
;
import
'snack_bar.dart'
;
import
'tool_bar.dart'
;
const
double
_kFloatingActionButtonMargin
=
16.0
;
// TODO(hmuller): should be device dependent
...
...
@@ -57,7 +58,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
if
(
isChild
(
_Child
.
bottomSheet
))
{
bottomSheetSize
=
layoutChild
(
_Child
.
bottomSheet
,
fullWidthConstraints
);
positionChild
(
_Child
.
bottomSheet
,
new
Point
(
0
.0
,
size
.
height
-
bottomSheetSize
.
height
));
positionChild
(
_Child
.
bottomSheet
,
new
Point
(
(
size
.
width
-
bottomSheetSize
.
width
)
/
2
.0
,
size
.
height
-
bottomSheetSize
.
height
));
}
if
(
isChild
(
_Child
.
snackBar
))
{
...
...
@@ -85,13 +86,11 @@ class Scaffold extends StatefulComponent {
Key
key
,
this
.
toolBar
,
this
.
body
,
this
.
bottomSheet
,
this
.
floatingActionButton
})
:
super
(
key:
key
);
final
ToolBar
toolBar
;
final
Widget
body
;
final
Widget
bottomSheet
;
// this is for non-modal bottom sheets
final
Widget
floatingActionButton
;
static
ScaffoldState
of
(
BuildContext
context
)
=>
context
.
ancestorStateOfType
(
ScaffoldState
);
...
...
@@ -101,6 +100,8 @@ class Scaffold extends StatefulComponent {
class
ScaffoldState
extends
State
<
Scaffold
>
{
// SNACKBAR API
Queue
<
SnackBar
>
_snackBars
=
new
Queue
<
SnackBar
>();
Performance
_snackBarPerformance
;
Timer
_snackBarTimer
;
...
...
@@ -108,6 +109,10 @@ class ScaffoldState extends State<Scaffold> {
void
showSnackBar
(
SnackBar
snackbar
)
{
_snackBarPerformance
??=
SnackBar
.
createPerformance
()
..
addStatusListener
(
_handleSnackBarStatusChange
);
if
(
_snackBars
.
isEmpty
)
{
assert
(
_snackBarPerformance
.
isDismissed
);
_snackBarPerformance
.
forward
();
}
setState
(()
{
_snackBars
.
addLast
(
snackbar
.
withPerformance
(
_snackBarPerformance
));
});
...
...
@@ -120,6 +125,8 @@ class ScaffoldState extends State<Scaffold> {
setState
(()
{
_snackBars
.
removeFirst
();
});
if
(
_snackBars
.
isNotEmpty
)
_snackBarPerformance
.
forward
();
break
;
case
PerformanceStatus
.
completed
:
setState
(()
{
...
...
@@ -138,6 +145,63 @@ class ScaffoldState extends State<Scaffold> {
_snackBarTimer
=
null
;
}
// PERSISTENT BOTTOM SHEET API
List
<
Widget
>
_dismissedBottomSheets
;
BottomSheetController
_currentBottomSheet
;
BottomSheetController
showBottomSheet
(
WidgetBuilder
builder
)
{
if
(
_currentBottomSheet
!=
null
)
{
_currentBottomSheet
.
close
();
assert
(
_currentBottomSheet
==
null
);
}
Completer
completer
=
new
Completer
();
GlobalKey
<
_PersistentBottomSheetState
>
bottomSheetKey
=
new
GlobalKey
<
_PersistentBottomSheetState
>();
Performance
performance
=
BottomSheet
.
createPerformance
()
..
forward
();
_PersistentBottomSheet
bottomSheet
;
Route
route
=
new
StateRoute
(
onPop:
()
{
assert
(
_currentBottomSheet
.
_widget
==
bottomSheet
);
assert
(
bottomSheetKey
.
currentState
!=
null
);
bottomSheetKey
.
currentState
.
close
();
_dismissedBottomSheets
??=
<
Widget
>[];
_dismissedBottomSheets
.
add
(
bottomSheet
);
_currentBottomSheet
=
null
;
completer
.
complete
();
}
);
bottomSheet
=
new
_PersistentBottomSheet
(
key:
bottomSheetKey
,
performance:
performance
,
onClosing:
()
{
assert
(
_currentBottomSheet
.
_widget
==
bottomSheet
);
Navigator
.
of
(
context
).
remove
(
route
);
},
onDismissed:
()
{
assert
(
_dismissedBottomSheets
!=
null
);
setState
(()
{
_dismissedBottomSheets
.
remove
(
bottomSheet
);
});
},
builder:
builder
);
Navigator
.
of
(
context
).
push
(
route
);
setState
(()
{
_currentBottomSheet
=
new
BottomSheetController
.
_
(
bottomSheet
,
completer
.
future
,
()
=>
Navigator
.
of
(
context
).
remove
(
route
),
setState
);
});
return
_currentBottomSheet
;
}
// INTERNALS
void
dispose
()
{
_snackBarPerformance
?.
stop
();
_snackBarPerformance
=
null
;
...
...
@@ -156,8 +220,6 @@ class ScaffoldState extends State<Scaffold> {
final
Widget
materialBody
=
config
.
body
!=
null
?
new
Material
(
child:
config
.
body
)
:
null
;
if
(
_snackBars
.
length
>
0
)
{
if
(
_snackBarPerformance
.
isDismissed
)
_snackBarPerformance
.
forward
();
ModalRoute
route
=
ModalRoute
.
of
(
context
);
if
(
route
==
null
||
route
.
isCurrent
)
{
if
(
_snackBarPerformance
.
isCompleted
&&
_snackBarTimer
==
null
)
...
...
@@ -171,12 +233,105 @@ class ScaffoldState extends State<Scaffold> {
final
List
<
LayoutId
>
children
=
new
List
<
LayoutId
>();
_addIfNonNull
(
children
,
materialBody
,
_Child
.
body
);
_addIfNonNull
(
children
,
paddedToolBar
,
_Child
.
toolBar
);
_addIfNonNull
(
children
,
config
.
bottomSheet
,
_Child
.
bottomSheet
);
if
(
_currentBottomSheet
!=
null
||
(
_dismissedBottomSheets
!=
null
&&
_dismissedBottomSheets
.
isNotEmpty
))
{
List
<
Widget
>
bottomSheets
=
<
Widget
>[];
if
(
_dismissedBottomSheets
!=
null
&&
_dismissedBottomSheets
.
isNotEmpty
)
bottomSheets
.
addAll
(
_dismissedBottomSheets
);
if
(
_currentBottomSheet
!=
null
)
bottomSheets
.
add
(
_currentBottomSheet
.
_widget
);
Widget
stack
=
new
Stack
(
bottomSheets
,
alignment:
const
FractionalOffset
(
0.5
,
1.0
)
// bottom-aligned, centered
);
_addIfNonNull
(
children
,
stack
,
_Child
.
bottomSheet
);
}
if
(
_snackBars
.
isNotEmpty
)
_addIfNonNull
(
children
,
_snackBars
.
first
,
_Child
.
snackBar
);
_addIfNonNull
(
children
,
config
.
floatingActionButton
,
_Child
.
floatingActionButton
);
return
new
CustomMultiChildLayout
(
children
,
delegate:
_scaffoldLayout
);
}
}
class
BottomSheetController
{
const
BottomSheetController
.
_
(
this
.
_widget
,
this
.
closed
,
this
.
close
,
this
.
setState
);
final
Widget
_widget
;
final
Future
closed
;
final
VoidCallback
close
;
// call this to close the bottom sheet
final
StateSetter
setState
;
}
class
_PersistentBottomSheet
extends
StatefulComponent
{
_PersistentBottomSheet
({
Key
key
,
this
.
performance
,
this
.
onClosing
,
this
.
onDismissed
,
this
.
builder
})
:
super
(
key:
key
);
final
Performance
performance
;
final
VoidCallback
onClosing
;
final
VoidCallback
onDismissed
;
final
WidgetBuilder
builder
;
_PersistentBottomSheetState
createState
()
=>
new
_PersistentBottomSheetState
();
}
class
_PersistentBottomSheetState
extends
State
<
_PersistentBottomSheet
>
{
// We take ownership of the performance given in the first configuration.
// We also share control of that performance with out BottomSheet widget.
void
initState
()
{
super
.
initState
();
assert
(
config
.
performance
.
status
==
PerformanceStatus
.
forward
);
config
.
performance
.
addStatusListener
(
_handleStatusChange
);
}
void
didUpdateConfig
(
_PersistentBottomSheet
oldConfig
)
{
super
.
didUpdateConfig
(
oldConfig
);
assert
(
config
.
performance
==
oldConfig
.
performance
);
}
void
dispose
()
{
config
.
performance
.
stop
();
super
.
dispose
();
}
void
close
()
{
config
.
performance
.
reverse
();
}
void
_handleStatusChange
(
PerformanceStatus
status
)
{
if
(
status
==
PerformanceStatus
.
dismissed
&&
config
.
onDismissed
!=
null
)
config
.
onDismissed
();
}
double
_childHeight
;
void
_updateChildHeight
(
Size
newSize
)
{
setState
(()
{
_childHeight
=
newSize
.
height
;
});
}
Widget
build
(
BuildContext
context
)
{
return
new
AlignTransition
(
performance:
config
.
performance
,
alignment:
new
AnimatedValue
<
FractionalOffset
>(
const
FractionalOffset
(
0.0
,
0.0
)),
heightFactor:
new
AnimatedValue
<
double
>(
0.0
,
end:
1.0
),
child:
new
BottomSheet
(
performance:
config
.
performance
,
onClosing:
config
.
onClosing
,
childHeight:
_childHeight
,
builder:
(
BuildContext
context
)
=>
new
SizeObserver
(
child:
config
.
builder
(
context
),
onSizeChanged:
_updateChildHeight
)
)
);
}
}
packages/flutter/lib/src/material/snack_bar.dart
View file @
98333cb2
...
...
@@ -21,10 +21,10 @@ const Color _kSnackBackground = const Color(0xFF323232);
// TODO(ianh): Implement the Tablet version of snackbar if we're "on a tablet".
const
Duration
kSnackBarTransitionDuration
=
const
Duration
(
milliseconds:
250
);
const
Duration
_
kSnackBarTransitionDuration
=
const
Duration
(
milliseconds:
250
);
const
Duration
kSnackBarShortDisplayDuration
=
const
Duration
(
milliseconds:
1500
);
const
Duration
kSnackBarMediumDisplayDuration
=
const
Duration
(
milliseconds:
2750
);
const
Curve
snackBarFadeCurve
=
const
Interval
(
0.72
,
1.0
,
curve:
Curves
.
fastOutSlowIn
);
const
Curve
_
snackBarFadeCurve
=
const
Interval
(
0.72
,
1.0
,
curve:
Curves
.
fastOutSlowIn
);
class
SnackBarAction
extends
StatelessComponent
{
SnackBarAction
({
Key
key
,
this
.
label
,
this
.
onPressed
})
:
super
(
key:
key
)
{
...
...
@@ -91,7 +91,7 @@ class SnackBar extends StatelessComponent {
style:
new
TextStyle
(
color:
Theme
.
of
(
context
).
accentColor
),
child:
new
FadeTransition
(
performance:
performance
,
opacity:
new
AnimatedValue
<
double
>(
0.0
,
end:
1.0
,
curve:
snackBarFadeCurve
),
opacity:
new
AnimatedValue
<
double
>(
0.0
,
end:
1.0
,
curve:
_
snackBarFadeCurve
),
child:
new
Row
(
children
)
)
)
...
...
@@ -105,7 +105,7 @@ class SnackBar extends StatelessComponent {
static
Performance
createPerformance
()
{
return
new
Performance
(
duration:
kSnackBarTransitionDuration
,
duration:
_
kSnackBarTransitionDuration
,
debugLabel:
'SnackBar'
);
}
...
...
packages/flutter/lib/src/widgets/framework.dart
View file @
98333cb2
...
...
@@ -332,6 +332,9 @@ enum _StateLifecycle {
defunct
,
}
/// The signature of setState() methods.
typedef
void
StateSetter
(
VoidCallback
fn
);
/// The logic and internal state for a StatefulComponent.
abstract
class
State
<
T
extends
StatefulComponent
>
{
/// The current configuration (an instance of the corresponding
...
...
@@ -377,7 +380,7 @@ abstract class State<T extends StatefulComponent> {
/// If you just change the state directly without calling setState(), then the
/// component will not be scheduled for rebuilding, meaning that its rendering
/// will not be updated.
void
setState
(
void
fn
()
)
{
void
setState
(
VoidCallback
fn
)
{
assert
(
_debugLifecycleState
!=
_StateLifecycle
.
defunct
);
fn
();
_element
.
markNeedsBuild
();
...
...
packages/flutter/lib/src/widgets/navigator.dart
View file @
98333cb2
...
...
@@ -133,6 +133,28 @@ class NavigatorState extends State<Navigator> {
assert
(
_ephemeral
.
isEmpty
);
}
/// Pops the given route, if it's the current route. If it's not the current
/// route, removes it from the list of active routes without notifying any
/// observers or adjacent routes.
///
/// Do not use this for ModalRoutes, or indeed anything other than
/// StateRoutes. Doing so would cause very odd results, e.g. ModalRoutes would
/// get confused about who is current.
void
remove
(
Route
route
,
[
dynamic
result
])
{
assert
(
_modal
.
contains
(
route
));
assert
(
route
.
overlayEntries
.
isEmpty
);
if
(
_modal
.
last
==
route
)
{
pop
(
result
);
}
else
{
setState
(()
{
_modal
.
remove
(
route
);
route
.
didPop
(
result
);
});
}
}
/// Removes the current route, notifying the observer (if any), and the
/// previous routes (using [Route.didPopNext]).
void
pop
([
dynamic
result
])
{
setState
(()
{
// We use setState to guarantee that we'll rebuild, since the routes can't
...
...
packages/unit/test/widget/bottom_sheet_test.dart
View file @
98333cb2
...
...
@@ -26,7 +26,10 @@ void main() {
tester
.
pump
();
expect
(
tester
.
findText
(
'BottomSheet'
),
isNull
);
showModalBottomSheet
(
context:
context
,
child:
new
Text
(
'BottomSheet'
)).
then
((
_
)
{
showModalBottomSheet
(
context:
context
,
builder:
(
BuildContext
context
)
=>
new
Text
(
'BottomSheet'
)
).
then
((
_
)
{
showBottomSheetThenCalled
=
true
;
});
...
...
@@ -42,7 +45,7 @@ void main() {
expect
(
showBottomSheetThenCalled
,
isTrue
);
expect
(
tester
.
findText
(
'BottomSheet'
),
isNull
);
showModalBottomSheet
(
context:
context
,
child:
new
Text
(
'BottomSheet'
));
showModalBottomSheet
(
context:
context
,
builder:
(
BuildContext
context
)
=>
new
Text
(
'BottomSheet'
));
tester
.
pump
();
// bottom sheet show animation starts
tester
.
pump
(
new
Duration
(
seconds:
1
));
// animation done
expect
(
tester
.
findText
(
'BottomSheet'
),
isNotNull
);
...
...
@@ -58,45 +61,60 @@ void main() {
test
(
'Verify that a downwards fling dismisses a persistent BottomSheet'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
GlobalKey
<
PlaceholderState
>
_bottomSheetPlaceholderKey
=
new
GlobalKey
<
PlaceholderState
>();
BuildContext
context
;
GlobalKey
<
ScaffoldState
>
scaffoldKey
=
new
GlobalKey
<
ScaffoldState
>();
bool
showBottomSheetThenCalled
=
false
;
tester
.
pumpWidget
(
new
MaterialApp
(
routes:
<
String
,
RouteBuilder
>{
'/'
:
(
RouteArguments
args
)
{
context
=
args
.
context
;
return
new
Scaffold
(
bottomSheet:
new
Placeholder
(
key:
_bottomSheetPlaceholderKey
)
,
key:
scaffoldKey
,
body:
new
Center
(
child:
new
Text
(
'body'
))
);
}
}
));
tester
.
pump
(
);
expect
(
showBottomSheetThenCalled
,
isFalse
);
expect
(
tester
.
findText
(
'BottomSheet'
),
isNull
);
showBottomSheet
(
context:
context
,
child:
new
Container
(
child:
new
Text
(
'BottomSheet'
),
margin:
new
EdgeDims
.
all
(
40.0
)),
placeholderKey:
_bottomSheetPlaceholderKey
).
then
((
_
)
{
scaffoldKey
.
currentState
.
showBottomSheet
((
BuildContext
context
)
{
return
new
Container
(
margin:
new
EdgeDims
.
all
(
40.0
),
child:
new
Text
(
'BottomSheet'
)
);
}).
closed
.
then
((
_
)
{
showBottomSheetThenCalled
=
true
;
});
expect
(
_bottomSheetPlaceholderKey
.
currentState
.
child
,
isNotNull
);
expect
(
showBottomSheetThenCalled
,
isFalse
);
expect
(
tester
.
findText
(
'BottomSheet'
),
isNull
);
tester
.
pump
();
// bottom sheet show animation starts
expect
(
showBottomSheetThenCalled
,
isFalse
);
expect
(
tester
.
findText
(
'BottomSheet'
),
isNotNull
);
tester
.
pump
(
new
Duration
(
seconds:
1
));
// animation done
expect
(
showBottomSheetThenCalled
,
isFalse
);
expect
(
tester
.
findText
(
'BottomSheet'
),
isNotNull
);
tester
.
fling
(
tester
.
findText
(
'BottomSheet'
),
const
Offset
(
0.0
,
20.0
),
1000.0
);
tester
.
pump
();
// drain the microtask queue (Future completion callback)
expect
(
showBottomSheetThenCalled
,
isTrue
);
expect
(
tester
.
findText
(
'BottomSheet'
),
isNotNull
);
tester
.
pump
();
// bottom sheet dismiss animation starts
expect
(
showBottomSheetThenCalled
,
isTrue
);
expect
(
tester
.
findText
(
'BottomSheet'
),
isNotNull
);
tester
.
pump
(
new
Duration
(
seconds:
1
));
// animation done
tester
.
pump
(
new
Duration
(
seconds:
1
));
// rebuild frame without the bottom sheet
expect
(
showBottomSheetThenCalled
,
isTrue
);
expect
(
tester
.
findText
(
'BottomSheet'
),
isNull
);
expect
(
_bottomSheetPlaceholderKey
.
currentState
.
child
,
isNull
);
});
});
...
...
packages/unit/test/widget/widget_tester.dart
View file @
98333cb2
...
...
@@ -162,7 +162,7 @@ class WidgetTester {
assert
(
velocity
!=
0.0
);
// velocity is pixels/second
final
TestPointer
p
=
new
TestPointer
(
pointer
);
final
HitTestResult
result
=
_hitTest
(
startLocation
);
final
kMoveCount
=
50
;
// Needs to be >= kHistorySize, see _LeastSquaresVelocityTrackerStrategy
const
int
kMoveCount
=
50
;
// Needs to be >= kHistorySize, see _LeastSquaresVelocityTrackerStrategy
final
double
timeStampDelta
=
1000.0
*
offset
.
distance
/
(
kMoveCount
*
velocity
);
double
timeStamp
=
0.0
;
_dispatchEvent
(
p
.
down
(
startLocation
,
timeStamp:
new
Duration
(
milliseconds:
timeStamp
.
round
())),
result
);
...
...
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