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
Expand all
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
This diff is collapsed.
Click to expand it.
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