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
TabController
_controller
;
_Page
_selectedPage
;
bool
_extendedButtons
;
@override
void
initState
()
{
...
...
@@ -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
Widget
build
(
BuildContext
context
)
{
return
new
Scaffold
(
...
...
@@ -110,15 +135,19 @@ class _TabsFabDemoState extends State<TabsFabDemo> with SingleTickerProviderStat
bottom:
new
TabBar
(
controller:
_controller
,
tabs:
_allPages
.
map
((
_Page
page
)
=>
new
Tab
(
text:
page
.
label
.
toUpperCase
())).
toList
(),
)
),
floatingActionButton:
!
_selectedPage
.
fabDefined
?
null
:
new
FloatingActionButton
(
key:
_selectedPage
.
fabKey
,
tooltip:
'Show explanation'
,
backgroundColor:
_selectedPage
.
fabColor
,
child:
_selectedPage
.
fabIcon
,
onPressed:
_showExplanatoryText
),
actions:
<
Widget
>[
new
IconButton
(
icon:
const
Icon
(
Icons
.
sentiment_very_satisfied
),
onPressed:
()
{
setState
(()
{
_extendedButtons
=
!
_extendedButtons
;
});
},
),
],
),
floatingActionButton:
buildFloatingActionButton
(
_selectedPage
),
body:
new
TabBarView
(
controller:
_controller
,
children:
_allPages
.
map
(
buildTabView
).
toList
()
...
...
packages/flutter/lib/src/material/floating_action_button.dart
View file @
3a93061e
...
...
@@ -5,19 +5,28 @@
import
'dart:math'
as
math
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/painting.dart'
;
import
'package:flutter/widgets.dart'
;
import
'colors.dart'
;
import
'ink_well.dart'
;
import
'material.dart'
;
import
'button.dart'
;
import
'scaffold.dart'
;
import
'theme.dart'
;
import
'tooltip.dart'
;
// TODO(eseidel): This needs to change based on device size?
// http://material.google.com/layout/metrics-keylines.html#metrics-keylines-keylines-spacing
const
double
_kSize
=
56.0
;
const
double
_kSizeMini
=
40.0
;
const
BoxConstraints
_kSizeConstraints
=
const
BoxConstraints
.
tightFor
(
width:
56.0
,
height:
56.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
{
const
_DefaultHeroTag
();
...
...
@@ -52,13 +61,15 @@ class _DefaultHeroTag {
/// * [FlatButton]
/// * <https://material.google.com/components/buttons-floating-action-button.html>
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
({
Key
key
,
this
.
child
,
this
.
tooltip
,
this
.
foregroundColor
,
this
.
backgroundColor
,
this
.
heroTag
:
const
_DefaultHeroTag
(),
this
.
elevation
:
6.0
,
...
...
@@ -66,7 +77,54 @@ class FloatingActionButton extends StatefulWidget {
@required
this
.
onPressed
,
this
.
mini
:
false
,
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.
///
...
...
@@ -79,9 +137,14 @@ class FloatingActionButton extends StatefulWidget {
/// used for accessibility.
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.
///
/// Defaults to
the accent color of
the current theme.
/// Defaults to
[ThemeData.accentColor] for
the current theme.
final
Color
backgroundColor
;
/// The tag to apply to the button's [Hero] widget.
...
...
@@ -141,13 +204,32 @@ class FloatingActionButton extends StatefulWidget {
/// floating action button.
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
_FloatingActionButtonState
createState
()
=>
new
_FloatingActionButtonState
();
}
class
_FloatingActionButtonState
extends
State
<
FloatingActionButton
>
{
bool
_highlight
=
false
;
VoidCallback
_clearComputeNotch
;
void
_handleHighlightChanged
(
bool
value
)
{
...
...
@@ -158,25 +240,33 @@ class _FloatingActionButtonState extends State<FloatingActionButton> {
@override
Widget
build
(
BuildContext
context
)
{
Color
iconColor
=
Colors
.
white
;
Color
materialColor
=
widget
.
backgroundColor
;
if
(
materialColor
==
null
)
{
final
ThemeData
themeData
=
Theme
.
of
(
context
);
materialColor
=
themeData
.
accentColor
;
iconColor
=
themeData
.
accentIconTheme
.
color
;
}
final
ThemeData
theme
=
Theme
.
of
(
context
);
final
Color
foregroundColor
=
widget
.
foregroundColor
??
theme
.
accentIconTheme
.
color
;
Widget
result
;
if
(
widget
.
child
!=
null
)
{
result
=
new
Center
(
child:
IconTheme
.
merge
(
data:
new
IconThemeData
(
color:
iconColor
),
child:
widget
.
child
,
result
=
IconTheme
.
merge
(
data:
new
IconThemeData
(
color:
foregroundColor
,
),
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
)
{
result
=
new
Tooltip
(
message:
widget
.
tooltip
,
...
...
@@ -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
)
{
result
=
new
Hero
(
tag:
widget
.
heroTag
,
...
...
packages/flutter/lib/src/material/scaffold.dart
View file @
3a93061e
...
...
@@ -18,6 +18,7 @@ import 'button_theme.dart';
import
'divider.dart'
;
import
'drawer.dart'
;
import
'flexible_space_bar.dart'
;
import
'floating_action_button.dart'
;
import
'floating_action_button_location.dart'
;
import
'material.dart'
;
import
'snack_bar.dart'
;
...
...
@@ -59,11 +60,11 @@ enum _ScaffoldSlot {
/// The geometry of the [Scaffold] after all its contents have been laid out
/// except the [FloatingActionButton].
///
///
/// 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].
///
///
/// For a description of the [Scaffold]'s geometry after it has
/// finished laying out, see the [ScaffoldGeometry].
@immutable
...
...
@@ -71,35 +72,35 @@ class ScaffoldPrelayoutGeometry {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const
ScaffoldPrelayoutGeometry
({
@required
this
.
bottomSheetSize
,
@required
this
.
contentBottom
,
@required
this
.
contentTop
,
@required
this
.
floatingActionButtonSize
,
@required
this
.
minInsets
,
@required
this
.
scaffoldSize
,
@required
this
.
snackBarSize
,
@required
this
.
bottomSheetSize
,
@required
this
.
contentBottom
,
@required
this
.
contentTop
,
@required
this
.
floatingActionButtonSize
,
@required
this
.
minInsets
,
@required
this
.
scaffoldSize
,
@required
this
.
snackBarSize
,
@required
this
.
textDirection
,
});
/// The [Size] of [Scaffold.floatingActionButton].
///
///
/// If [Scaffold.floatingActionButton] is null, this will be [Size.zero].
final
Size
floatingActionButtonSize
;
/// The [Size] of the [Scaffold]'s [BottomSheet].
///
///
/// If the [Scaffold] is not currently showing a [BottomSheet],
/// this will be [Size.zero].
final
Size
bottomSheetSize
;
/// The vertical distance from the Scaffold's origin to the bottom of
/// [Scaffold.body].
///
///
/// This is useful in a [FloatingActionButtonLocation] designed to
/// place the [FloatingActionButton] at the bottom of the screen, while
/// keeping it above the [BottomSheet], the [Scaffold.bottomNavigationBar],
/// or the keyboard.
///
///
/// Note that [Scaffold.body] is laid out with respect to [minInsets] already.
/// This means that a [FloatingActionButtonLocation] does not need to factor
/// in [minInsets.bottom] when aligning a [FloatingActionButton] to [contentBottom].
...
...
@@ -107,11 +108,11 @@ class ScaffoldPrelayoutGeometry {
/// The vertical distance from the [Scaffold]'s origin to the top of
/// [Scaffold.body].
///
///
/// This is useful in a [FloatingActionButtonLocation] designed to
/// place the [FloatingActionButton] at the top of the screen, while
/// keeping it below the [Scaffold.appBar].
///
///
/// Note that [Scaffold.body] is laid out with respect to [minInsets] already.
/// This means that a [FloatingActionButtonLocation] does not need to factor
/// in [minInsets.top] when aligning a [FloatingActionButton] to [contentTop].
...
...
@@ -119,33 +120,33 @@ class ScaffoldPrelayoutGeometry {
/// The minimum padding to inset the [FloatingActionButton] by for it
/// to remain visible.
///
///
/// This value is the result of calling [MediaQuery.padding] in the
/// [Scaffold]'s [BuildContext],
/// and is useful for insetting the [FloatingActionButton] to avoid features like
/// the system status bar or the keyboard.
///
///
/// If [Scaffold.resizeToAvoidBottomPadding] is set to false, [minInsets.bottom]
/// will be 0.0 instead of [MediaQuery.padding.bottom].
final
EdgeInsets
minInsets
;
/// 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
/// [scaffoldSize] will not reflect those changes.
///
///
/// This means that [FloatingActionButtonLocation]s designed to reposition
/// the [FloatingActionButton] based on events such as the keyboard popping
/// up should use [minInsets] to make sure that the [FloatingActionButton] is
/// inset by enough to remain visible.
///
///
/// See [minInsets] and [MediaQuery.padding] for more information on the appropriate
/// insets to apply.
final
Size
scaffoldSize
;
/// The [Size] of the [Scaffold]'s [SnackBar].
///
///
/// If the [Scaffold] is not showing a [SnackBar], this will be [Size.zero].
final
Size
snackBarSize
;
...
...
@@ -159,7 +160,7 @@ class ScaffoldPrelayoutGeometry {
/// when a running [FloatingActionButtonLocation] transition is interrupted by a new transition.
@immutable
class
_TransitionSnapshotFabLocation
extends
FloatingActionButtonLocation
{
const
_TransitionSnapshotFabLocation
(
this
.
begin
,
this
.
end
,
this
.
animator
,
this
.
progress
);
final
FloatingActionButtonLocation
begin
;
...
...
@@ -170,8 +171,8 @@ class _TransitionSnapshotFabLocation extends FloatingActionButtonLocation {
@override
Offset
getOffset
(
ScaffoldPrelayoutGeometry
scaffoldGeometry
)
{
return
animator
.
getOffset
(
begin:
begin
.
getOffset
(
scaffoldGeometry
),
end:
end
.
getOffset
(
scaffoldGeometry
),
begin:
begin
.
getOffset
(
scaffoldGeometry
),
end:
end
.
getOffset
(
scaffoldGeometry
),
progress:
progress
,
);
}
...
...
@@ -186,15 +187,15 @@ class _TransitionSnapshotFabLocation extends FloatingActionButtonLocation {
///
/// To get a [ValueNotifier] for the scaffold geometry of a given
/// [BuildContext], use [Scaffold.geometryOf].
///
///
/// The ScaffoldGeometry is only available during the paint phase, because
/// its value is computed during the animation and layout phases prior to painting.
///
///
/// For an example of using the [ScaffoldGeometry], see the [BottomAppBar],
/// which uses the [ScaffoldGeometry] to paint a notch around the
/// [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].
@immutable
class
ScaffoldGeometry
{
...
...
@@ -217,7 +218,7 @@ class ScaffoldGeometry {
final
Rect
floatingActionButtonArea
;
/// A [ComputeNotch] for the floating action button.
///
///
/// The contract for this [ComputeNotch] is described in [ComputeNotch] and
/// [Scaffold.setFloatingActionButtonNotchFor].
final
ComputeNotch
floatingActionButtonNotch
;
...
...
@@ -328,7 +329,7 @@ class _ScaffoldGeometryNotifier extends ChangeNotifier implements ValueListenabl
class
_ScaffoldLayout
extends
MultiChildLayoutDelegate
{
_ScaffoldLayout
({
@required
this
.
minInsets
,
@required
this
.
minInsets
,
@required
this
.
textDirection
,
@required
this
.
geometryNotifier
,
// for floating action button
...
...
@@ -336,7 +337,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
@required
this
.
currentFloatingActionButtonLocation
,
@required
this
.
floatingActionButtonMoveAnimationProgress
,
@required
this
.
floatingActionButtonMotionAnimator
,
})
:
assert
(
previousFloatingActionButtonLocation
!=
null
),
})
:
assert
(
previousFloatingActionButtonLocation
!=
null
),
assert
(
currentFloatingActionButtonLocation
!=
null
);
final
EdgeInsets
minInsets
;
...
...
@@ -431,7 +432,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
Rect
floatingActionButtonRect
;
if
(
hasChild
(
_ScaffoldSlot
.
floatingActionButton
))
{
final
Size
fabSize
=
layoutChild
(
_ScaffoldSlot
.
floatingActionButton
,
looseConstraints
);
// To account for the FAB position being changed, we'll animate between
// the old and new positions.
final
ScaffoldPrelayoutGeometry
currentGeometry
=
new
ScaffoldPrelayoutGeometry
(
...
...
@@ -447,8 +448,8 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
final
Offset
currentFabOffset
=
currentFloatingActionButtonLocation
.
getOffset
(
currentGeometry
);
final
Offset
previousFabOffset
=
previousFloatingActionButtonLocation
.
getOffset
(
currentGeometry
);
final
Offset
fabOffset
=
floatingActionButtonMotionAnimator
.
getOffset
(
begin:
previousFabOffset
,
end:
currentFabOffset
,
begin:
previousFabOffset
,
end:
currentFabOffset
,
progress:
floatingActionButtonMoveAnimationProgress
,
);
positionChild
(
_ScaffoldSlot
.
floatingActionButton
,
fabOffset
);
...
...
@@ -489,7 +490,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
/// Handler for scale and rotation animations in the [FloatingActionButton].
///
/// Currently, there are two types of [FloatingActionButton] animations:
///
///
/// * Entrance/Exit animations, which this widget triggers
/// when the [FloatingActionButton] is added, updated, or removed.
/// * Motion animations, which are triggered by the [Scaffold]
...
...
@@ -501,7 +502,7 @@ class _FloatingActionButtonTransition extends StatefulWidget {
@required
this
.
fabMoveAnimation
,
@required
this
.
fabMotionAnimator
,
@required
this
.
geometryNotifier
,
})
:
assert
(
fabMoveAnimation
!=
null
),
})
:
assert
(
fabMoveAnimation
!=
null
),
assert
(
fabMotionAnimator
!=
null
),
super
(
key:
key
);
...
...
@@ -524,6 +525,7 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
AnimationController
_currentController
;
// The animations to run, considering the widget's fabMoveAnimation and the current/previous entrance/exit animations.
Animation
<
double
>
_currentScaleAnimation
;
Animation
<
double
>
_extendedCurrentScaleAnimation
;
Animation
<
double
>
_currentRotationAnimation
;
Widget
_previousChild
;
...
...
@@ -535,7 +537,7 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
duration:
kFloatingActionButtonSegue
,
vsync:
this
,
)..
addStatusListener
(
_handlePreviousAnimationStatusChanged
);
_currentController
=
new
AnimationController
(
duration:
kFloatingActionButtonSegue
,
vsync:
this
,
...
...
@@ -601,7 +603,10 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
curve:
Curves
.
easeIn
,
);
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
(
...
...
@@ -609,19 +614,26 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
curve:
Curves
.
easeIn
,
);
final
Animation
<
double
>
currentEntranceRotationAnimation
=
new
Tween
<
double
>(
begin:
1.0
-
kFloatingActionButtonTurnInterval
,
begin:
1.0
-
kFloatingActionButtonTurnInterval
,
end:
1.0
,
).
animate
(
new
CurvedAnimation
(
parent:
_currentController
,
curve:
Curves
.
easeIn
),
new
CurvedAnimation
(
parent:
_currentController
,
curve:
Curves
.
easeIn
),
);
// Get the animations for when the FAB is moving.
final
Animation
<
double
>
moveScaleAnimation
=
widget
.
fabMotionAnimator
.
getScaleAnimation
(
parent:
widget
.
fabMoveAnimation
);
final
Animation
<
double
>
moveRotationAnimation
=
widget
.
fabMotionAnimator
.
getRotationAnimation
(
parent:
widget
.
fabMoveAnimation
);
// Aggregate the animations.
_previousScaleAnimation
=
new
AnimationMin
<
double
>(
moveScaleAnimation
,
previousExitScaleAnimation
);
_currentScaleAnimation
=
new
AnimationMin
<
double
>(
moveScaleAnimation
,
currentEntranceScaleAnimation
);
_extendedCurrentScaleAnimation
=
new
CurvedAnimation
(
parent:
_currentScaleAnimation
,
curve:
const
Interval
(
0.0
,
0.1
),
);
_previousRotationAnimation
=
new
TrainHoppingAnimation
(
previousExitRotationAnimation
,
moveRotationAnimation
);
_currentRotationAnimation
=
new
TrainHoppingAnimation
(
currentEntranceRotationAnimation
,
moveRotationAnimation
);
...
...
@@ -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
Widget
build
(
BuildContext
context
)
{
final
List
<
Widget
>
children
=
<
Widget
>[];
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
(
scale:
_previousScaleAnimation
,
scale:
_extendedCurrentScaleAnimation
,
child:
new
FadeTransition
(
opacity:
_currentScaleAnimation
,
child:
widget
.
child
,
),
));
}
else
{
children
.
add
(
new
ScaleTransition
(
scale:
_currentScaleAnimation
,
child:
new
RotationTransition
(
turns:
_
previous
RotationAnimation
,
child:
_previousC
hild
,
turns:
_
current
RotationAnimation
,
child:
widget
.
c
hild
,
),
));
}
children
.
add
(
new
ScaleTransition
(
scale:
_currentScaleAnimation
,
child:
new
RotationTransition
(
turns:
_currentRotationAnimation
,
child:
widget
.
child
,
),
));
return
new
Stack
(
children:
children
);
return
new
Stack
(
alignment:
Alignment
.
centerRight
,
children:
children
,
);
}
void
_onProgressChanged
()
{
...
...
@@ -689,10 +731,10 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
/// of an app using the [bottomNavigationBar] property.
/// * [FloatingActionButton], which is a circular button typically shown in the
/// 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.
/// * [FloatingActionButtonAnimator], which is used to animate the
/// [floatingActionButton] from one [floatingActionButtonLocation] to
/// [floatingActionButton] from one [floatingActionButtonLocation] to
/// another.
/// * [Drawer], which is a vertical panel that is typically displayed to the
/// left of the body (and often hidden on phones) using the [drawer]
...
...
@@ -753,12 +795,12 @@ class Scaffold extends StatefulWidget {
final
Widget
floatingActionButton
;
/// Responsible for determining where the [floatingActionButton] should go.
///
///
/// If null, the [ScaffoldState] will use the default location, [FloatingActionButtonLocation.endFloat].
final
FloatingActionButtonLocation
floatingActionButtonLocation
;
/// Animator to move the [floatingActionButton] to a new [floatingActionButtonLocation].
///
///
/// If null, the [ScaffoldState] will use the default animator, [FloatingActionButtonAnimator.scaling].
final
FloatingActionButtonAnimator
floatingActionButtonAnimator
;
...
...
@@ -1307,10 +1349,10 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
_floatingActionButtonAnimator
=
widget
.
floatingActionButtonAnimator
??
_kDefaultFloatingActionButtonAnimator
;
_previousFloatingActionButtonLocation
=
_floatingActionButtonLocation
;
_floatingActionButtonMoveController
=
new
AnimationController
(
vsync:
this
,
lowerBound:
0.0
,
upperBound:
1.0
,
value:
1.0
,
vsync:
this
,
lowerBound:
0.0
,
upperBound:
1.0
,
value:
1.0
,
duration:
kFloatingActionButtonSegue
*
2
,
);
}
...
...
@@ -1567,7 +1609,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
final
EdgeInsets
minInsets
=
mediaQuery
.
padding
.
copyWith
(
bottom:
widget
.
resizeToAvoidBottomPadding
?
mediaQuery
.
viewInsets
.
bottom
:
0.0
,
);
return
new
_ScaffoldScope
(
hasDrawer:
hasDrawer
,
geometryNotifier:
_geometryNotifier
,
...
...
packages/flutter/test/material/floating_action_button_test.dart
View file @
3a93061e
...
...
@@ -65,10 +65,59 @@ void main() {
expect
(
find
.
byType
(
Text
),
findsNothing
);
await
tester
.
longPress
(
find
.
byType
(
FloatingActionButton
));
await
tester
.
pump
();
await
tester
.
pump
AndSettle
();
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
{
BuildContext
theContext
;
await
tester
.
pumpWidget
(
...
...
@@ -372,7 +421,7 @@ class GeometryListenerState extends State<GeometryListener> {
final
ValueListenable
<
ScaffoldGeometry
>
newListenable
=
Scaffold
.
geometryOf
(
context
);
if
(
geometryListenable
==
newListenable
)
return
;
geometryListenable
=
newListenable
;
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