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
1a0bdf41
Commit
1a0bdf41
authored
Dec 01, 2015
by
Ian Hickson
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #621 from Hixie/material-ink
Model ink splashes more physically
parents
366d078d
ee802bfe
Changes
23
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
923 additions
and
494 deletions
+923
-494
animated_value.dart
packages/flutter/lib/src/animation/animated_value.dart
+23
-10
colors.dart
packages/flutter/lib/src/material/colors.dart
+4
-2
drawer_item.dart
packages/flutter/lib/src/material/drawer_item.dart
+23
-21
flat_button.dart
packages/flutter/lib/src/material/flat_button.dart
+21
-11
floating_action_button.dart
...ages/flutter/lib/src/material/floating_action_button.dart
+15
-13
icon_button.dart
packages/flutter/lib/src/material/icon_button.dart
+2
-4
ink_well.dart
packages/flutter/lib/src/material/ink_well.dart
+130
-268
material.dart
packages/flutter/lib/src/material/material.dart
+391
-27
material_button.dart
packages/flutter/lib/src/material/material_button.dart
+39
-20
raised_button.dart
packages/flutter/lib/src/material/raised_button.dart
+33
-13
scaffold.dart
packages/flutter/lib/src/material/scaffold.dart
+4
-1
snack_bar.dart
packages/flutter/lib/src/material/snack_bar.dart
+24
-11
tabs.dart
packages/flutter/lib/src/material/tabs.dart
+12
-14
theme_data.dart
packages/flutter/lib/src/material/theme_data.dart
+10
-12
tool_bar.dart
packages/flutter/lib/src/material/tool_bar.dart
+46
-35
framework.dart
packages/flutter/lib/src/widgets/framework.dart
+42
-6
notification_listener.dart
packages/flutter/lib/src/widgets/notification_listener.dart
+55
-0
scrollable.dart
packages/flutter/lib/src/widgets/scrollable.dart
+10
-0
widgets.dart
packages/flutter/lib/widgets.dart
+1
-0
scheduler_test.dart
packages/unit/test/animation/scheduler_test.dart
+4
-0
date_picker_test.dart
packages/unit/test/widget/date_picker_test.dart
+12
-10
heroes_test.dart
packages/unit/test/widget/heroes_test.dart
+16
-12
tabs_test.dart
packages/unit/test/widget/tabs_test.dart
+6
-4
No files found.
packages/flutter/lib/src/animation/animated_value.dart
View file @
1a0bdf41
...
...
@@ -58,26 +58,27 @@ class AnimationTiming {
}
}
/// An animated variable with a concrete type
/// An animated variable with a concrete type
.
class
AnimatedValue
<
T
extends
dynamic
>
extends
AnimationTiming
implements
Animatable
{
AnimatedValue
(
this
.
begin
,
{
this
.
end
,
Curve
curve
,
Curve
reverseCurve
})
:
super
(
curve:
curve
,
reverseCurve:
reverseCurve
)
{
value
=
begin
;
}
/// The current value of this variable
/// The current value of this variable
.
T
value
;
/// The value this variable has at the beginning of the animation
/// The value this variable has at the beginning of the animation
.
T
begin
;
/// The value this variable has at the end of the animation
/// The value this variable has at the end of the animation
.
T
end
;
/// Returns the value this variable has at the given animation clock value
/// Returns the value this variable has at the given animation clock value
.
T
lerp
(
double
t
)
=>
begin
+
(
end
-
begin
)
*
t
;
/// Updates the value of this variable according to the given animation clock value and direction
/// Updates the value of this variable according to the given animation clock
/// value and direction.
void
setProgress
(
double
t
,
AnimationDirection
direction
)
{
if
(
end
!=
null
)
{
t
=
transform
(
t
,
direction
);
...
...
@@ -93,7 +94,7 @@ class AnimatedValue<T extends dynamic> extends AnimationTiming implements Animat
String
toString
()
=>
'AnimatedValue(begin=
$begin
, end=
$end
, value=
$value
)'
;
}
/// An animated variable containing a color
/// An animated variable containing a color
.
///
/// This class specializes the interpolation of AnimatedValue<Color> to be
/// appropriate for colors.
...
...
@@ -104,9 +105,9 @@ class AnimatedColorValue extends AnimatedValue<Color> {
Color
lerp
(
double
t
)
=>
Color
.
lerp
(
begin
,
end
,
t
);
}
/// An animated variable containing a
rectangle
/// An animated variable containing a
size.
///
/// This class specializes the interpolation of AnimatedValue<
Rect
> to be
/// This class specializes the interpolation of AnimatedValue<
Size
> to be
/// appropriate for rectangles.
class
AnimatedSizeValue
extends
AnimatedValue
<
Size
>
{
AnimatedSizeValue
(
Size
begin
,
{
Size
end
,
Curve
curve
,
Curve
reverseCurve
})
...
...
@@ -115,7 +116,7 @@ class AnimatedSizeValue extends AnimatedValue<Size> {
Size
lerp
(
double
t
)
=>
Size
.
lerp
(
begin
,
end
,
t
);
}
/// An animated variable containing a rectangle
/// An animated variable containing a rectangle
.
///
/// This class specializes the interpolation of AnimatedValue<Rect> to be
/// appropriate for rectangles.
...
...
@@ -125,3 +126,15 @@ class AnimatedRectValue extends AnimatedValue<Rect> {
Rect
lerp
(
double
t
)
=>
Rect
.
lerp
(
begin
,
end
,
t
);
}
/// An animated variable containing a int.
///
/// The inherited lerp() function doesn't work with ints because it multiplies
/// the begin and end types by a double, and int * double returns a double.
/// This class overrides the lerp() function to round off the result to an int.
class
AnimatedIntValue
extends
AnimatedValue
<
int
>
{
AnimatedIntValue
(
int
begin
,
{
int
end
,
Curve
curve
,
Curve
reverseCurve
})
:
super
(
begin
,
end:
end
,
curve:
curve
,
reverseCurve:
reverseCurve
);
int
lerp
(
double
t
)
=>
(
begin
+
(
end
-
begin
)
*
t
).
round
();
}
packages/flutter/lib/src/material/colors.dart
View file @
1a0bdf41
...
...
@@ -14,11 +14,13 @@ class Colors {
static
const
black
=
const
Color
(
0xFF000000
);
static
const
black87
=
const
Color
(
0xDD000000
);
static
const
black54
=
const
Color
(
0x8A000000
);
static
const
black26
=
const
Color
(
0x42000000
);
// disabled radio buttons and text of disabled flat buttons in light theme (26% black)
static
const
black45
=
const
Color
(
0x73000000
);
// mask color
static
const
black26
=
const
Color
(
0x42000000
);
// disabled radio buttons and text of disabled flat buttons in light theme
static
const
black12
=
const
Color
(
0x1F000000
);
// background of disabled raised buttons in light theme
static
const
white
=
const
Color
(
0xFFFFFFFF
);
static
const
white70
=
const
Color
(
0xB3FFFFFF
);
static
const
white30
=
const
Color
(
0x4DFFFFFF
);
// disabled radio buttons and text of disabled flat buttons in dark theme
(30% white)
static
const
white30
=
const
Color
(
0x4DFFFFFF
);
// disabled radio buttons and text of disabled flat buttons in dark theme
static
const
white12
=
const
Color
(
0x1FFFFFFF
);
// background of disabled raised buttons in dark theme
static
const
white10
=
const
Color
(
0x1AFFFFFF
);
...
...
packages/flutter/lib/src/material/drawer_item.dart
View file @
1a0bdf41
...
...
@@ -10,35 +10,39 @@ import 'ink_well.dart';
import
'theme.dart'
;
class
DrawerItem
extends
StatelessComponent
{
const
DrawerItem
({
Key
key
,
this
.
icon
,
this
.
child
,
this
.
onPressed
,
this
.
selected
:
false
})
:
super
(
key:
key
);
const
DrawerItem
({
Key
key
,
this
.
icon
,
this
.
child
,
this
.
onPressed
,
this
.
selected
:
false
})
:
super
(
key:
key
);
final
String
icon
;
final
Widget
child
;
final
VoidCallback
onPressed
;
final
bool
selected
;
ColorFilter
_getIconColorFilter
(
ThemeData
themeData
)
{
if
(
selected
)
{
if
(
themeData
.
brightness
==
ThemeBrightness
.
dark
)
return
new
ColorFilter
.
mode
(
themeData
.
accentColor
,
TransferMode
.
srcATop
);
return
new
ColorFilter
.
mode
(
themeData
.
primaryColor
,
TransferMode
.
srcATop
);
}
return
new
ColorFilter
.
mode
(
Colors
.
black45
,
TransferMode
.
dstIn
);
}
TextStyle
_getTextStyle
(
ThemeData
themeData
)
{
TextStyle
result
=
themeData
.
text
.
body2
;
if
(
selected
)
result
=
result
.
copyWith
(
color:
themeData
.
primaryColor
);
if
(
selected
)
{
if
(
themeData
.
brightness
==
ThemeBrightness
.
dark
)
result
=
result
.
copyWith
(
color:
themeData
.
accentColor
);
else
result
=
result
.
copyWith
(
color:
themeData
.
primaryColor
);
}
return
result
;
}
Color
_getBackgroundColor
(
ThemeData
themeData
,
{
bool
highlight
})
{
if
(
highlight
)
return
themeData
.
highlightColor
;
if
(
selected
)
return
themeData
.
selectedColor
;
return
Colors
.
transparent
;
}
ColorFilter
_getColorFilter
(
ThemeData
themeData
)
{
if
(
selected
)
return
new
ColorFilter
.
mode
(
themeData
.
primaryColor
,
TransferMode
.
srcATop
);
return
new
ColorFilter
.
mode
(
const
Color
(
0x73000000
),
TransferMode
.
dstIn
);
}
Widget
build
(
BuildContext
context
)
{
ThemeData
themeData
=
Theme
.
of
(
context
);
...
...
@@ -49,7 +53,7 @@ class DrawerItem extends StatelessComponent {
padding:
const
EdgeDims
.
symmetric
(
horizontal:
16.0
),
child:
new
Icon
(
icon:
icon
,
colorFilter:
_getColorFilter
(
themeData
)
colorFilter:
_get
Icon
ColorFilter
(
themeData
)
)
)
);
...
...
@@ -70,8 +74,6 @@ class DrawerItem extends StatelessComponent {
height:
48.0
,
child:
new
InkWell
(
onTap:
onPressed
,
defaultColor:
_getBackgroundColor
(
themeData
,
highlight:
false
),
highlightColor:
_getBackgroundColor
(
themeData
,
highlight:
true
),
child:
new
Row
(
flexChildren
)
)
);
...
...
packages/flutter/lib/src/material/flat_button.dart
View file @
1a0bdf41
...
...
@@ -4,7 +4,6 @@
import
'package:flutter/widgets.dart'
;
import
'colors.dart'
;
import
'material_button.dart'
;
import
'theme.dart'
;
...
...
@@ -12,11 +11,27 @@ class FlatButton extends MaterialButton {
FlatButton
({
Key
key
,
Widget
child
,
ButtonColor
textTheme
,
Color
textColor
,
Color
disabledTextColor
,
this
.
color
,
this
.
colorBrightness
,
this
.
disabledColor
,
VoidCallback
onPressed
})
:
super
(
key:
key
,
child:
child
,
textTheme:
textTheme
,
textColor:
textColor
,
disabledTextColor:
disabledTextColor
,
onPressed:
onPressed
);
// These default to null, meaning transparent.
final
Color
color
;
final
Color
disabledColor
;
/// Controls the default text color if the text color isn't explicit set.
final
ThemeBrightness
colorBrightness
;
_FlatButtonState
createState
()
=>
new
_FlatButtonState
();
}
...
...
@@ -24,19 +39,14 @@ class _FlatButtonState extends MaterialButtonState<FlatButton> {
int
get
elevation
=>
0
;
Color
getColor
(
BuildContext
context
,
{
bool
highlight
})
{
if
(!
config
.
enabled
||
!
highlight
)
return
null
;
switch
(
Theme
.
of
(
context
).
brightness
)
{
case
ThemeBrightness
.
light
:
return
Colors
.
grey
[
400
];
case
ThemeBrightness
.
dark
:
return
Colors
.
grey
[
200
];
}
Color
getColor
(
BuildContext
context
)
{
if
(!
config
.
enabled
)
return
config
.
disabledColor
;
return
config
.
color
;
}
ThemeBrightness
getColorBrightness
(
BuildContext
context
)
{
return
Theme
.
of
(
context
).
brightness
;
return
config
.
colorBrightness
??
Theme
.
of
(
context
).
brightness
;
}
}
packages/flutter/lib/src/material/floating_action_button.dart
View file @
1a0bdf41
...
...
@@ -19,12 +19,16 @@ class FloatingActionButton extends StatefulComponent {
Key
key
,
this
.
child
,
this
.
backgroundColor
,
this
.
elevation
:
6
,
this
.
highlightElevation
:
12
,
this
.
onPressed
})
:
super
(
key:
key
);
final
Widget
child
;
final
Color
backgroundColor
;
final
VoidCallback
onPressed
;
final
int
elevation
;
final
int
highlightElevation
;
_FloatingActionButtonState
createState
()
=>
new
_FloatingActionButtonState
();
}
...
...
@@ -50,19 +54,17 @@ class _FloatingActionButtonState extends State<FloatingActionButton> {
return
new
Material
(
color:
materialColor
,
type:
MaterialType
.
circle
,
elevation:
_highlight
?
12
:
6
,
child:
new
ClipOval
(
child:
new
Container
(
width:
_kSize
,
height:
_kSize
,
child:
new
InkWell
(
onTap:
config
.
onPressed
,
onHighlightChanged:
_handleHighlightChanged
,
child:
new
Center
(
child:
new
IconTheme
(
data:
new
IconThemeData
(
color:
iconThemeColor
),
child:
config
.
child
)
elevation:
_highlight
?
config
.
highlightElevation
:
config
.
elevation
,
child:
new
Container
(
width:
_kSize
,
height:
_kSize
,
child:
new
InkWell
(
onTap:
config
.
onPressed
,
onHighlightChanged:
_handleHighlightChanged
,
child:
new
Center
(
child:
new
IconTheme
(
data:
new
IconThemeData
(
color:
iconThemeColor
),
child:
config
.
child
)
)
)
...
...
packages/flutter/lib/src/material/icon_button.dart
View file @
1a0bdf41
...
...
@@ -6,6 +6,7 @@ import 'package:flutter/widgets.dart';
import
'icon.dart'
;
import
'icon_theme_data.dart'
;
import
'ink_well.dart'
;
class
IconButton
extends
StatelessComponent
{
const
IconButton
({
...
...
@@ -22,11 +23,8 @@ class IconButton extends StatelessComponent {
final
VoidCallback
onPressed
;
Widget
build
(
BuildContext
context
)
{
// TODO(abarth): We should use a radial reaction here so you can hit the
// 8.0 pixel padding as well as the icon.
return
new
GestureDetector
(
return
new
InkResponse
(
onTap:
onPressed
,
behavior:
HitTestBehavior
.
opaque
,
child:
new
Padding
(
padding:
const
EdgeDims
.
all
(
8.0
),
child:
new
Icon
(
...
...
packages/flutter/lib/src/material/ink_well.dart
View file @
1a0bdf41
...
...
@@ -2,331 +2,193 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:math'
as
math
;
import
'package:flutter/animation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
// This file has the following classes:
// InkWell - the widget for material-design-style inkly-reacting material, showing splashes and a highlight
// _InkWellState - InkWell's State class
// _InkSplash - tracks a single splash
// _RenderInkSplashes - a RenderBox that renders multiple _InkSplash objects and handles gesture recognition
// _InkSplashes - the RenderObjectWidget for _RenderInkSplashes used by InkWell to handle the splashes
const
int
_kSplashInitialOpacity
=
0x30
;
// 0..255
const
double
_kSplashCanceledVelocity
=
0.7
;
// logical pixels per millisecond
const
double
_kSplashConfirmedVelocity
=
0.7
;
// logical pixels per millisecond
const
double
_kSplashInitialSize
=
0.0
;
// logical pixels
const
double
_kSplashUnconfirmedVelocity
=
0.2
;
// logical pixels per millisecond
const
Duration
_kInkWellHighlightFadeDuration
=
const
Duration
(
milliseconds:
100
);
import
'material.dart'
;
import
'theme.dart'
;
class
Ink
Well
extends
StatefulComponent
{
Ink
Well
({
class
Ink
Response
extends
StatefulComponent
{
Ink
Response
({
Key
key
,
this
.
child
,
this
.
onTap
,
this
.
onDoubleTap
,
this
.
onLongPress
,
this
.
onHighlightChanged
,
this
.
defaultColor
,
this
.
highlightColor
this
.
onHighlightChanged
})
:
super
(
key:
key
);
final
Widget
child
;
final
GestureTapCallback
onTap
;
final
GestureTapCallback
onDoubleTap
;
final
GestureLongPressCallback
onLongPress
;
final
_HighlightChangedCallback
onHighlightChanged
;
final
Color
defaultColor
;
final
Color
highlightColor
;
final
ValueChanged
<
bool
>
onHighlightChanged
;
_Ink
WellState
createState
()
=>
new
_InkWellState
();
_Ink
ResponseState
createState
()
=>
new
_InkResponseState
<
InkResponse
>
();
}
class
_InkWellState
extends
State
<
InkWell
>
{
bool
_highlight
=
false
;
Widget
build
(
BuildContext
context
)
{
return
new
AnimatedContainer
(
decoration:
new
BoxDecoration
(
backgroundColor:
_highlight
?
config
.
highlightColor
:
config
.
defaultColor
),
duration:
_kInkWellHighlightFadeDuration
,
child:
new
_InkSplashes
(
onTap:
config
.
onTap
,
onDoubleTap:
config
.
onDoubleTap
,
onLongPress:
config
.
onLongPress
,
onHighlightChanged:
(
bool
value
)
{
setState
(()
{
_highlight
=
value
;
});
if
(
config
.
onHighlightChanged
!=
null
)
config
.
onHighlightChanged
(
value
);
},
child:
config
.
child
)
);
}
}
class
_InkResponseState
<
T
extends
InkResponse
>
extends
State
<
T
>
{
bool
get
containedInWell
=>
false
;
double
_getSplashTargetSize
(
Size
bounds
,
Point
position
)
{
double
d1
=
(
position
-
bounds
.
topLeft
(
Point
.
origin
)).
distance
;
double
d2
=
(
position
-
bounds
.
topRight
(
Point
.
origin
)).
distance
;
double
d3
=
(
position
-
bounds
.
bottomLeft
(
Point
.
origin
)).
distance
;
double
d4
=
(
position
-
bounds
.
bottomRight
(
Point
.
origin
)).
distance
;
return
math
.
max
(
math
.
max
(
d1
,
d2
),
math
.
max
(
d3
,
d4
)).
ceil
().
toDouble
();
}
Set
<
InkSplash
>
_splashes
;
InkSplash
_currentSplash
;
class
_InkSplash
{
_InkSplash
(
this
.
position
,
this
.
renderer
)
{
_targetRadius
=
_getSplashTargetSize
(
renderer
.
size
,
position
);
_radius
=
new
ValuePerformance
<
double
>(
variable:
new
AnimatedValue
<
double
>(
_kSplashInitialSize
,
end:
_targetRadius
,
curve:
Curves
.
easeOut
),
duration:
new
Duration
(
milliseconds:
(
_targetRadius
/
_kSplashUnconfirmedVelocity
).
floor
())
)..
addListener
(
_handleRadiusChange
)
..
play
();
void
_handleTapDown
(
Point
position
)
{
RenderBox
referenceBox
=
context
.
findRenderObject
();
assert
(
Material
.
of
(
context
)
!=
null
);
InkSplash
splash
;
splash
=
Material
.
of
(
context
).
splashAt
(
referenceBox:
referenceBox
,
position:
referenceBox
.
globalToLocal
(
position
),
containedInWell:
containedInWell
,
onRemoved:
()
{
if
(
_splashes
!=
null
)
{
assert
(
_splashes
.
contains
(
splash
));
_splashes
.
remove
(
splash
);
if
(
_currentSplash
==
splash
)
_currentSplash
=
null
;
}
// else we're probably in deactivate()
}
);
_splashes
??=
new
Set
<
InkSplash
>();
_splashes
.
add
(
splash
);
_currentSplash
=
splash
;
}
final
Point
position
;
final
_RenderInkSplashes
renderer
;
double
_targetRadius
;
double
_pinnedRadius
;
ValuePerformance
<
double
>
_radius
;
void
_updateVelocity
(
double
velocity
)
{
int
duration
=
(
_targetRadius
/
velocity
).
floor
();
_radius
.
duration
=
new
Duration
(
milliseconds:
duration
);
_radius
.
play
();
void
_handleTap
()
{
_currentSplash
?.
confirm
();
_currentSplash
=
null
;
if
(
config
.
onTap
!=
null
)
config
.
onTap
();
}
void
confirm
()
{
_
updateVelocity
(
_kSplashConfirmedVelocity
);
_
pinnedRadius
=
null
;
void
_handleTapCancel
()
{
_
currentSplash
?.
cancel
(
);
_
currentSplash
=
null
;
}
void
cancel
()
{
_updateVelocity
(
_kSplashCanceledVelocity
);
_pinnedRadius
=
_radius
.
value
;
void
_handleDoubleTap
()
{
_currentSplash
?.
confirm
();
_currentSplash
=
null
;
if
(
config
.
onDoubleTap
!=
null
)
config
.
onDoubleTap
();
}
void
_handleRadiusChange
()
{
if
(
_radius
.
value
==
_targetRadius
)
renderer
.
_removeSplash
(
this
);
renderer
.
markNeedsPaint
();
void
_handleLongPress
()
{
_currentSplash
?.
confirm
();
_currentSplash
=
null
;
if
(
config
.
onLongPress
!=
null
)
config
.
onLongPress
();
}
void
deactivate
()
{
if
(
_splashes
!=
null
)
{
Set
<
InkSplash
>
splashes
=
_splashes
;
_splashes
=
null
;
for
(
InkSplash
splash
in
splashes
)
splash
.
dispose
();
_currentSplash
=
null
;
}
assert
(
_currentSplash
==
null
);
super
.
deactivate
();
}
void
paint
(
PaintingCanvas
canvas
)
{
int
opacity
=
(
_kSplashInitialOpacity
*
(
1.1
-
(
_radius
.
value
/
_targetRadius
))).
floor
();
Paint
paint
=
new
Paint
()..
color
=
new
Color
(
opacity
<<
24
);
double
radius
=
_pinnedRadius
==
null
?
_radius
.
value
:
_pinnedRadius
;
canvas
.
drawCircle
(
position
,
radius
,
paint
);
Widget
build
(
BuildContext
context
)
{
final
bool
enabled
=
config
.
onTap
!=
null
||
config
.
onDoubleTap
!=
null
||
config
.
onLongPress
!=
null
;
return
new
GestureDetector
(
onTapDown:
enabled
?
_handleTapDown
:
null
,
onTap:
enabled
?
_handleTap
:
null
,
onTapCancel:
enabled
?
_handleTapCancel
:
null
,
onDoubleTap:
config
.
onDoubleTap
!=
null
?
_handleDoubleTap
:
null
,
onLongPress:
config
.
onLongPress
!=
null
?
_handleLongPress
:
null
,
behavior:
HitTestBehavior
.
opaque
,
child:
config
.
child
);
}
}
typedef
_HighlightChangedCallback
(
bool
value
);
}
class
_RenderInkSplashes
extends
RenderProxyBox
{
_RenderInkSplashes
({
RenderBox
child
,
/// An area of a Material that responds to touch.
///
/// Must have an ancestor Material widget in which to cause ink reactions.
class
InkWell
extends
InkResponse
{
InkWell
({
Key
key
,
Widget
child
,
GestureTapCallback
onTap
,
GestureTapCallback
onDoubleTap
,
GestureLongPressCallback
onLongPress
,
this
.
onHighlightChanged
})
:
super
(
child
)
{
this
.
onTap
=
onTap
;
this
.
onDoubleTap
=
onDoubleTap
;
this
.
onLongPress
=
onLongPress
;
}
GestureTapCallback
get
onTap
=>
_onTap
;
GestureTapCallback
_onTap
;
void
set
onTap
(
GestureTapCallback
value
)
{
_onTap
=
value
;
_syncTapRecognizer
();
}
GestureTapCallback
get
onDoubleTap
=>
_onDoubleTap
;
GestureTapCallback
_onDoubleTap
;
void
set
onDoubleTap
(
GestureTapCallback
value
)
{
_onDoubleTap
=
value
;
_syncDoubleTapRecognizer
();
}
GestureTapCallback
get
onLongPress
=>
_onLongPress
;
GestureTapCallback
_onLongPress
;
void
set
onLongPress
(
GestureTapCallback
value
)
{
_onLongPress
=
value
;
_syncLongPressRecognizer
();
}
_HighlightChangedCallback
onHighlightChanged
;
final
List
<
_InkSplash
>
_splashes
=
new
List
<
_InkSplash
>();
_InkSplash
_lastSplash
;
TapGestureRecognizer
_tap
;
DoubleTapGestureRecognizer
_doubleTap
;
LongPressGestureRecognizer
_longPress
;
void
_removeSplash
(
_InkSplash
splash
)
{
_splashes
.
remove
(
splash
);
if
(
_lastSplash
==
splash
)
_lastSplash
=
null
;
}
void
handleEvent
(
InputEvent
event
,
BoxHitTestEntry
entry
)
{
if
(
event
.
type
==
'pointerdown'
&&
(
onTap
!=
null
||
onDoubleTap
!=
null
||
onLongPress
!=
null
))
{
_tap
?.
addPointer
(
event
);
_doubleTap
?.
addPointer
(
event
);
_longPress
?.
addPointer
(
event
);
}
}
void
attach
()
{
super
.
attach
();
_syncTapRecognizer
();
_syncDoubleTapRecognizer
();
_syncLongPressRecognizer
();
}
void
detach
()
{
_disposeTapRecognizer
();
_disposeDoubleTapRecognizer
();
_disposeLongPressRecognizer
();
super
.
detach
();
}
void
_syncTapRecognizer
()
{
if
(
onTap
==
null
&&
onDoubleTap
==
null
&&
onLongPress
==
null
)
{
_disposeTapRecognizer
();
}
else
{
_tap
??=
new
TapGestureRecognizer
(
router:
FlutterBinding
.
instance
.
pointerRouter
)
..
onTapDown
=
_handleTapDown
..
onTap
=
_handleTap
..
onTapCancel
=
_handleTapCancel
;
}
}
})
:
super
(
key:
key
,
child:
child
,
onTap:
onTap
,
onDoubleTap:
onDoubleTap
,
onLongPress:
onLongPress
);
void
_disposeTapRecognizer
()
{
_tap
?.
dispose
();
_tap
=
null
;
}
final
ValueChanged
<
bool
>
onHighlightChanged
;
void
_syncDoubleTapRecognizer
()
{
if
(
onDoubleTap
==
null
)
{
_disposeDoubleTapRecognizer
();
}
else
{
_doubleTap
??=
new
DoubleTapGestureRecognizer
(
router:
FlutterBinding
.
instance
.
pointerRouter
)
..
onDoubleTap
=
_handleDoubleTap
;
}
}
void
_disposeDoubleTapRecognizer
()
{
_doubleTap
?.
dispose
();
_doubleTap
=
null
;
}
_InkWellState
createState
()
=>
new
_InkWellState
();
}
void
_syncLongPressRecognizer
()
{
if
(
onLongPress
==
null
)
{
_disposeLongPressRecognizer
();
class
_InkWellState
extends
_InkResponseState
<
InkWell
>
{
bool
get
containedInWell
=>
true
;
InkHighlight
_lastHighlight
;
void
updateHighlight
(
bool
value
)
{
if
(
value
==
(
_lastHighlight
!=
null
&&
_lastHighlight
.
active
))
return
;
if
(
value
)
{
if
(
_lastHighlight
==
null
)
{
RenderBox
referenceBox
=
context
.
findRenderObject
();
assert
(
Material
.
of
(
context
)
!=
null
);
_lastHighlight
=
Material
.
of
(
context
).
highlightRectAt
(
referenceBox:
referenceBox
,
color:
Theme
.
of
(
context
).
highlightColor
,
onRemoved:
()
{
assert
(
_lastHighlight
!=
null
);
_lastHighlight
=
null
;
}
);
}
else
{
_lastHighlight
.
activate
();
}
}
else
{
_longPress
??=
new
LongPressGestureRecognizer
(
router:
FlutterBinding
.
instance
.
pointerRouter
)
..
onLongPress
=
_handleLongPress
;
_lastHighlight
.
deactivate
();
}
}
void
_disposeLongPressRecognizer
()
{
_longPress
?.
dispose
();
_longPress
=
null
;
if
(
config
.
onHighlightChanged
!=
null
)
config
.
onHighlightChanged
(
value
!=
null
);
}
void
_handleTapDown
(
Point
position
)
{
_lastSplash
=
new
_InkSplash
(
globalToLocal
(
position
),
this
);
_splashes
.
add
(
_lastSplash
);
if
(
onHighlightChanged
!=
null
)
onHighlightChanged
(
true
);
super
.
_handleTapDown
(
position
);
updateHighlight
(
true
);
}
void
_handleTap
()
{
_lastSplash
?.
confirm
();
_lastSplash
=
null
;
if
(
onHighlightChanged
!=
null
)
onHighlightChanged
(
false
);
if
(
onTap
!=
null
)
onTap
();
super
.
_handleTap
();
updateHighlight
(
false
);
}
void
_handleTapCancel
()
{
_lastSplash
?.
cancel
();
_lastSplash
=
null
;
if
(
onHighlightChanged
!=
null
)
onHighlightChanged
(
false
);
}
void
_handleDoubleTap
()
{
_lastSplash
?.
confirm
();
_lastSplash
=
null
;
if
(
onDoubleTap
!=
null
)
onDoubleTap
();
super
.
_handleTapCancel
();
updateHighlight
(
false
);
}
void
_handleLongPress
()
{
_lastSplash
?.
confirm
();
_lastSplash
=
null
;
if
(
onLongPress
!=
null
)
onLongPress
();
void
deactivate
()
{
_lastHighlight
?.
dispose
();
_lastHighlight
=
null
;
super
.
deactivate
();
}
bool
hitTestSelf
(
Point
position
)
=>
true
;
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(!
_splashes
.
isEmpty
)
{
final
PaintingCanvas
canvas
=
context
.
canvas
;
canvas
.
save
();
canvas
.
translate
(
offset
.
dx
,
offset
.
dy
);
canvas
.
clipRect
(
Point
.
origin
&
size
);
for
(
_InkSplash
splash
in
_splashes
)
splash
.
paint
(
canvas
);
canvas
.
restore
();
}
super
.
paint
(
context
,
offset
);
void
dependenciesChanged
(
Type
affectedWidgetType
)
{
if
(
affectedWidgetType
==
Theme
&&
_lastHighlight
!=
null
)
_lastHighlight
.
color
=
Theme
.
of
(
context
).
highlightColor
;
}
}
class
_InkSplashes
extends
OneChildRenderObjectWidget
{
_InkSplashes
({
Key
key
,
Widget
child
,
this
.
onTap
,
this
.
onDoubleTap
,
this
.
onLongPress
,
this
.
onHighlightChanged
})
:
super
(
key:
key
,
child:
child
);
final
GestureTapCallback
onTap
;
final
GestureTapCallback
onDoubleTap
;
final
GestureLongPressCallback
onLongPress
;
final
_HighlightChangedCallback
onHighlightChanged
;
_RenderInkSplashes
createRenderObject
()
=>
new
_RenderInkSplashes
(
onTap:
onTap
,
onDoubleTap:
onDoubleTap
,
onLongPress:
onLongPress
,
onHighlightChanged:
onHighlightChanged
);
void
updateRenderObject
(
_RenderInkSplashes
renderObject
,
_InkSplashes
oldWidget
)
{
renderObject
.
onTap
=
onTap
;
renderObject
.
onDoubleTap
=
onDoubleTap
;
renderObject
.
onLongPress
=
onLongPress
;
renderObject
.
onHighlightChanged
=
onHighlightChanged
;
}
}
packages/flutter/lib/src/material/material.dart
View file @
1a0bdf41
...
...
@@ -2,23 +2,76 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:math'
as
math
;
import
'package:flutter/animation.dart'
;
import
'package:flutter/painting.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
import
'constants.dart'
;
import
'shadows.dart'
;
import
'theme.dart'
;
enum
MaterialType
{
canvas
,
card
,
circle
,
button
}
enum
MaterialType
{
/// Infinite extent using default theme canvas color.
canvas
,
/// Rounded edges, card theme color.
card
,
const
Map
<
MaterialType
,
double
>
_kEdges
=
const
<
MaterialType
,
double
>{
/// A circle, no color by default (used for floating action buttons).
circle
,
/// Rounded edges, no color by default (used for MaterialButton buttons).
button
}
const
Map
<
MaterialType
,
double
>
kMaterialEdges
=
const
<
MaterialType
,
double
>{
MaterialType
.
canvas
:
null
,
MaterialType
.
card
:
2.0
,
MaterialType
.
circle
:
null
,
MaterialType
.
button
:
2.0
,
};
class
Material
extends
StatelessComponent
{
abstract
class
InkSplash
{
void
confirm
();
void
cancel
();
void
dispose
();
}
abstract
class
InkHighlight
{
void
activate
();
void
deactivate
();
void
dispose
();
bool
get
active
;
Color
get
color
;
void
set
color
(
Color
value
);
}
abstract
class
MaterialInkController
{
/// The color of the material
Color
get
color
;
/// Begin a splash, centered at position relative to referenceBox.
/// If containedInWell is true, then the splash will be sized to fit
/// the referenceBox, then clipped to it when drawn.
/// When the splash is removed, onRemoved will be invoked.
InkSplash
splashAt
({
RenderBox
referenceBox
,
Point
position
,
bool
containedInWell
,
VoidCallback
onRemoved
});
/// Begin a highlight, coincident with the referenceBox.
InkHighlight
highlightRectAt
({
RenderBox
referenceBox
,
Color
color
,
VoidCallback
onRemoved
});
/// Add an arbitrary InkFeature to this InkController.
void
addInkFeature
(
InkFeature
feature
);
}
/// Describes a sheet of Material. If the layout changes (e.g. because there's a
/// list on the paper, and it's been scrolled), a LayoutChangedNotification must
/// be dispatched at the relevant subtree. (This in particular means that
/// Transitions should not be placed inside Material.)
class
Material
extends
StatefulComponent
{
Material
({
Key
key
,
this
.
child
,
...
...
@@ -36,10 +89,21 @@ class Material extends StatelessComponent {
final
Color
color
;
final
TextStyle
textStyle
;
static
MaterialInkController
of
(
BuildContext
context
)
{
final
RenderInkFeatures
result
=
context
.
ancestorRenderObjectOfType
(
RenderInkFeatures
);
return
result
;
}
_MaterialState
createState
()
=>
new
_MaterialState
();
}
class
_MaterialState
extends
State
<
Material
>
{
final
GlobalKey
_inkFeatureRenderer
=
new
GlobalKey
(
debugLabel:
'ink renderer'
);
Color
_getBackgroundColor
(
BuildContext
context
)
{
if
(
color
!=
null
)
return
color
;
switch
(
type
)
{
if
(
co
nfig
.
co
lor
!=
null
)
return
co
nfig
.
co
lor
;
switch
(
config
.
type
)
{
case
MaterialType
.
canvas
:
return
Theme
.
of
(
context
).
canvasColor
;
case
MaterialType
.
card
:
...
...
@@ -50,33 +114,333 @@ class Material extends StatelessComponent {
}
Widget
build
(
BuildContext
context
)
{
Widget
contents
=
child
;
if
(
child
!=
null
)
{
Color
backgroundColor
=
_getBackgroundColor
(
context
);
Widget
contents
=
config
.
child
;
if
(
contents
!=
null
)
{
contents
=
new
DefaultTextStyle
(
style:
textStyle
??
Theme
.
of
(
context
).
text
.
body1
,
style:
config
.
textStyle
??
Theme
.
of
(
context
).
text
.
body1
,
child:
contents
);
if
(
_kEdges
[
type
]
!=
null
)
{
contents
=
new
ClipRRect
(
xRadius:
_kEdges
[
type
],
yRadius:
_kEdges
[
type
],
child:
contents
);
}
}
return
new
DefaultTextStyle
(
style:
Theme
.
of
(
context
).
text
.
body1
,
child:
new
AnimatedContainer
(
curve:
Curves
.
ease
,
duration:
kThemeChangeDuration
,
decoration:
new
BoxDecoration
(
backgroundColor:
_getBackgroundColor
(
context
),
borderRadius:
_kEdges
[
type
],
boxShadow:
elevation
==
0
?
null
:
elevationToShadow
[
elevation
],
shape:
type
==
MaterialType
.
circle
?
Shape
.
circle
:
Shape
.
rectangle
),
contents
=
new
NotificationListener
<
LayoutChangedNotification
>(
onNotification:
(
LayoutChangedNotification
notification
)
{
_inkFeatureRenderer
.
currentContext
.
findRenderObject
().
markNeedsPaint
();
},
child:
new
InkFeatures
(
key:
_inkFeatureRenderer
,
color:
backgroundColor
,
child:
contents
)
);
if
(
config
.
type
==
MaterialType
.
circle
)
{
contents
=
new
ClipOval
(
child:
contents
);
}
else
if
(
kMaterialEdges
[
config
.
type
]
!=
null
)
{
contents
=
new
ClipRRect
(
xRadius:
kMaterialEdges
[
config
.
type
],
yRadius:
kMaterialEdges
[
config
.
type
],
child:
contents
);
}
contents
=
new
AnimatedContainer
(
curve:
Curves
.
ease
,
duration:
kThemeChangeDuration
,
decoration:
new
BoxDecoration
(
backgroundColor:
backgroundColor
,
borderRadius:
kMaterialEdges
[
config
.
type
],
boxShadow:
config
.
elevation
==
0
?
null
:
elevationToShadow
[
config
.
elevation
],
shape:
config
.
type
==
MaterialType
.
circle
?
Shape
.
circle
:
Shape
.
rectangle
),
child:
contents
);
return
contents
;
}
}
const
Duration
_kHighlightFadeDuration
=
const
Duration
(
milliseconds:
100
);
const
double
_kDefaultSplashRadius
=
35.0
;
// logical pixels
const
int
_kSplashInitialAlpha
=
0x30
;
// 0..255
const
double
_kSplashCanceledVelocity
=
0.7
;
// logical pixels per millisecond
const
double
_kSplashConfirmedVelocity
=
0.7
;
// logical pixels per millisecond
const
double
_kSplashInitialSize
=
0.0
;
// logical pixels
const
double
_kSplashUnconfirmedVelocity
=
0.2
;
// logical pixels per millisecond
class
RenderInkFeatures
extends
RenderProxyBox
implements
MaterialInkController
{
RenderInkFeatures
({
RenderBox
child
,
this
.
color
})
:
super
(
child
);
// This is here to satisfy the MaterialInkController contract.
// The actual painting of this color is done by a Container in the
// MaterialState build method.
Color
color
;
final
List
<
InkFeature
>
_inkFeatures
=
<
InkFeature
>[];
InkSplash
splashAt
({
RenderBox
referenceBox
,
Point
position
,
bool
containedInWell
,
VoidCallback
onRemoved
})
{
double
radius
;
if
(
containedInWell
)
{
radius
=
_getSplashTargetSize
(
referenceBox
.
size
,
position
);
}
else
{
radius
=
_kDefaultSplashRadius
;
}
_InkSplash
splash
=
new
_InkSplash
(
renderer:
this
,
referenceBox:
referenceBox
,
position:
position
,
targetRadius:
radius
,
clipToReferenceBox:
containedInWell
,
onRemoved:
onRemoved
);
addInkFeature
(
splash
);
return
splash
;
}
double
_getSplashTargetSize
(
Size
bounds
,
Point
position
)
{
double
d1
=
(
position
-
bounds
.
topLeft
(
Point
.
origin
)).
distance
;
double
d2
=
(
position
-
bounds
.
topRight
(
Point
.
origin
)).
distance
;
double
d3
=
(
position
-
bounds
.
bottomLeft
(
Point
.
origin
)).
distance
;
double
d4
=
(
position
-
bounds
.
bottomRight
(
Point
.
origin
)).
distance
;
return
math
.
max
(
math
.
max
(
d1
,
d2
),
math
.
max
(
d3
,
d4
)).
ceilToDouble
();
}
InkHighlight
highlightRectAt
({
RenderBox
referenceBox
,
Color
color
,
VoidCallback
onRemoved
})
{
_InkHighlight
highlight
=
new
_InkHighlight
(
renderer:
this
,
referenceBox:
referenceBox
,
color:
color
,
onRemoved:
onRemoved
);
addInkFeature
(
highlight
);
return
highlight
;
}
void
addInkFeature
(
InkFeature
feature
)
{
assert
(!
feature
.
_debugDisposed
);
assert
(
feature
.
renderer
==
this
);
assert
(!
_inkFeatures
.
contains
(
feature
));
_inkFeatures
.
add
(
feature
);
markNeedsPaint
();
}
void
_removeFeature
(
InkFeature
feature
)
{
_inkFeatures
.
remove
(
feature
);
markNeedsPaint
();
}
bool
hitTestSelf
(
Point
position
)
=>
true
;
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
_inkFeatures
.
isNotEmpty
)
{
final
Canvas
canvas
=
context
.
canvas
;
canvas
.
save
();
canvas
.
translate
(
offset
.
dx
,
offset
.
dy
);
canvas
.
clipRect
(
Point
.
origin
&
size
);
for
(
InkFeature
inkFeature
in
_inkFeatures
)
inkFeature
.
_paint
(
canvas
);
canvas
.
restore
();
}
super
.
paint
(
context
,
offset
);
}
}
class
InkFeatures
extends
OneChildRenderObjectWidget
{
InkFeatures
({
Key
key
,
this
.
color
,
Widget
child
})
:
super
(
key:
key
,
child:
child
);
final
Color
color
;
RenderInkFeatures
createRenderObject
()
=>
new
RenderInkFeatures
(
color:
color
);
void
updateRenderObject
(
RenderInkFeatures
renderObject
,
InkFeatures
oldWidget
)
{
renderObject
.
color
=
color
;
}
}
abstract
class
InkFeature
{
InkFeature
({
this
.
renderer
,
this
.
referenceBox
,
this
.
onRemoved
});
final
RenderInkFeatures
renderer
;
final
RenderBox
referenceBox
;
final
VoidCallback
onRemoved
;
bool
_debugDisposed
=
false
;
void
dispose
()
{
assert
(!
_debugDisposed
);
assert
(()
{
_debugDisposed
=
true
;
return
true
;
});
renderer
.
_removeFeature
(
this
);
if
(
onRemoved
!=
null
)
onRemoved
();
}
void
_paint
(
Canvas
canvas
)
{
assert
(
referenceBox
.
attached
);
assert
(!
_debugDisposed
);
// find the chain of renderers from us to the feature's referenceBox
List
<
RenderBox
>
descendants
=
<
RenderBox
>[];
RenderBox
node
=
referenceBox
;
while
(
node
!=
renderer
)
{
descendants
.
add
(
node
);
node
=
node
.
parent
;
assert
(
node
!=
null
);
}
// determine the transform that gets our coordinate system to be like theirs
Matrix4
transform
=
new
Matrix4
.
identity
();
for
(
RenderBox
descendant
in
descendants
.
reversed
)
descendant
.
applyPaintTransform
(
transform
);
paintFeature
(
canvas
,
transform
);
}
void
paintFeature
(
Canvas
canvas
,
Matrix4
transform
);
String
toString
()
=>
"
$runtimeType
@
$hashCode
"
;
}
class
_InkSplash
extends
InkFeature
implements
InkSplash
{
_InkSplash
({
RenderInkFeatures
renderer
,
RenderBox
referenceBox
,
this
.
position
,
this
.
targetRadius
,
this
.
clipToReferenceBox
,
VoidCallback
onRemoved
})
:
super
(
renderer:
renderer
,
referenceBox:
referenceBox
,
onRemoved:
onRemoved
)
{
_radius
=
new
ValuePerformance
<
double
>(
variable:
new
AnimatedValue
<
double
>(
_kSplashInitialSize
,
end:
targetRadius
,
curve:
Curves
.
easeOut
),
duration:
new
Duration
(
milliseconds:
(
targetRadius
/
_kSplashUnconfirmedVelocity
).
floor
())
)..
addListener
(
_handleRadiusChange
)
..
play
();
}
final
Point
position
;
final
double
targetRadius
;
final
bool
clipToReferenceBox
;
double
_pinnedRadius
;
ValuePerformance
<
double
>
_radius
;
void
confirm
()
{
_updateVelocity
(
_kSplashConfirmedVelocity
);
}
void
cancel
()
{
_updateVelocity
(
_kSplashCanceledVelocity
);
_pinnedRadius
=
_radius
.
value
;
}
void
_updateVelocity
(
double
velocity
)
{
int
duration
=
(
targetRadius
/
velocity
).
floor
();
_radius
.
duration
=
new
Duration
(
milliseconds:
duration
);
_radius
.
play
();
}
void
_handleRadiusChange
()
{
if
(
_radius
.
value
==
targetRadius
)
dispose
();
else
renderer
.
markNeedsPaint
();
}
void
dispose
()
{
_radius
.
stop
();
super
.
dispose
();
}
void
paintFeature
(
Canvas
canvas
,
Matrix4
transform
)
{
int
alpha
=
(
_kSplashInitialAlpha
*
(
1.1
-
(
_radius
.
value
/
targetRadius
))).
floor
();
Paint
paint
=
new
Paint
()..
color
=
new
Color
(
alpha
<<
24
);
// TODO(ianh): in dark theme, this isn't very visible
double
radius
=
_pinnedRadius
==
null
?
_radius
.
value
:
_pinnedRadius
;
Offset
originOffset
=
MatrixUtils
.
getAsTranslation
(
transform
);
if
(
originOffset
==
null
)
{
canvas
.
save
();
canvas
.
concat
(
transform
.
storage
);
if
(
clipToReferenceBox
)
canvas
.
clipRect
(
Point
.
origin
&
referenceBox
.
size
);
canvas
.
drawCircle
(
position
,
radius
,
paint
);
canvas
.
restore
();
}
else
{
if
(
clipToReferenceBox
)
{
canvas
.
save
();
canvas
.
clipRect
(
originOffset
.
toPoint
()
&
referenceBox
.
size
);
}
canvas
.
drawCircle
(
position
+
originOffset
,
radius
,
paint
);
if
(
clipToReferenceBox
)
canvas
.
restore
();
}
}
}
class
_InkHighlight
extends
InkFeature
implements
InkHighlight
{
_InkHighlight
({
RenderInkFeatures
renderer
,
RenderBox
referenceBox
,
Color
color
,
VoidCallback
onRemoved
})
:
_color
=
color
,
super
(
renderer:
renderer
,
referenceBox:
referenceBox
,
onRemoved:
onRemoved
)
{
_alpha
=
new
ValuePerformance
<
int
>(
variable:
new
AnimatedIntValue
(
0
,
end:
color
.
alpha
,
curve:
Curves
.
linear
),
duration:
_kHighlightFadeDuration
)..
addListener
(
_handleAlphaChange
)
..
play
();
}
Color
get
color
=>
_color
;
Color
_color
;
void
set
color
(
Color
value
)
{
if
(
value
==
_color
)
return
;
_color
=
value
;
renderer
.
markNeedsPaint
();
}
bool
get
active
=>
_active
;
bool
_active
=
true
;
ValuePerformance
<
int
>
_alpha
;
void
activate
()
{
_active
=
true
;
_alpha
.
forward
();
}
void
deactivate
()
{
_active
=
false
;
_alpha
.
reverse
();
}
void
_handleAlphaChange
()
{
if
(
_alpha
.
value
==
0.0
&&
!
_active
)
dispose
();
else
renderer
.
markNeedsPaint
();
}
void
dispose
()
{
_alpha
.
stop
();
super
.
dispose
();
}
void
paintFeature
(
Canvas
canvas
,
Matrix4
transform
)
{
Paint
paint
=
new
Paint
()..
color
=
color
.
withAlpha
(
_alpha
.
value
);
Offset
originOffset
=
MatrixUtils
.
getAsTranslation
(
transform
);
if
(
originOffset
==
null
)
{
canvas
.
save
();
canvas
.
concat
(
transform
.
storage
);
canvas
.
drawRect
(
Point
.
origin
&
referenceBox
.
size
,
paint
);
canvas
.
restore
();
}
else
{
canvas
.
drawRect
(
originOffset
.
toPoint
()
&
referenceBox
.
size
,
paint
);
}
}
}
packages/flutter/lib/src/material/material_button.dart
View file @
1a0bdf41
...
...
@@ -36,12 +36,16 @@ abstract class MaterialButton extends StatefulComponent {
MaterialButton
({
Key
key
,
this
.
child
,
this
.
textTheme
,
this
.
textColor
,
this
.
disabledTextColor
,
this
.
onPressed
})
:
super
(
key:
key
);
final
Widget
child
;
final
ButtonColor
textColor
;
final
ButtonColor
textTheme
;
final
Color
textColor
;
final
Color
disabledTextColor
;
final
VoidCallback
onPressed
;
bool
get
enabled
=>
onPressed
!=
null
;
...
...
@@ -57,12 +61,14 @@ abstract class MaterialButtonState<T extends MaterialButton> extends State<T> {
bool
highlight
=
false
;
int
get
elevation
;
Color
getColor
(
BuildContext
context
,
{
bool
highlight
}
);
Color
getColor
(
BuildContext
context
);
ThemeBrightness
getColorBrightness
(
BuildContext
context
);
Color
getTextColor
(
BuildContext
context
)
{
if
(
config
.
enabled
)
{
switch
(
config
.
textColor
??
ButtonTheme
.
of
(
context
))
{
if
(
config
.
textColor
!=
null
)
return
config
.
textColor
;
switch
(
config
.
textTheme
??
ButtonTheme
.
of
(
context
))
{
case
ButtonColor
.
accent
:
return
Theme
.
of
(
context
).
accentColor
;
case
ButtonColor
.
normal
:
...
...
@@ -74,6 +80,8 @@ abstract class MaterialButtonState<T extends MaterialButton> extends State<T> {
}
}
}
if
(
config
.
disabledTextColor
!=
null
)
return
config
.
disabledTextColor
;
switch
(
getColorBrightness
(
context
))
{
case
ThemeBrightness
.
light
:
return
Colors
.
black26
;
...
...
@@ -84,34 +92,45 @@ abstract class MaterialButtonState<T extends MaterialButton> extends State<T> {
void
_handleHighlightChanged
(
bool
value
)
{
setState
(()
{
// mostly just used by the RaisedButton subclass to change the elevation
highlight
=
value
;
});
}
Widget
build
(
BuildContext
context
)
{
Widget
contents
=
new
Container
(
padding:
new
EdgeDims
.
symmetric
(
horizontal:
8.0
),
child:
new
Center
(
widthFactor:
1.0
,
child:
config
.
child
Widget
contents
=
new
InkWell
(
onTap:
config
.
onPressed
,
onHighlightChanged:
_handleHighlightChanged
,
child:
new
Container
(
padding:
new
EdgeDims
.
symmetric
(
horizontal:
8.0
),
child:
new
Center
(
widthFactor:
1.0
,
child:
config
.
child
)
)
);
TextStyle
style
=
Theme
.
of
(
context
).
text
.
button
.
copyWith
(
color:
getTextColor
(
context
));
int
elevation
=
this
.
elevation
;
Color
color
=
getColor
(
context
);
if
(
elevation
>
0
||
color
!=
null
)
{
contents
=
new
Material
(
type:
MaterialType
.
button
,
color:
getColor
(
context
),
elevation:
elevation
,
textStyle:
style
,
child:
contents
);
}
else
{
contents
=
new
DefaultTextStyle
(
style:
style
,
child:
contents
);
}
return
new
Container
(
height:
36.0
,
constraints:
new
BoxConstraints
(
minWidth:
88.0
),
margin:
new
EdgeDims
.
all
(
8.0
),
child:
new
Material
(
type:
MaterialType
.
button
,
elevation:
elevation
,
textStyle:
Theme
.
of
(
context
).
text
.
button
.
copyWith
(
color:
getTextColor
(
context
)),
child:
new
InkWell
(
onTap:
config
.
enabled
?
config
.
onPressed
:
null
,
defaultColor:
getColor
(
context
,
highlight:
false
),
highlightColor:
getColor
(
context
,
highlight:
true
),
onHighlightChanged:
_handleHighlightChanged
,
child:
contents
)
)
child:
contents
);
}
}
packages/flutter/lib/src/material/raised_button.dart
View file @
1a0bdf41
...
...
@@ -12,36 +12,56 @@ class RaisedButton extends MaterialButton {
RaisedButton
({
Key
key
,
Widget
child
,
this
.
color
,
this
.
colorBrightness
,
this
.
disabledColor
,
this
.
elevation
:
2
,
this
.
highlightElevation
:
8
,
this
.
disabledElevation
:
0
,
VoidCallback
onPressed
})
:
super
(
key:
key
,
child:
child
,
onPressed:
onPressed
);
final
Color
color
;
final
Color
disabledColor
;
/// Controls the default text color if the text color isn't explicit set.
final
ThemeBrightness
colorBrightness
;
final
int
elevation
;
final
int
highlightElevation
;
final
int
disabledElevation
;
_RaisedButtonState
createState
()
=>
new
_RaisedButtonState
();
}
class
_RaisedButtonState
extends
MaterialButtonState
<
RaisedButton
>
{
int
get
elevation
=>
config
.
enabled
?
(
highlight
?
8
:
2
)
:
0
;
int
get
elevation
{
if
(
config
.
enabled
)
{
if
(
highlight
)
return
config
.
highlightElevation
;
return
config
.
elevation
;
}
else
{
return
config
.
disabledElevation
;
}
}
Color
getColor
(
BuildContext
context
,
{
bool
highlight
}
)
{
Color
getColor
(
BuildContext
context
)
{
if
(
config
.
enabled
)
{
if
(
config
.
color
!=
null
)
return
config
.
color
;
switch
(
Theme
.
of
(
context
).
brightness
)
{
case
ThemeBrightness
.
light
:
if
(
highlight
)
return
Colors
.
grey
[
350
];
else
return
Colors
.
grey
[
300
];
break
;
return
Colors
.
grey
[
300
];
case
ThemeBrightness
.
dark
:
Map
<
int
,
Color
>
swatch
=
Theme
.
of
(
context
).
primarySwatch
??
Colors
.
blue
;
if
(
highlight
)
return
swatch
[
700
];
else
return
swatch
[
600
];
break
;
return
swatch
[
600
];
}
}
else
{
if
(
config
.
disabledColor
!=
null
)
return
config
.
disabledColor
;
switch
(
Theme
.
of
(
context
).
brightness
)
{
case
ThemeBrightness
.
light
:
return
Colors
.
black12
;
...
...
@@ -52,7 +72,7 @@ class _RaisedButtonState extends MaterialButtonState<RaisedButton> {
}
ThemeBrightness
getColorBrightness
(
BuildContext
context
)
{
return
Theme
.
of
(
context
).
brightness
;
return
config
.
colorBrightness
??
Theme
.
of
(
context
).
brightness
;
}
}
packages/flutter/lib/src/material/scaffold.dart
View file @
1a0bdf41
...
...
@@ -115,7 +115,10 @@ class ScaffoldState extends State<Scaffold> {
}
ScaffoldFeatureController
<
SnackBar
>
controller
;
controller
=
new
ScaffoldFeatureController
<
SnackBar
>.
_
(
snackbar
.
withPerformance
(
_snackBarPerformance
),
// We provide a fallback key so that if back-to-back snackbars happen to
// match in structure, material ink splashes and highlights don't survive
// from one to the next.
snackbar
.
withPerformance
(
_snackBarPerformance
,
fallbackKey:
new
UniqueKey
()),
new
Completer
(),
()
{
assert
(
_snackBars
.
first
==
controller
);
...
...
packages/flutter/lib/src/material/snack_bar.dart
View file @
1a0bdf41
...
...
@@ -5,14 +5,18 @@
import
'package:flutter/animation.dart'
;
import
'package:flutter/widgets.dart'
;
import
'flat_button.dart'
;
import
'material.dart'
;
import
'material_button.dart'
;
import
'theme.dart'
;
import
'theme_data.dart'
;
import
'typography.dart'
;
// https://www.google.com/design/spec/components/snackbars-toasts.html#snackbars-toasts-specs
const
double
_kSideMargins
=
24.0
;
const
double
_kSingleLineVerticalPadding
=
14.0
;
const
double
_kMultiLineVerticalPadding
=
24.0
;
const
double
_kMultiLineVerticalTopPadding
=
24.0
;
const
double
_kMultiLineVerticalSpaceBetweenTextAndButtons
=
10.0
;
const
Color
_kSnackBackground
=
const
Color
(
0xFF323232
);
// TODO(ianh): We should check if the given text and actions are going to fit on
...
...
@@ -35,11 +39,11 @@ class SnackBarAction extends StatelessComponent {
final
VoidCallback
onPressed
;
Widget
build
(
BuildContext
context
)
{
return
new
GestureDetecto
r
(
onTap:
onPressed
,
child:
new
Container
(
margin:
const
EdgeDims
.
only
(
left:
_kSideMargins
)
,
padding:
const
EdgeDims
.
symmetric
(
vertical:
_kSingleLineVerticalPadding
)
,
return
new
Containe
r
(
margin:
const
EdgeDims
.
only
(
left:
_kSideMargins
)
,
child:
new
FlatButton
(
onPressed:
onPressed
,
textTheme:
ButtonColor
.
accent
,
child:
new
Text
(
label
)
)
);
...
...
@@ -77,6 +81,7 @@ class SnackBar extends StatelessComponent {
];
if
(
actions
!=
null
)
children
.
addAll
(
actions
);
ThemeData
theme
=
Theme
.
of
(
context
);
return
new
ClipRect
(
child:
new
AlignTransition
(
performance:
performance
,
...
...
@@ -87,12 +92,20 @@ class SnackBar extends StatelessComponent {
color:
_kSnackBackground
,
child:
new
Container
(
margin:
const
EdgeDims
.
symmetric
(
horizontal:
_kSideMargins
),
child:
new
DefaultTextStyle
(
style:
new
TextStyle
(
color:
Theme
.
of
(
context
).
accentColor
),
child:
new
Theme
(
data:
new
ThemeData
(
brightness:
ThemeBrightness
.
dark
,
accentColor:
theme
.
accentColor
,
accentColorBrightness:
theme
.
accentColorBrightness
,
text:
Typography
.
white
),
child:
new
FadeTransition
(
performance:
performance
,
opacity:
new
AnimatedValue
<
double
>(
0.0
,
end:
1.0
,
curve:
_snackBarFadeCurve
),
child:
new
Row
(
children
)
child:
new
Row
(
children
,
alignItems:
FlexAlignItems
.
center
)
)
)
)
...
...
@@ -110,9 +123,9 @@ class SnackBar extends StatelessComponent {
);
}
SnackBar
withPerformance
(
Performance
newPerformance
)
{
SnackBar
withPerformance
(
Performance
newPerformance
,
{
Key
fallbackKey
}
)
{
return
new
SnackBar
(
key:
key
,
key:
key
??
fallbackKey
,
content:
content
,
actions:
actions
,
duration:
duration
,
...
...
packages/flutter/lib/src/material/tabs.dart
View file @
1a0bdf41
...
...
@@ -10,11 +10,11 @@ import 'package:flutter/rendering.dart';
import
'package:flutter/widgets.dart'
;
import
'colors.dart'
;
import
'constants.dart'
;
import
'icon.dart'
;
import
'icon_theme.dart'
;
import
'icon_theme_data.dart'
;
import
'ink_well.dart'
;
import
'material.dart'
;
import
'theme.dart'
;
typedef
void
TabSelectedIndexChanged
(
int
selectedIndex
);
...
...
@@ -403,6 +403,10 @@ class TabBarSelection {
int
_previousIndex
=
0
;
}
/// A tab strip, consisting of several TabLabels and a TabBarSelection.
/// The TabBarSelection can be used to link this to a TabBarView.
///
/// Tabs must always have an ancestor Material object.
class
TabBar
extends
Scrollable
{
TabBar
({
Key
key
,
...
...
@@ -551,13 +555,13 @@ class _TabBarState extends ScrollableState<TabBar> {
Widget
buildContent
(
BuildContext
context
)
{
assert
(
config
.
labels
!=
null
&&
config
.
labels
.
isNotEmpty
);
assert
(
Material
.
of
(
context
)
!=
null
);
ThemeData
themeData
=
Theme
.
of
(
context
);
Color
backgroundColor
=
themeData
.
primaryC
olor
;
Color
backgroundColor
=
Material
.
of
(
context
).
c
olor
;
Color
indicatorColor
=
themeData
.
accentColor
;
if
(
indicatorColor
==
backgroundColor
)
{
if
(
indicatorColor
==
backgroundColor
)
indicatorColor
=
Colors
.
white
;
}
TextStyle
textStyle
=
themeData
.
primaryTextTheme
.
body1
;
IconThemeData
iconTheme
=
themeData
.
primaryIconTheme
;
...
...
@@ -571,7 +575,7 @@ class _TabBarState extends ScrollableState<TabBar> {
textAndIcons
=
true
;
}
Widget
content
=
new
IconTheme
(
Widget
content
s
=
new
IconTheme
(
data:
iconTheme
,
child:
new
DefaultTextStyle
(
style:
textStyle
,
...
...
@@ -594,23 +598,17 @@ class _TabBarState extends ScrollableState<TabBar> {
);
if
(
config
.
isScrollable
)
{
content
=
new
SizeObserver
(
content
s
=
new
SizeObserver
(
onSizeChanged:
_handleViewportSizeChanged
,
child:
new
Viewport
(
scrollDirection:
ScrollDirection
.
horizontal
,
scrollOffset:
new
Offset
(
scrollOffset
,
0.0
),
child:
content
child:
content
s
)
);
}
return
new
AnimatedContainer
(
decoration:
new
BoxDecoration
(
backgroundColor:
backgroundColor
),
duration:
kThemeChangeDuration
,
child:
content
);
return
contents
;
}
}
...
...
packages/flutter/lib/src/material/theme_data.dart
View file @
1a0bdf41
...
...
@@ -27,15 +27,7 @@ class ThemeData {
// Some users want the pre-multiplied color, others just want the opacity.
hintColor
=
brightness
==
ThemeBrightness
.
dark
?
const
Color
(
0x42FFFFFF
)
:
const
Color
(
0x4C000000
),
hintOpacity
=
brightness
==
ThemeBrightness
.
dark
?
0.26
:
0.30
,
// TODO(eseidel): Where are highlight and selected colors documented?
// I flipped highlight/selected to match the News app (which is clearly not quite Material)
// Gmail has an interesting behavior of showing selected darker until
// you click on a different drawer item when the first one loses its
// selected color and becomes lighter, the ink then fills to make the new
// click dark to match the previous (resting) selected state. States
// revert when you cancel the tap.
highlightColor
=
const
Color
(
0x33999999
),
selectedColor
=
const
Color
(
0x66999999
),
highlightColor
=
brightness
==
ThemeBrightness
.
dark
?
const
Color
(
0x42FFFFFF
)
:
const
Color
(
0x1F000000
),
text
=
brightness
==
ThemeBrightness
.
dark
?
Typography
.
white
:
Typography
.
black
{
assert
(
brightness
!=
null
);
...
...
@@ -63,6 +55,13 @@ class ThemeData {
/// The brightness of the overall theme of the application. Used by widgets
/// like buttons to determine what color to pick when not using the primary or
/// accent color.
///
/// When the ThemeBrightness is dark, the canvas, card, and primary colors are
/// all dark. When the ThemeBrightness is light, the canvas and card colors
/// are bright, and the primary color's darkness varies as described by
/// primaryColorBrightness. The primaryColor does not contrast well with the
/// card and canvas colors when the brightness is dask; when the birghtness is
/// dark, use Colors.white or the accentColor for a contrasting color.
final
ThemeBrightness
brightness
;
final
Map
<
int
,
Color
>
primarySwatch
;
...
...
@@ -71,8 +70,9 @@ class ThemeData {
final
Color
dividerColor
;
final
Color
hintColor
;
final
Color
highlightColor
;
final
Color
selectedColor
;
final
double
hintOpacity
;
/// Text with a color that contrasts with the card and canvas colors.
final
TextTheme
text
;
/// The background colour for major parts of the app (toolbars, tab bars, etc)
...
...
@@ -128,7 +128,6 @@ class ThemeData {
(
otherData
.
dividerColor
==
dividerColor
)
&&
(
otherData
.
hintColor
==
hintColor
)
&&
(
otherData
.
highlightColor
==
highlightColor
)
&&
(
otherData
.
selectedColor
==
selectedColor
)
&&
(
otherData
.
hintOpacity
==
hintOpacity
)
&&
(
otherData
.
text
==
text
)
&&
(
otherData
.
primaryColorBrightness
==
primaryColorBrightness
)
&&
...
...
@@ -143,7 +142,6 @@ class ThemeData {
value
=
37
*
value
+
dividerColor
.
hashCode
;
value
=
37
*
value
+
hintColor
.
hashCode
;
value
=
37
*
value
+
highlightColor
.
hashCode
;
value
=
37
*
value
+
selectedColor
.
hashCode
;
value
=
37
*
value
+
hintOpacity
.
hashCode
;
value
=
37
*
value
+
text
.
hashCode
;
value
=
37
*
value
+
primaryColorBrightness
.
hashCode
;
...
...
packages/flutter/lib/src/material/tool_bar.dart
View file @
1a0bdf41
...
...
@@ -7,7 +7,7 @@ import 'package:flutter/widgets.dart';
import
'constants.dart'
;
import
'icon_theme.dart'
;
import
'icon_theme_data.dart'
;
import
'
shadows
.dart'
;
import
'
material
.dart'
;
import
'tabs.dart'
;
import
'theme.dart'
;
import
'typography.dart'
;
...
...
@@ -36,9 +36,9 @@ class ToolBar extends StatelessComponent {
final
TextTheme
textTheme
;
final
EdgeDims
padding
;
ToolBar
withPadding
(
EdgeDims
newPadding
)
{
ToolBar
withPadding
(
EdgeDims
newPadding
,
{
Key
fallbackKey
}
)
{
return
new
ToolBar
(
key:
key
,
key:
key
??
fallbackKey
,
left:
left
,
center:
center
,
right:
right
,
...
...
@@ -67,52 +67,63 @@ class ToolBar extends StatelessComponent {
sideStyle
??=
primaryTextTheme
.
body2
;
}
List
<
Widget
>
children
=
new
List
<
Widget
>();
final
List
<
Widget
>
firstRow
=
<
Widget
>[];
if
(
left
!=
null
)
children
.
add
(
left
);
children
.
add
(
firstRow
.
add
(
left
);
firstRow
.
add
(
new
Flexible
(
child:
new
Padding
(
child:
center
!=
null
?
new
DefaultTextStyle
(
child:
center
,
style:
centerStyle
)
:
null
,
padding:
new
EdgeDims
.
only
(
left:
24.0
)
padding:
new
EdgeDims
.
only
(
left:
24.0
)
,
child:
center
!=
null
?
new
DefaultTextStyle
(
style:
centerStyle
,
child:
center
)
:
null
)
)
);
if
(
right
!=
null
)
children
.
addAll
(
right
);
final
List
<
Widget
>
columnChildren
=
<
Widget
>[
new
Container
(
height:
kToolBarHeight
,
child:
new
Row
(
children
))
firstRow
.
addAll
(
right
);
final
List
<
Widget
>
rows
=
<
Widget
>[
new
Container
(
height:
kToolBarHeight
,
child:
new
DefaultTextStyle
(
style:
sideStyle
,
child:
new
Row
(
firstRow
)
)
)
];
if
(
bottom
!=
null
)
columnChildren
.
add
(
new
DefaultTextStyle
(
style:
centerStyle
,
child:
new
Container
(
height:
kExtendedToolBarHeight
-
kToolBarHeight
,
child:
bottom
)
));
if
(
bottom
!=
null
)
{
rows
.
add
(
new
DefaultTextStyle
(
style:
centerStyle
,
child:
new
Container
(
height:
kExtendedToolBarHeight
-
kToolBarHeight
,
child:
bottom
)
)
);
}
if
(
tabBar
!=
null
)
columnChildren
.
add
(
tabBar
);
rows
.
add
(
tabBar
);
EdgeDims
combinedPadding
=
new
EdgeDims
.
symmetric
(
horizontal:
8.0
);
if
(
padding
!=
null
)
combinedPadding
+=
padding
;
Widget
content
=
new
AnimatedContainer
(
duration:
kThemeChangeDuration
,
padding:
new
EdgeDims
.
symmetric
(
horizontal:
8.0
),
decoration:
new
BoxDecoration
(
backgroundColor:
color
,
boxShadow:
elevationToShadow
[
elevation
]
),
child:
new
DefaultTextStyle
(
style:
sideStyle
,
child:
new
Container
(
padding:
padding
,
child:
new
Column
(
columnChildren
,
justifyContent:
FlexJustifyContent
.
collapse
))
Widget
contents
=
new
Material
(
color:
color
,
elevation:
elevation
,
child:
new
Container
(
padding:
combinedPadding
,
child:
new
Column
(
rows
,
justifyContent:
FlexJustifyContent
.
collapse
)
)
);
if
(
iconThemeData
!=
null
)
content
=
new
IconTheme
(
data:
iconThemeData
,
child:
content
);
return
content
;
contents
=
new
IconTheme
(
data:
iconThemeData
,
child:
contents
);
return
contents
;
}
}
packages/flutter/lib/src/widgets/framework.dart
View file @
1a0bdf41
...
...
@@ -386,8 +386,17 @@ abstract class State<T extends StatefulComponent> {
_element
.
markNeedsBuild
();
}
/// Called when this object is removed from the tree. Override this to clean
/// up any resources allocated by this object.
/// Called when this object is removed from the tree.
/// The object might momentarily be reattached to the tree elsewhere.
///
/// Use this to clean up any links between this state and other
/// elements in the tree (e.g. if you have provided an ancestor with
/// a pointer to a descendant's renderObject).
void
deactivate
()
{
}
/// Called when this object is removed from the tree permanently.
/// Override this to clean up any resources allocated by this
/// object.
///
/// If you override this, make sure to end your method with a call to
/// super.dispose().
...
...
@@ -405,6 +414,11 @@ abstract class State<T extends StatefulComponent> {
/// provides the set of inherited widgets for this location in the tree.
Widget
build
(
BuildContext
context
);
/// Called when an Inherited widget in the ancestor chain has changed. Usually
/// there is nothing to do here; whenever this is called, build() is also
/// called.
void
dependenciesChanged
(
Type
affectedWidgetType
)
{
}
String
toString
()
{
final
List
<
String
>
data
=
<
String
>[];
debugFillDescription
(
data
);
...
...
@@ -540,6 +554,7 @@ abstract class BuildContext {
InheritedWidget
inheritFromWidgetOfType
(
Type
targetType
);
Widget
ancestorWidgetOfType
(
Type
targetType
);
State
ancestorStateOfType
(
Type
targetType
);
RenderObject
ancestorRenderObjectOfType
(
Type
targetType
);
void
visitAncestorElements
(
bool
visitor
(
Element
element
));
void
visitChildElements
(
void
visitor
(
Element
element
));
}
...
...
@@ -777,7 +792,7 @@ abstract class Element<T extends Widget> implements BuildContext {
assert
(
child
.
_parent
==
this
);
child
.
_parent
=
null
;
child
.
detachRenderObject
();
_inactiveElements
.
add
(
child
);
_inactiveElements
.
add
(
child
);
// this eventually calls child.deactivate()
}
void
deactivate
()
{
...
...
@@ -839,13 +854,24 @@ abstract class Element<T extends Widget> implements BuildContext {
return
statefulAncestor
?.
state
;
}
RenderObject
ancestorRenderObjectOfType
(
Type
targetType
)
{
Element
ancestor
=
_parent
;
while
(
ancestor
!=
null
)
{
if
(
ancestor
is
RenderObjectElement
&&
ancestor
.
renderObject
.
runtimeType
==
targetType
)
break
;
ancestor
=
ancestor
.
_parent
;
}
RenderObjectElement
renderObjectAncestor
=
ancestor
;
return
renderObjectAncestor
?.
renderObject
;
}
void
visitAncestorElements
(
bool
visitor
(
Element
element
))
{
Element
ancestor
=
_parent
;
while
(
ancestor
!=
null
&&
visitor
(
ancestor
))
ancestor
=
ancestor
.
_parent
;
}
void
dependenciesChanged
()
{
void
dependenciesChanged
(
Type
affectedWidgetType
)
{
assert
(
false
);
}
...
...
@@ -1024,7 +1050,7 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
/// Called by rebuild() after the appropriate checks have been made.
void
performRebuild
();
void
dependenciesChanged
()
{
void
dependenciesChanged
(
Type
affectedWidgetType
)
{
markNeedsBuild
();
}
...
...
@@ -1169,6 +1195,11 @@ class StatefulComponentElement<T extends StatefulComponent, U extends State<T>>
rebuild
();
}
void
deactivate
()
{
_state
.
deactivate
();
super
.
deactivate
();
}
void
unmount
()
{
super
.
unmount
();
_state
.
dispose
();
...
...
@@ -1183,6 +1214,11 @@ class StatefulComponentElement<T extends StatefulComponent, U extends State<T>>
_state
=
null
;
}
void
dependenciesChanged
(
Type
affectedWidgetType
)
{
super
.
dependenciesChanged
(
affectedWidgetType
);
_state
.
dependenciesChanged
(
affectedWidgetType
);
}
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
if
(
state
!=
null
)
...
...
@@ -1252,7 +1288,7 @@ class InheritedElement extends ProxyElement<InheritedWidget> {
void
notifyChildren
(
Element
child
)
{
if
(
child
.
_dependencies
!=
null
&&
child
.
_dependencies
.
contains
(
ourRuntimeType
))
{
child
.
dependenciesChanged
();
child
.
dependenciesChanged
(
ourRuntimeType
);
}
if
(
child
.
runtimeType
!=
ourRuntimeType
)
child
.
visitChildren
(
notifyChildren
);
...
...
packages/flutter/lib/src/widgets/notification_listener.dart
0 → 100644
View file @
1a0bdf41
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'framework.dart'
;
/// Return true to cancel the notification bubbling.
typedef
bool
NotificationListenerCallback
<
T
extends
Notification
>(
T
notification
);
abstract
class
Notification
{
void
dispatch
(
BuildContext
target
)
{
target
.
visitAncestorElements
((
Element
element
)
{
if
(
element
is
StatelessComponentElement
&&
element
.
widget
is
NotificationListener
)
{
final
NotificationListener
widget
=
element
.
widget
;
if
(
widget
.
_dispatch
(
this
))
return
false
;
}
return
true
;
});
}
}
class
NotificationListener
<
T
extends
Notification
>
extends
StatelessComponent
{
NotificationListener
({
Key
key
,
this
.
child
,
this
.
onNotification
})
:
super
(
key:
key
);
final
Widget
child
;
final
NotificationListenerCallback
<
T
>
onNotification
;
bool
_dispatch
(
Notification
notification
)
{
if
(
onNotification
!=
null
&&
notification
is
T
)
return
onNotification
(
notification
);
return
false
;
}
Widget
build
(
BuildContext
context
)
=>
child
;
}
/// Indicates that the layout of one of the descendants of the object receiving
/// this notification has changed in some way, and that therefore any
/// assumptions about that layout are no longer valid.
///
/// Useful if, for instance, you're trying to align multiple descendants.
///
/// Be aware that in the widgets library, only the [Scrollable] classes dispatch
/// this notification. (Transitions, in particular, do not.) Changing one's
/// layout in one's build function does not cause this notification to be
/// dispatched automatically. If an ancestor expects to be notified for any
/// layout change, make sure you only use widgets that either never change
/// layout, or that do notify their ancestors when appropriate.
class
LayoutChangedNotification
extends
Notification
{
}
packages/flutter/lib/src/widgets/scrollable.dart
View file @
1a0bdf41
...
...
@@ -16,6 +16,7 @@ import 'framework.dart';
import
'gesture_detector.dart'
;
import
'homogeneous_viewport.dart'
;
import
'mixed_viewport.dart'
;
import
'notification_listener.dart'
;
import
'page_storage.dart'
;
// The gesture velocity properties are pixels/second, config min,max limits are pixels/ms
...
...
@@ -50,6 +51,8 @@ abstract class Scrollable extends StatefulComponent {
final
ScrollListener
onScrollEnd
;
final
SnapOffsetCallback
snapOffsetCallback
;
final
double
snapAlignmentOffset
;
ScrollableState
createState
();
}
abstract
class
ScrollableState
<
T
extends
Scrollable
>
extends
State
<
T
>
{
...
...
@@ -180,6 +183,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
_scrollOffset
=
newScrollOffset
;
});
PageStorage
.
of
(
context
)?.
writeState
(
context
,
_scrollOffset
);
new
ScrollNotification
(
this
,
_scrollOffset
).
dispatch
(
context
);
dispatchOnScroll
();
}
...
...
@@ -271,6 +275,12 @@ ScrollableState findScrollableAncestor(BuildContext context) {
return
result
;
}
class
ScrollNotification
extends
Notification
{
ScrollNotification
(
this
.
scrollable
,
this
.
position
);
final
ScrollableState
scrollable
;
final
double
position
;
}
Future
ensureWidgetIsVisible
(
BuildContext
context
,
{
Duration
duration
,
Curve
curve
})
{
assert
(
context
.
findRenderObject
()
is
RenderBox
);
// TODO(abarth): This function doesn't handle nested scrollable widgets.
...
...
packages/flutter/lib/widgets.dart
View file @
1a0bdf41
...
...
@@ -25,6 +25,7 @@ export 'src/widgets/mimic.dart';
export
'src/widgets/mixed_viewport.dart'
;
export
'src/widgets/modal_barrier.dart'
;
export
'src/widgets/navigator.dart'
;
export
'src/widgets/notification_listener.dart'
;
export
'src/widgets/overlay.dart'
;
export
'src/widgets/page_storage.dart'
;
export
'src/widgets/placeholder.dart'
;
...
...
packages/unit/test/animation/scheduler_test.dart
View file @
1a0bdf41
...
...
@@ -2,6 +2,10 @@ import 'package:flutter/animation.dart';
import
'package:test/test.dart'
;
void
main
(
)
{
test
(
"Check for a time dilation being in effect"
,
()
{
expect
(
timeDilation
,
equals
(
1.0
));
});
test
(
"Can cancel queued callback"
,
()
{
int
secondId
;
...
...
packages/unit/test/widget/date_picker_test.dart
View file @
1a0bdf41
...
...
@@ -11,16 +11,18 @@ void main() {
testWidgets
((
WidgetTester
tester
)
{
DateTime
currentValue
;
Widget
widget
=
new
Block
(<
Widget
>[
new
DatePicker
(
selectedDate:
new
DateTime
.
utc
(
2015
,
6
,
9
,
7
,
12
),
firstDate:
new
DateTime
.
utc
(
2013
),
lastDate:
new
DateTime
.
utc
(
2018
),
onChanged:
(
DateTime
dateTime
)
{
currentValue
=
dateTime
;
}
)
]);
Widget
widget
=
new
Material
(
child:
new
Block
(<
Widget
>[
new
DatePicker
(
selectedDate:
new
DateTime
.
utc
(
2015
,
6
,
9
,
7
,
12
),
firstDate:
new
DateTime
.
utc
(
2013
),
lastDate:
new
DateTime
.
utc
(
2018
),
onChanged:
(
DateTime
dateTime
)
{
currentValue
=
dateTime
;
}
)
])
);
tester
.
pumpWidget
(
widget
);
...
...
packages/unit/test/widget/heroes_test.dart
View file @
1a0bdf41
...
...
@@ -11,18 +11,22 @@ import 'test_matchers.dart';
Key
firstKey
=
new
Key
(
'first'
);
Key
secondKey
=
new
Key
(
'second'
);
final
Map
<
String
,
RouteBuilder
>
routes
=
<
String
,
RouteBuilder
>{
'/'
:
(
RouteArguments
args
)
=>
new
Block
([
new
Container
(
height:
100.0
,
width:
100.0
),
new
Card
(
child:
new
Hero
(
tag:
'a'
,
child:
new
Container
(
height:
100.0
,
width:
100.0
,
key:
firstKey
))),
new
Container
(
height:
100.0
,
width:
100.0
),
new
FlatButton
(
child:
new
Text
(
'button'
),
onPressed:
()
=>
Navigator
.
pushNamed
(
args
.
context
,
'/two'
)),
]),
'/two'
:
(
RouteArguments
args
)
=>
new
Block
([
new
Container
(
height:
150.0
,
width:
150.0
),
new
Card
(
child:
new
Hero
(
tag:
'a'
,
child:
new
Container
(
height:
150.0
,
width:
150.0
,
key:
secondKey
))),
new
Container
(
height:
150.0
,
width:
150.0
),
new
FlatButton
(
child:
new
Text
(
'button'
),
onPressed:
()
=>
Navigator
.
pop
(
args
.
context
)),
]),
'/'
:
(
RouteArguments
args
)
=>
new
Material
(
child:
new
Block
([
new
Container
(
height:
100.0
,
width:
100.0
),
new
Card
(
child:
new
Hero
(
tag:
'a'
,
child:
new
Container
(
height:
100.0
,
width:
100.0
,
key:
firstKey
))),
new
Container
(
height:
100.0
,
width:
100.0
),
new
FlatButton
(
child:
new
Text
(
'button'
),
onPressed:
()
=>
Navigator
.
pushNamed
(
args
.
context
,
'/two'
)),
])
),
'/two'
:
(
RouteArguments
args
)
=>
new
Material
(
child:
new
Block
([
new
Container
(
height:
150.0
,
width:
150.0
),
new
Card
(
child:
new
Hero
(
tag:
'a'
,
child:
new
Container
(
height:
150.0
,
width:
150.0
,
key:
secondKey
))),
new
Container
(
height:
150.0
,
width:
150.0
),
new
FlatButton
(
child:
new
Text
(
'button'
),
onPressed:
()
=>
Navigator
.
pop
(
args
.
context
)),
])
),
};
void
main
(
)
{
...
...
packages/unit/test/widget/tabs_test.dart
View file @
1a0bdf41
...
...
@@ -10,10 +10,12 @@ import 'package:test/test.dart';
TabBarSelection
selection
;
Widget
buildFrame
(
{
List
<
String
>
tabs
,
bool
isScrollable:
false
})
{
return
new
TabBar
(
labels:
tabs
.
map
((
String
tab
)
=>
new
TabLabel
(
text:
tab
)).
toList
(),
selection:
selection
,
isScrollable:
isScrollable
return
new
Material
(
child:
new
TabBar
(
labels:
tabs
.
map
((
String
tab
)
=>
new
TabLabel
(
text:
tab
)).
toList
(),
selection:
selection
,
isScrollable:
isScrollable
)
);
}
...
...
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