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
3a93061e
Unverified
Commit
3a93061e
authored
Mar 23, 2018
by
Hans Muller
Committed by
GitHub
Mar 23, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Extended Floating Action Button (#15841)
parent
1f5d9041
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
307 additions
and
116 deletions
+307
-116
tabs_fab_demo.dart
...ples/flutter_gallery/lib/demo/material/tabs_fab_demo.dart
+37
-8
floating_action_button.dart
...ages/flutter/lib/src/material/floating_action_button.dart
+114
-43
scaffold.dart
packages/flutter/lib/src/material/scaffold.dart
+105
-63
floating_action_button_test.dart
...es/flutter/test/material/floating_action_button_test.dart
+51
-2
No files found.
examples/flutter_gallery/lib/demo/material/tabs_fab_demo.dart
View file @
3a93061e
...
@@ -44,6 +44,7 @@ class _TabsFabDemoState extends State<TabsFabDemo> with SingleTickerProviderStat
...
@@ -44,6 +44,7 @@ class _TabsFabDemoState extends State<TabsFabDemo> with SingleTickerProviderStat
TabController
_controller
;
TabController
_controller
;
_Page
_selectedPage
;
_Page
_selectedPage
;
bool
_extendedButtons
;
@override
@override
void
initState
()
{
void
initState
()
{
...
@@ -101,6 +102,30 @@ class _TabsFabDemoState extends State<TabsFabDemo> with SingleTickerProviderStat
...
@@ -101,6 +102,30 @@ class _TabsFabDemoState extends State<TabsFabDemo> with SingleTickerProviderStat
);
);
}
}
Widget
buildFloatingActionButton
(
_Page
page
)
{
if
(!
page
.
fabDefined
)
return
null
;
if
(
_extendedButtons
)
{
return
new
FloatingActionButton
.
extended
(
key:
new
ValueKey
<
Key
>(
page
.
fabKey
),
tooltip:
'Show explanation'
,
backgroundColor:
page
.
fabColor
,
icon:
page
.
fabIcon
,
label:
new
Text
(
page
.
label
.
toUpperCase
()),
onPressed:
_showExplanatoryText
);
}
return
new
FloatingActionButton
(
key:
page
.
fabKey
,
tooltip:
'Show explanation'
,
backgroundColor:
page
.
fabColor
,
child:
page
.
fabIcon
,
onPressed:
_showExplanatoryText
);
}
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
new
Scaffold
(
return
new
Scaffold
(
...
@@ -110,15 +135,19 @@ class _TabsFabDemoState extends State<TabsFabDemo> with SingleTickerProviderStat
...
@@ -110,15 +135,19 @@ class _TabsFabDemoState extends State<TabsFabDemo> with SingleTickerProviderStat
bottom:
new
TabBar
(
bottom:
new
TabBar
(
controller:
_controller
,
controller:
_controller
,
tabs:
_allPages
.
map
((
_Page
page
)
=>
new
Tab
(
text:
page
.
label
.
toUpperCase
())).
toList
(),
tabs:
_allPages
.
map
((
_Page
page
)
=>
new
Tab
(
text:
page
.
label
.
toUpperCase
())).
toList
(),
)
),
),
actions:
<
Widget
>[
floatingActionButton:
!
_selectedPage
.
fabDefined
?
null
:
new
FloatingActionButton
(
new
IconButton
(
key:
_selectedPage
.
fabKey
,
icon:
const
Icon
(
Icons
.
sentiment_very_satisfied
),
tooltip:
'Show explanation'
,
onPressed:
()
{
backgroundColor:
_selectedPage
.
fabColor
,
setState
(()
{
child:
_selectedPage
.
fabIcon
,
_extendedButtons
=
!
_extendedButtons
;
onPressed:
_showExplanatoryText
});
},
),
],
),
),
floatingActionButton:
buildFloatingActionButton
(
_selectedPage
),
body:
new
TabBarView
(
body:
new
TabBarView
(
controller:
_controller
,
controller:
_controller
,
children:
_allPages
.
map
(
buildTabView
).
toList
()
children:
_allPages
.
map
(
buildTabView
).
toList
()
...
...
packages/flutter/lib/src/material/floating_action_button.dart
View file @
3a93061e
...
@@ -5,19 +5,28 @@
...
@@ -5,19 +5,28 @@
import
'dart:math'
as
math
;
import
'dart:math'
as
math
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/painting.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/widgets.dart'
;
import
'colors.dart'
;
import
'button.dart'
;
import
'ink_well.dart'
;
import
'material.dart'
;
import
'scaffold.dart'
;
import
'scaffold.dart'
;
import
'theme.dart'
;
import
'theme.dart'
;
import
'tooltip.dart'
;
import
'tooltip.dart'
;
// TODO(eseidel): This needs to change based on device size?
const
BoxConstraints
_kSizeConstraints
=
const
BoxConstraints
.
tightFor
(
// http://material.google.com/layout/metrics-keylines.html#metrics-keylines-keylines-spacing
width:
56.0
,
const
double
_kSize
=
56.0
;
height:
56.0
,
const
double
_kSizeMini
=
40.0
;
);
const
BoxConstraints
_kMiniSizeConstraints
=
const
BoxConstraints
.
tightFor
(
width:
40.0
,
height:
40.0
,
);
const
BoxConstraints
_kExtendedSizeConstraints
=
const
BoxConstraints
(
minHeight:
48.0
,
maxHeight:
48.0
,
);
class
_DefaultHeroTag
{
class
_DefaultHeroTag
{
const
_DefaultHeroTag
();
const
_DefaultHeroTag
();
...
@@ -52,13 +61,15 @@ class _DefaultHeroTag {
...
@@ -52,13 +61,15 @@ class _DefaultHeroTag {
/// * [FlatButton]
/// * [FlatButton]
/// * <https://material.google.com/components/buttons-floating-action-button.html>
/// * <https://material.google.com/components/buttons-floating-action-button.html>
class
FloatingActionButton
extends
StatefulWidget
{
class
FloatingActionButton
extends
StatefulWidget
{
/// Creates a floating action button.
/// Creates a
circular
floating action button.
///
///
/// Most commonly used in the [Scaffold.floatingActionButton] field.
/// The [elevation], [highlightElevation], [mini], [notchMargin], and [shape]
/// arguments must not be null.
const
FloatingActionButton
({
const
FloatingActionButton
({
Key
key
,
Key
key
,
this
.
child
,
this
.
child
,
this
.
tooltip
,
this
.
tooltip
,
this
.
foregroundColor
,
this
.
backgroundColor
,
this
.
backgroundColor
,
this
.
heroTag
:
const
_DefaultHeroTag
(),
this
.
heroTag
:
const
_DefaultHeroTag
(),
this
.
elevation
:
6.0
,
this
.
elevation
:
6.0
,
...
@@ -66,7 +77,54 @@ class FloatingActionButton extends StatefulWidget {
...
@@ -66,7 +77,54 @@ class FloatingActionButton extends StatefulWidget {
@required
this
.
onPressed
,
@required
this
.
onPressed
,
this
.
mini
:
false
,
this
.
mini
:
false
,
this
.
notchMargin
:
4.0
,
this
.
notchMargin
:
4.0
,
})
:
super
(
key:
key
);
this
.
shape
:
const
CircleBorder
(),
this
.
isExtended
:
false
,
})
:
assert
(
elevation
!=
null
),
assert
(
highlightElevation
!=
null
),
assert
(
mini
!=
null
),
assert
(
notchMargin
!=
null
),
assert
(
shape
!=
null
),
assert
(
isExtended
!=
null
),
_sizeConstraints
=
mini
?
_kMiniSizeConstraints
:
_kSizeConstraints
,
super
(
key:
key
);
/// Creates a wider [StadiumBorder] shaped floating action button with both
/// an [icon] and a [label].
///
/// The [label], [icon], [elevation], [highlightElevation]
/// [notchMargin], and [shape] arguments must not be null.
FloatingActionButton
.
extended
({
Key
key
,
this
.
tooltip
,
this
.
foregroundColor
,
this
.
backgroundColor
,
this
.
heroTag
:
const
_DefaultHeroTag
(),
this
.
elevation
:
6.0
,
this
.
highlightElevation
:
12.0
,
@required
this
.
onPressed
,
this
.
notchMargin
:
4.0
,
this
.
shape
:
const
StadiumBorder
(),
this
.
isExtended
:
true
,
@required
Widget
icon
,
@required
Widget
label
,
})
:
assert
(
elevation
!=
null
),
assert
(
highlightElevation
!=
null
),
assert
(
notchMargin
!=
null
),
assert
(
shape
!=
null
),
assert
(
isExtended
!=
null
),
_sizeConstraints
=
_kExtendedSizeConstraints
,
mini
=
false
,
child
=
new
Row
(
mainAxisSize:
MainAxisSize
.
min
,
children:
<
Widget
>[
const
SizedBox
(
width:
16.0
),
icon
,
const
SizedBox
(
width:
8.0
),
label
,
const
SizedBox
(
width:
20.0
),
],
),
super
(
key:
key
);
/// The widget below this widget in the tree.
/// The widget below this widget in the tree.
///
///
...
@@ -79,9 +137,14 @@ class FloatingActionButton extends StatefulWidget {
...
@@ -79,9 +137,14 @@ class FloatingActionButton extends StatefulWidget {
/// used for accessibility.
/// used for accessibility.
final
String
tooltip
;
final
String
tooltip
;
/// The default icon and text color.
///
/// Defaults to [ThemeData.accentIconTheme.color] for the current theme.
final
Color
foregroundColor
;
/// The color to use when filling the button.
/// The color to use when filling the button.
///
///
/// Defaults to
the accent color of
the current theme.
/// Defaults to
[ThemeData.accentColor] for
the current theme.
final
Color
backgroundColor
;
final
Color
backgroundColor
;
/// The tag to apply to the button's [Hero] widget.
/// The tag to apply to the button's [Hero] widget.
...
@@ -141,13 +204,32 @@ class FloatingActionButton extends StatefulWidget {
...
@@ -141,13 +204,32 @@ class FloatingActionButton extends StatefulWidget {
/// floating action button.
/// floating action button.
final
double
notchMargin
;
final
double
notchMargin
;
/// The shape of the button's [Material].
///
/// The button's highlight and splash are clipped to this shape. If the
/// button has an elevation, then its drop shadow is defined by this
/// shape as well.
final
ShapeBorder
shape
;
/// True if this is an "extended" floating action button.
///
/// Typically [extended] buttons have a [StadiumBorder] [shape]
/// and have been created with the [FloatingActionButton.extended]
/// constructor.
///
/// The [Scaffold] animates the appearance of ordinary floating
/// action buttons with scale and rotation transitions. Extended
/// floating action buttons are scaled and faded in.
final
bool
isExtended
;
final
BoxConstraints
_sizeConstraints
;
@override
@override
_FloatingActionButtonState
createState
()
=>
new
_FloatingActionButtonState
();
_FloatingActionButtonState
createState
()
=>
new
_FloatingActionButtonState
();
}
}
class
_FloatingActionButtonState
extends
State
<
FloatingActionButton
>
{
class
_FloatingActionButtonState
extends
State
<
FloatingActionButton
>
{
bool
_highlight
=
false
;
bool
_highlight
=
false
;
VoidCallback
_clearComputeNotch
;
VoidCallback
_clearComputeNotch
;
void
_handleHighlightChanged
(
bool
value
)
{
void
_handleHighlightChanged
(
bool
value
)
{
...
@@ -158,25 +240,33 @@ class _FloatingActionButtonState extends State<FloatingActionButton> {
...
@@ -158,25 +240,33 @@ class _FloatingActionButtonState extends State<FloatingActionButton> {
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
Color
iconColor
=
Colors
.
white
;
final
ThemeData
theme
=
Theme
.
of
(
context
);
Color
materialColor
=
widget
.
backgroundColor
;
final
Color
foregroundColor
=
widget
.
foregroundColor
??
theme
.
accentIconTheme
.
color
;
if
(
materialColor
==
null
)
{
final
ThemeData
themeData
=
Theme
.
of
(
context
);
materialColor
=
themeData
.
accentColor
;
iconColor
=
themeData
.
accentIconTheme
.
color
;
}
Widget
result
;
Widget
result
;
if
(
widget
.
child
!=
null
)
{
if
(
widget
.
child
!=
null
)
{
result
=
new
Center
(
result
=
IconTheme
.
merge
(
child:
IconTheme
.
merge
(
data:
new
IconThemeData
(
data:
new
IconThemeData
(
color:
iconColor
),
color:
foregroundColor
,
child:
widget
.
child
,
),
),
child:
widget
.
child
,
);
);
}
}
result
=
new
RawMaterialButton
(
onPressed:
widget
.
onPressed
,
onHighlightChanged:
_handleHighlightChanged
,
elevation:
_highlight
?
widget
.
highlightElevation
:
widget
.
elevation
,
constraints:
widget
.
_sizeConstraints
,
fillColor:
widget
.
backgroundColor
??
theme
.
accentColor
,
textStyle:
theme
.
accentTextTheme
.
button
.
copyWith
(
color:
foregroundColor
,
letterSpacing:
1.2
,
),
shape:
widget
.
shape
,
child:
result
,
);
if
(
widget
.
tooltip
!=
null
)
{
if
(
widget
.
tooltip
!=
null
)
{
result
=
new
Tooltip
(
result
=
new
Tooltip
(
message:
widget
.
tooltip
,
message:
widget
.
tooltip
,
...
@@ -184,25 +274,6 @@ class _FloatingActionButtonState extends State<FloatingActionButton> {
...
@@ -184,25 +274,6 @@ class _FloatingActionButtonState extends State<FloatingActionButton> {
);
);
}
}
result
=
new
Material
(
color:
materialColor
,
type:
MaterialType
.
circle
,
elevation:
_highlight
?
widget
.
highlightElevation
:
widget
.
elevation
,
child:
new
Container
(
width:
widget
.
mini
?
_kSizeMini
:
_kSize
,
height:
widget
.
mini
?
_kSizeMini
:
_kSize
,
child:
new
Semantics
(
button:
true
,
enabled:
widget
.
onPressed
!=
null
,
child:
new
InkWell
(
onTap:
widget
.
onPressed
,
onHighlightChanged:
_handleHighlightChanged
,
child:
result
,
),
),
),
);
if
(
widget
.
heroTag
!=
null
)
{
if
(
widget
.
heroTag
!=
null
)
{
result
=
new
Hero
(
result
=
new
Hero
(
tag:
widget
.
heroTag
,
tag:
widget
.
heroTag
,
...
...
packages/flutter/lib/src/material/scaffold.dart
View file @
3a93061e
...
@@ -18,6 +18,7 @@ import 'button_theme.dart';
...
@@ -18,6 +18,7 @@ import 'button_theme.dart';
import
'divider.dart'
;
import
'divider.dart'
;
import
'drawer.dart'
;
import
'drawer.dart'
;
import
'flexible_space_bar.dart'
;
import
'flexible_space_bar.dart'
;
import
'floating_action_button.dart'
;
import
'floating_action_button_location.dart'
;
import
'floating_action_button_location.dart'
;
import
'material.dart'
;
import
'material.dart'
;
import
'snack_bar.dart'
;
import
'snack_bar.dart'
;
...
@@ -59,11 +60,11 @@ enum _ScaffoldSlot {
...
@@ -59,11 +60,11 @@ enum _ScaffoldSlot {
/// The geometry of the [Scaffold] after all its contents have been laid out
/// The geometry of the [Scaffold] after all its contents have been laid out
/// except the [FloatingActionButton].
/// except the [FloatingActionButton].
///
///
/// The [Scaffold] passes this prelayout geometry to its
/// The [Scaffold] passes this prelayout geometry to its
/// [FloatingActionButtonLocation], which produces an [Offset] that the
/// [FloatingActionButtonLocation], which produces an [Offset] that the
/// [Scaffold] uses to position the [FloatingActionButton].
/// [Scaffold] uses to position the [FloatingActionButton].
///
///
/// For a description of the [Scaffold]'s geometry after it has
/// For a description of the [Scaffold]'s geometry after it has
/// finished laying out, see the [ScaffoldGeometry].
/// finished laying out, see the [ScaffoldGeometry].
@immutable
@immutable
...
@@ -71,35 +72,35 @@ class ScaffoldPrelayoutGeometry {
...
@@ -71,35 +72,35 @@ class ScaffoldPrelayoutGeometry {
/// Abstract const constructor. This constructor enables subclasses to provide
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
/// const constructors so that they can be used in const expressions.
const
ScaffoldPrelayoutGeometry
({
const
ScaffoldPrelayoutGeometry
({
@required
this
.
bottomSheetSize
,
@required
this
.
bottomSheetSize
,
@required
this
.
contentBottom
,
@required
this
.
contentBottom
,
@required
this
.
contentTop
,
@required
this
.
contentTop
,
@required
this
.
floatingActionButtonSize
,
@required
this
.
floatingActionButtonSize
,
@required
this
.
minInsets
,
@required
this
.
minInsets
,
@required
this
.
scaffoldSize
,
@required
this
.
scaffoldSize
,
@required
this
.
snackBarSize
,
@required
this
.
snackBarSize
,
@required
this
.
textDirection
,
@required
this
.
textDirection
,
});
});
/// The [Size] of [Scaffold.floatingActionButton].
/// The [Size] of [Scaffold.floatingActionButton].
///
///
/// If [Scaffold.floatingActionButton] is null, this will be [Size.zero].
/// If [Scaffold.floatingActionButton] is null, this will be [Size.zero].
final
Size
floatingActionButtonSize
;
final
Size
floatingActionButtonSize
;
/// The [Size] of the [Scaffold]'s [BottomSheet].
/// The [Size] of the [Scaffold]'s [BottomSheet].
///
///
/// If the [Scaffold] is not currently showing a [BottomSheet],
/// If the [Scaffold] is not currently showing a [BottomSheet],
/// this will be [Size.zero].
/// this will be [Size.zero].
final
Size
bottomSheetSize
;
final
Size
bottomSheetSize
;
/// The vertical distance from the Scaffold's origin to the bottom of
/// The vertical distance from the Scaffold's origin to the bottom of
/// [Scaffold.body].
/// [Scaffold.body].
///
///
/// This is useful in a [FloatingActionButtonLocation] designed to
/// This is useful in a [FloatingActionButtonLocation] designed to
/// place the [FloatingActionButton] at the bottom of the screen, while
/// place the [FloatingActionButton] at the bottom of the screen, while
/// keeping it above the [BottomSheet], the [Scaffold.bottomNavigationBar],
/// keeping it above the [BottomSheet], the [Scaffold.bottomNavigationBar],
/// or the keyboard.
/// or the keyboard.
///
///
/// Note that [Scaffold.body] is laid out with respect to [minInsets] already.
/// Note that [Scaffold.body] is laid out with respect to [minInsets] already.
/// This means that a [FloatingActionButtonLocation] does not need to factor
/// This means that a [FloatingActionButtonLocation] does not need to factor
/// in [minInsets.bottom] when aligning a [FloatingActionButton] to [contentBottom].
/// in [minInsets.bottom] when aligning a [FloatingActionButton] to [contentBottom].
...
@@ -107,11 +108,11 @@ class ScaffoldPrelayoutGeometry {
...
@@ -107,11 +108,11 @@ class ScaffoldPrelayoutGeometry {
/// The vertical distance from the [Scaffold]'s origin to the top of
/// The vertical distance from the [Scaffold]'s origin to the top of
/// [Scaffold.body].
/// [Scaffold.body].
///
///
/// This is useful in a [FloatingActionButtonLocation] designed to
/// This is useful in a [FloatingActionButtonLocation] designed to
/// place the [FloatingActionButton] at the top of the screen, while
/// place the [FloatingActionButton] at the top of the screen, while
/// keeping it below the [Scaffold.appBar].
/// keeping it below the [Scaffold.appBar].
///
///
/// Note that [Scaffold.body] is laid out with respect to [minInsets] already.
/// Note that [Scaffold.body] is laid out with respect to [minInsets] already.
/// This means that a [FloatingActionButtonLocation] does not need to factor
/// This means that a [FloatingActionButtonLocation] does not need to factor
/// in [minInsets.top] when aligning a [FloatingActionButton] to [contentTop].
/// in [minInsets.top] when aligning a [FloatingActionButton] to [contentTop].
...
@@ -119,33 +120,33 @@ class ScaffoldPrelayoutGeometry {
...
@@ -119,33 +120,33 @@ class ScaffoldPrelayoutGeometry {
/// The minimum padding to inset the [FloatingActionButton] by for it
/// The minimum padding to inset the [FloatingActionButton] by for it
/// to remain visible.
/// to remain visible.
///
///
/// This value is the result of calling [MediaQuery.padding] in the
/// This value is the result of calling [MediaQuery.padding] in the
/// [Scaffold]'s [BuildContext],
/// [Scaffold]'s [BuildContext],
/// and is useful for insetting the [FloatingActionButton] to avoid features like
/// and is useful for insetting the [FloatingActionButton] to avoid features like
/// the system status bar or the keyboard.
/// the system status bar or the keyboard.
///
///
/// If [Scaffold.resizeToAvoidBottomPadding] is set to false, [minInsets.bottom]
/// If [Scaffold.resizeToAvoidBottomPadding] is set to false, [minInsets.bottom]
/// will be 0.0 instead of [MediaQuery.padding.bottom].
/// will be 0.0 instead of [MediaQuery.padding.bottom].
final
EdgeInsets
minInsets
;
final
EdgeInsets
minInsets
;
/// The [Size] of the whole [Scaffold].
/// The [Size] of the whole [Scaffold].
///
///
/// If the [Size] of the [Scaffold]'s contents is modified by values such as
/// If the [Size] of the [Scaffold]'s contents is modified by values such as
/// [Scaffold.resizeToAvoidBottomPadding] or the keyboard opening, then the
/// [Scaffold.resizeToAvoidBottomPadding] or the keyboard opening, then the
/// [scaffoldSize] will not reflect those changes.
/// [scaffoldSize] will not reflect those changes.
///
///
/// This means that [FloatingActionButtonLocation]s designed to reposition
/// This means that [FloatingActionButtonLocation]s designed to reposition
/// the [FloatingActionButton] based on events such as the keyboard popping
/// the [FloatingActionButton] based on events such as the keyboard popping
/// up should use [minInsets] to make sure that the [FloatingActionButton] is
/// up should use [minInsets] to make sure that the [FloatingActionButton] is
/// inset by enough to remain visible.
/// inset by enough to remain visible.
///
///
/// See [minInsets] and [MediaQuery.padding] for more information on the appropriate
/// See [minInsets] and [MediaQuery.padding] for more information on the appropriate
/// insets to apply.
/// insets to apply.
final
Size
scaffoldSize
;
final
Size
scaffoldSize
;
/// The [Size] of the [Scaffold]'s [SnackBar].
/// The [Size] of the [Scaffold]'s [SnackBar].
///
///
/// If the [Scaffold] is not showing a [SnackBar], this will be [Size.zero].
/// If the [Scaffold] is not showing a [SnackBar], this will be [Size.zero].
final
Size
snackBarSize
;
final
Size
snackBarSize
;
...
@@ -159,7 +160,7 @@ class ScaffoldPrelayoutGeometry {
...
@@ -159,7 +160,7 @@ class ScaffoldPrelayoutGeometry {
/// when a running [FloatingActionButtonLocation] transition is interrupted by a new transition.
/// when a running [FloatingActionButtonLocation] transition is interrupted by a new transition.
@immutable
@immutable
class
_TransitionSnapshotFabLocation
extends
FloatingActionButtonLocation
{
class
_TransitionSnapshotFabLocation
extends
FloatingActionButtonLocation
{
const
_TransitionSnapshotFabLocation
(
this
.
begin
,
this
.
end
,
this
.
animator
,
this
.
progress
);
const
_TransitionSnapshotFabLocation
(
this
.
begin
,
this
.
end
,
this
.
animator
,
this
.
progress
);
final
FloatingActionButtonLocation
begin
;
final
FloatingActionButtonLocation
begin
;
...
@@ -170,8 +171,8 @@ class _TransitionSnapshotFabLocation extends FloatingActionButtonLocation {
...
@@ -170,8 +171,8 @@ class _TransitionSnapshotFabLocation extends FloatingActionButtonLocation {
@override
@override
Offset
getOffset
(
ScaffoldPrelayoutGeometry
scaffoldGeometry
)
{
Offset
getOffset
(
ScaffoldPrelayoutGeometry
scaffoldGeometry
)
{
return
animator
.
getOffset
(
return
animator
.
getOffset
(
begin:
begin
.
getOffset
(
scaffoldGeometry
),
begin:
begin
.
getOffset
(
scaffoldGeometry
),
end:
end
.
getOffset
(
scaffoldGeometry
),
end:
end
.
getOffset
(
scaffoldGeometry
),
progress:
progress
,
progress:
progress
,
);
);
}
}
...
@@ -186,15 +187,15 @@ class _TransitionSnapshotFabLocation extends FloatingActionButtonLocation {
...
@@ -186,15 +187,15 @@ class _TransitionSnapshotFabLocation extends FloatingActionButtonLocation {
///
///
/// To get a [ValueNotifier] for the scaffold geometry of a given
/// To get a [ValueNotifier] for the scaffold geometry of a given
/// [BuildContext], use [Scaffold.geometryOf].
/// [BuildContext], use [Scaffold.geometryOf].
///
///
/// The ScaffoldGeometry is only available during the paint phase, because
/// The ScaffoldGeometry is only available during the paint phase, because
/// its value is computed during the animation and layout phases prior to painting.
/// its value is computed during the animation and layout phases prior to painting.
///
///
/// For an example of using the [ScaffoldGeometry], see the [BottomAppBar],
/// For an example of using the [ScaffoldGeometry], see the [BottomAppBar],
/// which uses the [ScaffoldGeometry] to paint a notch around the
/// which uses the [ScaffoldGeometry] to paint a notch around the
/// [FloatingActionButton].
/// [FloatingActionButton].
///
///
/// For information about the [Scaffold]'s geometry that is used while laying
/// For information about the [Scaffold]'s geometry that is used while laying
/// out the [FloatingActionButton], see [ScaffoldPrelayoutGeometry].
/// out the [FloatingActionButton], see [ScaffoldPrelayoutGeometry].
@immutable
@immutable
class
ScaffoldGeometry
{
class
ScaffoldGeometry
{
...
@@ -217,7 +218,7 @@ class ScaffoldGeometry {
...
@@ -217,7 +218,7 @@ class ScaffoldGeometry {
final
Rect
floatingActionButtonArea
;
final
Rect
floatingActionButtonArea
;
/// A [ComputeNotch] for the floating action button.
/// A [ComputeNotch] for the floating action button.
///
///
/// The contract for this [ComputeNotch] is described in [ComputeNotch] and
/// The contract for this [ComputeNotch] is described in [ComputeNotch] and
/// [Scaffold.setFloatingActionButtonNotchFor].
/// [Scaffold.setFloatingActionButtonNotchFor].
final
ComputeNotch
floatingActionButtonNotch
;
final
ComputeNotch
floatingActionButtonNotch
;
...
@@ -328,7 +329,7 @@ class _ScaffoldGeometryNotifier extends ChangeNotifier implements ValueListenabl
...
@@ -328,7 +329,7 @@ class _ScaffoldGeometryNotifier extends ChangeNotifier implements ValueListenabl
class
_ScaffoldLayout
extends
MultiChildLayoutDelegate
{
class
_ScaffoldLayout
extends
MultiChildLayoutDelegate
{
_ScaffoldLayout
({
_ScaffoldLayout
({
@required
this
.
minInsets
,
@required
this
.
minInsets
,
@required
this
.
textDirection
,
@required
this
.
textDirection
,
@required
this
.
geometryNotifier
,
@required
this
.
geometryNotifier
,
// for floating action button
// for floating action button
...
@@ -336,7 +337,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
...
@@ -336,7 +337,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
@required
this
.
currentFloatingActionButtonLocation
,
@required
this
.
currentFloatingActionButtonLocation
,
@required
this
.
floatingActionButtonMoveAnimationProgress
,
@required
this
.
floatingActionButtonMoveAnimationProgress
,
@required
this
.
floatingActionButtonMotionAnimator
,
@required
this
.
floatingActionButtonMotionAnimator
,
})
:
assert
(
previousFloatingActionButtonLocation
!=
null
),
})
:
assert
(
previousFloatingActionButtonLocation
!=
null
),
assert
(
currentFloatingActionButtonLocation
!=
null
);
assert
(
currentFloatingActionButtonLocation
!=
null
);
final
EdgeInsets
minInsets
;
final
EdgeInsets
minInsets
;
...
@@ -431,7 +432,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
...
@@ -431,7 +432,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
Rect
floatingActionButtonRect
;
Rect
floatingActionButtonRect
;
if
(
hasChild
(
_ScaffoldSlot
.
floatingActionButton
))
{
if
(
hasChild
(
_ScaffoldSlot
.
floatingActionButton
))
{
final
Size
fabSize
=
layoutChild
(
_ScaffoldSlot
.
floatingActionButton
,
looseConstraints
);
final
Size
fabSize
=
layoutChild
(
_ScaffoldSlot
.
floatingActionButton
,
looseConstraints
);
// To account for the FAB position being changed, we'll animate between
// To account for the FAB position being changed, we'll animate between
// the old and new positions.
// the old and new positions.
final
ScaffoldPrelayoutGeometry
currentGeometry
=
new
ScaffoldPrelayoutGeometry
(
final
ScaffoldPrelayoutGeometry
currentGeometry
=
new
ScaffoldPrelayoutGeometry
(
...
@@ -447,8 +448,8 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
...
@@ -447,8 +448,8 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
final
Offset
currentFabOffset
=
currentFloatingActionButtonLocation
.
getOffset
(
currentGeometry
);
final
Offset
currentFabOffset
=
currentFloatingActionButtonLocation
.
getOffset
(
currentGeometry
);
final
Offset
previousFabOffset
=
previousFloatingActionButtonLocation
.
getOffset
(
currentGeometry
);
final
Offset
previousFabOffset
=
previousFloatingActionButtonLocation
.
getOffset
(
currentGeometry
);
final
Offset
fabOffset
=
floatingActionButtonMotionAnimator
.
getOffset
(
final
Offset
fabOffset
=
floatingActionButtonMotionAnimator
.
getOffset
(
begin:
previousFabOffset
,
begin:
previousFabOffset
,
end:
currentFabOffset
,
end:
currentFabOffset
,
progress:
floatingActionButtonMoveAnimationProgress
,
progress:
floatingActionButtonMoveAnimationProgress
,
);
);
positionChild
(
_ScaffoldSlot
.
floatingActionButton
,
fabOffset
);
positionChild
(
_ScaffoldSlot
.
floatingActionButton
,
fabOffset
);
...
@@ -489,7 +490,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
...
@@ -489,7 +490,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
/// Handler for scale and rotation animations in the [FloatingActionButton].
/// Handler for scale and rotation animations in the [FloatingActionButton].
///
///
/// Currently, there are two types of [FloatingActionButton] animations:
/// Currently, there are two types of [FloatingActionButton] animations:
///
///
/// * Entrance/Exit animations, which this widget triggers
/// * Entrance/Exit animations, which this widget triggers
/// when the [FloatingActionButton] is added, updated, or removed.
/// when the [FloatingActionButton] is added, updated, or removed.
/// * Motion animations, which are triggered by the [Scaffold]
/// * Motion animations, which are triggered by the [Scaffold]
...
@@ -501,7 +502,7 @@ class _FloatingActionButtonTransition extends StatefulWidget {
...
@@ -501,7 +502,7 @@ class _FloatingActionButtonTransition extends StatefulWidget {
@required
this
.
fabMoveAnimation
,
@required
this
.
fabMoveAnimation
,
@required
this
.
fabMotionAnimator
,
@required
this
.
fabMotionAnimator
,
@required
this
.
geometryNotifier
,
@required
this
.
geometryNotifier
,
})
:
assert
(
fabMoveAnimation
!=
null
),
})
:
assert
(
fabMoveAnimation
!=
null
),
assert
(
fabMotionAnimator
!=
null
),
assert
(
fabMotionAnimator
!=
null
),
super
(
key:
key
);
super
(
key:
key
);
...
@@ -524,6 +525,7 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
...
@@ -524,6 +525,7 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
AnimationController
_currentController
;
AnimationController
_currentController
;
// The animations to run, considering the widget's fabMoveAnimation and the current/previous entrance/exit animations.
// The animations to run, considering the widget's fabMoveAnimation and the current/previous entrance/exit animations.
Animation
<
double
>
_currentScaleAnimation
;
Animation
<
double
>
_currentScaleAnimation
;
Animation
<
double
>
_extendedCurrentScaleAnimation
;
Animation
<
double
>
_currentRotationAnimation
;
Animation
<
double
>
_currentRotationAnimation
;
Widget
_previousChild
;
Widget
_previousChild
;
...
@@ -535,7 +537,7 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
...
@@ -535,7 +537,7 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
duration:
kFloatingActionButtonSegue
,
duration:
kFloatingActionButtonSegue
,
vsync:
this
,
vsync:
this
,
)..
addStatusListener
(
_handlePreviousAnimationStatusChanged
);
)..
addStatusListener
(
_handlePreviousAnimationStatusChanged
);
_currentController
=
new
AnimationController
(
_currentController
=
new
AnimationController
(
duration:
kFloatingActionButtonSegue
,
duration:
kFloatingActionButtonSegue
,
vsync:
this
,
vsync:
this
,
...
@@ -601,7 +603,10 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
...
@@ -601,7 +603,10 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
curve:
Curves
.
easeIn
,
curve:
Curves
.
easeIn
,
);
);
final
Animation
<
double
>
previousExitRotationAnimation
=
new
Tween
<
double
>(
begin:
1.0
,
end:
1.0
).
animate
(
final
Animation
<
double
>
previousExitRotationAnimation
=
new
Tween
<
double
>(
begin:
1.0
,
end:
1.0
).
animate
(
new
CurvedAnimation
(
parent:
_previousController
,
curve:
Curves
.
easeIn
),
new
CurvedAnimation
(
parent:
_previousController
,
curve:
Curves
.
easeIn
,
),
);
);
final
CurvedAnimation
currentEntranceScaleAnimation
=
new
CurvedAnimation
(
final
CurvedAnimation
currentEntranceScaleAnimation
=
new
CurvedAnimation
(
...
@@ -609,19 +614,26 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
...
@@ -609,19 +614,26 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
curve:
Curves
.
easeIn
,
curve:
Curves
.
easeIn
,
);
);
final
Animation
<
double
>
currentEntranceRotationAnimation
=
new
Tween
<
double
>(
final
Animation
<
double
>
currentEntranceRotationAnimation
=
new
Tween
<
double
>(
begin:
1.0
-
kFloatingActionButtonTurnInterval
,
begin:
1.0
-
kFloatingActionButtonTurnInterval
,
end:
1.0
,
end:
1.0
,
).
animate
(
).
animate
(
new
CurvedAnimation
(
parent:
_currentController
,
curve:
Curves
.
easeIn
),
new
CurvedAnimation
(
parent:
_currentController
,
curve:
Curves
.
easeIn
),
);
);
// Get the animations for when the FAB is moving.
// Get the animations for when the FAB is moving.
final
Animation
<
double
>
moveScaleAnimation
=
widget
.
fabMotionAnimator
.
getScaleAnimation
(
parent:
widget
.
fabMoveAnimation
);
final
Animation
<
double
>
moveScaleAnimation
=
widget
.
fabMotionAnimator
.
getScaleAnimation
(
parent:
widget
.
fabMoveAnimation
);
final
Animation
<
double
>
moveRotationAnimation
=
widget
.
fabMotionAnimator
.
getRotationAnimation
(
parent:
widget
.
fabMoveAnimation
);
final
Animation
<
double
>
moveRotationAnimation
=
widget
.
fabMotionAnimator
.
getRotationAnimation
(
parent:
widget
.
fabMoveAnimation
);
// Aggregate the animations.
// Aggregate the animations.
_previousScaleAnimation
=
new
AnimationMin
<
double
>(
moveScaleAnimation
,
previousExitScaleAnimation
);
_previousScaleAnimation
=
new
AnimationMin
<
double
>(
moveScaleAnimation
,
previousExitScaleAnimation
);
_currentScaleAnimation
=
new
AnimationMin
<
double
>(
moveScaleAnimation
,
currentEntranceScaleAnimation
);
_currentScaleAnimation
=
new
AnimationMin
<
double
>(
moveScaleAnimation
,
currentEntranceScaleAnimation
);
_extendedCurrentScaleAnimation
=
new
CurvedAnimation
(
parent:
_currentScaleAnimation
,
curve:
const
Interval
(
0.0
,
0.1
),
);
_previousRotationAnimation
=
new
TrainHoppingAnimation
(
previousExitRotationAnimation
,
moveRotationAnimation
);
_previousRotationAnimation
=
new
TrainHoppingAnimation
(
previousExitRotationAnimation
,
moveRotationAnimation
);
_currentRotationAnimation
=
new
TrainHoppingAnimation
(
currentEntranceRotationAnimation
,
moveRotationAnimation
);
_currentRotationAnimation
=
new
TrainHoppingAnimation
(
currentEntranceRotationAnimation
,
moveRotationAnimation
);
...
@@ -640,26 +652,56 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
...
@@ -640,26 +652,56 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
});
});
}
}
bool
_isExtendedFloatingActionButton
(
Widget
widget
)
{
if
(
widget
is
!
FloatingActionButton
)
return
false
;
final
FloatingActionButton
fab
=
widget
;
return
fab
.
isExtended
;
}
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
final
List
<
Widget
>
children
=
<
Widget
>[];
final
List
<
Widget
>
children
=
<
Widget
>[];
if
(
_previousController
.
status
!=
AnimationStatus
.
dismissed
)
{
if
(
_previousController
.
status
!=
AnimationStatus
.
dismissed
)
{
if
(
_isExtendedFloatingActionButton
(
_previousChild
))
{
children
.
add
(
new
FadeTransition
(
opacity:
_previousScaleAnimation
,
child:
_previousChild
,
));
}
else
{
children
.
add
(
new
ScaleTransition
(
scale:
_previousScaleAnimation
,
child:
new
RotationTransition
(
turns:
_previousRotationAnimation
,
child:
_previousChild
,
),
));
}
}
if
(
_isExtendedFloatingActionButton
(
widget
.
child
))
{
children
.
add
(
new
ScaleTransition
(
children
.
add
(
new
ScaleTransition
(
scale:
_previousScaleAnimation
,
scale:
_extendedCurrentScaleAnimation
,
child:
new
FadeTransition
(
opacity:
_currentScaleAnimation
,
child:
widget
.
child
,
),
));
}
else
{
children
.
add
(
new
ScaleTransition
(
scale:
_currentScaleAnimation
,
child:
new
RotationTransition
(
child:
new
RotationTransition
(
turns:
_
previous
RotationAnimation
,
turns:
_
current
RotationAnimation
,
child:
_previousC
hild
,
child:
widget
.
c
hild
,
),
),
));
));
}
}
children
.
add
(
new
ScaleTransition
(
scale:
_currentScaleAnimation
,
return
new
Stack
(
child:
new
RotationTransition
(
alignment:
Alignment
.
centerRight
,
turns:
_currentRotationAnimation
,
children:
children
,
child:
widget
.
child
,
);
),
));
return
new
Stack
(
children:
children
);
}
}
void
_onProgressChanged
()
{
void
_onProgressChanged
()
{
...
@@ -689,10 +731,10 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
...
@@ -689,10 +731,10 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
/// of an app using the [bottomNavigationBar] property.
/// of an app using the [bottomNavigationBar] property.
/// * [FloatingActionButton], which is a circular button typically shown in the
/// * [FloatingActionButton], which is a circular button typically shown in the
/// bottom right corner of the app using the [floatingActionButton] property.
/// bottom right corner of the app using the [floatingActionButton] property.
/// * [FloatingActionButtonLocation], which is used to place the
/// * [FloatingActionButtonLocation], which is used to place the
/// [floatingActionButton] within the [Scaffold]'s layout.
/// [floatingActionButton] within the [Scaffold]'s layout.
/// * [FloatingActionButtonAnimator], which is used to animate the
/// * [FloatingActionButtonAnimator], which is used to animate the
/// [floatingActionButton] from one [floatingActionButtonLocation] to
/// [floatingActionButton] from one [floatingActionButtonLocation] to
/// another.
/// another.
/// * [Drawer], which is a vertical panel that is typically displayed to the
/// * [Drawer], which is a vertical panel that is typically displayed to the
/// left of the body (and often hidden on phones) using the [drawer]
/// left of the body (and often hidden on phones) using the [drawer]
...
@@ -753,12 +795,12 @@ class Scaffold extends StatefulWidget {
...
@@ -753,12 +795,12 @@ class Scaffold extends StatefulWidget {
final
Widget
floatingActionButton
;
final
Widget
floatingActionButton
;
/// Responsible for determining where the [floatingActionButton] should go.
/// Responsible for determining where the [floatingActionButton] should go.
///
///
/// If null, the [ScaffoldState] will use the default location, [FloatingActionButtonLocation.endFloat].
/// If null, the [ScaffoldState] will use the default location, [FloatingActionButtonLocation.endFloat].
final
FloatingActionButtonLocation
floatingActionButtonLocation
;
final
FloatingActionButtonLocation
floatingActionButtonLocation
;
/// Animator to move the [floatingActionButton] to a new [floatingActionButtonLocation].
/// Animator to move the [floatingActionButton] to a new [floatingActionButtonLocation].
///
///
/// If null, the [ScaffoldState] will use the default animator, [FloatingActionButtonAnimator.scaling].
/// If null, the [ScaffoldState] will use the default animator, [FloatingActionButtonAnimator.scaling].
final
FloatingActionButtonAnimator
floatingActionButtonAnimator
;
final
FloatingActionButtonAnimator
floatingActionButtonAnimator
;
...
@@ -1307,10 +1349,10 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
...
@@ -1307,10 +1349,10 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
_floatingActionButtonAnimator
=
widget
.
floatingActionButtonAnimator
??
_kDefaultFloatingActionButtonAnimator
;
_floatingActionButtonAnimator
=
widget
.
floatingActionButtonAnimator
??
_kDefaultFloatingActionButtonAnimator
;
_previousFloatingActionButtonLocation
=
_floatingActionButtonLocation
;
_previousFloatingActionButtonLocation
=
_floatingActionButtonLocation
;
_floatingActionButtonMoveController
=
new
AnimationController
(
_floatingActionButtonMoveController
=
new
AnimationController
(
vsync:
this
,
vsync:
this
,
lowerBound:
0.0
,
lowerBound:
0.0
,
upperBound:
1.0
,
upperBound:
1.0
,
value:
1.0
,
value:
1.0
,
duration:
kFloatingActionButtonSegue
*
2
,
duration:
kFloatingActionButtonSegue
*
2
,
);
);
}
}
...
@@ -1567,7 +1609,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
...
@@ -1567,7 +1609,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
final
EdgeInsets
minInsets
=
mediaQuery
.
padding
.
copyWith
(
final
EdgeInsets
minInsets
=
mediaQuery
.
padding
.
copyWith
(
bottom:
widget
.
resizeToAvoidBottomPadding
?
mediaQuery
.
viewInsets
.
bottom
:
0.0
,
bottom:
widget
.
resizeToAvoidBottomPadding
?
mediaQuery
.
viewInsets
.
bottom
:
0.0
,
);
);
return
new
_ScaffoldScope
(
return
new
_ScaffoldScope
(
hasDrawer:
hasDrawer
,
hasDrawer:
hasDrawer
,
geometryNotifier:
_geometryNotifier
,
geometryNotifier:
_geometryNotifier
,
...
...
packages/flutter/test/material/floating_action_button_test.dart
View file @
3a93061e
...
@@ -65,10 +65,59 @@ void main() {
...
@@ -65,10 +65,59 @@ void main() {
expect
(
find
.
byType
(
Text
),
findsNothing
);
expect
(
find
.
byType
(
Text
),
findsNothing
);
await
tester
.
longPress
(
find
.
byType
(
FloatingActionButton
));
await
tester
.
longPress
(
find
.
byType
(
FloatingActionButton
));
await
tester
.
pump
();
await
tester
.
pump
AndSettle
();
expect
(
find
.
byType
(
Text
),
findsOneWidget
);
expect
(
find
.
byType
(
Text
),
findsOneWidget
);
});
});
testWidgets
(
'FloatingActionButton.isExtended'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
MaterialApp
(
home:
const
Scaffold
(
floatingActionButton:
const
FloatingActionButton
(
onPressed:
null
),
),
),
);
final
Finder
fabFinder
=
find
.
byType
(
FloatingActionButton
);
FloatingActionButton
getFabWidget
()
{
return
tester
.
widget
<
FloatingActionButton
>(
fabFinder
);
}
expect
(
getFabWidget
().
isExtended
,
false
);
expect
(
getFabWidget
().
shape
,
const
CircleBorder
());
await
tester
.
pumpWidget
(
new
MaterialApp
(
home:
new
Scaffold
(
floatingActionButton:
new
FloatingActionButton
.
extended
(
label:
const
Text
(
'label'
),
icon:
const
Icon
(
Icons
.
android
),
onPressed:
null
,
),
),
),
);
expect
(
getFabWidget
().
isExtended
,
true
);
expect
(
getFabWidget
().
shape
,
const
StadiumBorder
());
expect
(
find
.
text
(
'label'
),
findsOneWidget
);
expect
(
find
.
byType
(
Icon
),
findsOneWidget
);
// Verify that the widget's height is 48 and that its internal
/// horizontal layout is: 16 icon 8 label 20
expect
(
tester
.
getSize
(
fabFinder
).
height
,
48.0
);
final
double
fabLeft
=
tester
.
getTopLeft
(
fabFinder
).
dx
;
final
double
fabRight
=
tester
.
getTopRight
(
fabFinder
).
dx
;
final
double
iconLeft
=
tester
.
getTopLeft
(
find
.
byType
(
Icon
)).
dx
;
final
double
iconRight
=
tester
.
getTopRight
(
find
.
byType
(
Icon
)).
dx
;
final
double
labelLeft
=
tester
.
getTopLeft
(
find
.
text
(
'label'
)).
dx
;
final
double
labelRight
=
tester
.
getTopRight
(
find
.
text
(
'label'
)).
dx
;
expect
(
iconLeft
-
fabLeft
,
16.0
);
expect
(
labelLeft
-
iconRight
,
8.0
);
expect
(
fabRight
-
labelRight
,
20.0
);
});
testWidgets
(
'Floating Action Button heroTag'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Floating Action Button heroTag'
,
(
WidgetTester
tester
)
async
{
BuildContext
theContext
;
BuildContext
theContext
;
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
...
@@ -372,7 +421,7 @@ class GeometryListenerState extends State<GeometryListener> {
...
@@ -372,7 +421,7 @@ class GeometryListenerState extends State<GeometryListener> {
final
ValueListenable
<
ScaffoldGeometry
>
newListenable
=
Scaffold
.
geometryOf
(
context
);
final
ValueListenable
<
ScaffoldGeometry
>
newListenable
=
Scaffold
.
geometryOf
(
context
);
if
(
geometryListenable
==
newListenable
)
if
(
geometryListenable
==
newListenable
)
return
;
return
;
geometryListenable
=
newListenable
;
geometryListenable
=
newListenable
;
cache
=
new
GeometryCachePainter
(
geometryListenable
);
cache
=
new
GeometryCachePainter
(
geometryListenable
);
}
}
...
...
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