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
03e8ab1f
Unverified
Commit
03e8ab1f
authored
Jan 09, 2018
by
Hans Muller
Committed by
GitHub
Jan 09, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add support for a new kind of InkSplash: the "ripple" (#13986)
parent
c5c63dfd
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
614 additions
and
60 deletions
+614
-60
material.dart
packages/flutter/lib/material.dart
+1
-0
ink_highlight.dart
packages/flutter/lib/src/material/ink_highlight.dart
+3
-13
ink_ripple.dart
packages/flutter/lib/src/material/ink_ripple.dart
+275
-0
ink_splash.dart
packages/flutter/lib/src/material/ink_splash.dart
+44
-21
ink_well.dart
packages/flutter/lib/src/material/ink_well.dart
+143
-24
theme_data.dart
packages/flutter/lib/src/material/theme_data.dart
+23
-1
ink_paint_test.dart
packages/flutter/test/material/ink_paint_test.dart
+125
-1
No files found.
packages/flutter/lib/material.dart
View file @
03e8ab1f
...
...
@@ -53,6 +53,7 @@ export 'src/material/grid_tile_bar.dart';
export
'src/material/icon_button.dart'
;
export
'src/material/icons.dart'
;
export
'src/material/ink_highlight.dart'
;
export
'src/material/ink_ripple.dart'
;
export
'src/material/ink_splash.dart'
;
export
'src/material/ink_well.dart'
;
export
'src/material/input_border.dart'
;
...
...
packages/flutter/lib/src/material/ink_highlight.dart
View file @
03e8ab1f
...
...
@@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
import
'ink_well.dart'
show
InteractiveInkFeature
;
import
'material.dart'
;
const
Duration
_kHighlightFadeDuration
=
const
Duration
(
milliseconds:
200
);
...
...
@@ -25,7 +26,7 @@ const Duration _kHighlightFadeDuration = const Duration(milliseconds: 200);
/// * [Material], which is the widget on which the ink highlight is painted.
/// * [InkSplash], which is an ink feature that shows a reaction to user input
/// on a [Material].
class
InkHighlight
extends
InkFeature
{
class
InkHighlight
extends
In
teractiveIn
kFeature
{
/// Begin a highlight animation.
///
/// The [controller] argument is typically obtained via
...
...
@@ -45,11 +46,10 @@ class InkHighlight extends InkFeature {
VoidCallback
onRemoved
,
})
:
assert
(
color
!=
null
),
assert
(
shape
!=
null
),
_color
=
color
,
_shape
=
shape
,
_borderRadius
=
borderRadius
??
BorderRadius
.
zero
,
_rectCallback
=
rectCallback
,
super
(
controller:
controller
,
referenceBox:
referenceBox
,
onRemoved:
onRemoved
)
{
super
(
controller:
controller
,
referenceBox:
referenceBox
,
color:
color
,
onRemoved:
onRemoved
)
{
_alphaController
=
new
AnimationController
(
duration:
_kHighlightFadeDuration
,
vsync:
controller
.
vsync
)
..
addListener
(
controller
.
markNeedsPaint
)
..
addStatusListener
(
_handleAlphaStatusChanged
)
...
...
@@ -69,16 +69,6 @@ class InkHighlight extends InkFeature {
Animation
<
int
>
_alpha
;
AnimationController
_alphaController
;
/// The color of the ink used to emphasize part of the material.
Color
get
color
=>
_color
;
Color
_color
;
set
color
(
Color
value
)
{
if
(
value
==
_color
)
return
;
_color
=
value
;
controller
.
markNeedsPaint
();
}
/// Whether this part of the material is being visually emphasized.
bool
get
active
=>
_active
;
bool
_active
=
true
;
...
...
packages/flutter/lib/src/material/ink_ripple.dart
0 → 100644
View file @
03e8ab1f
// Copyright 2017 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
'dart:math'
as
math
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
import
'ink_well.dart'
;
import
'material.dart'
;
const
Duration
_kUnconfirmedRippleDuration
=
const
Duration
(
seconds:
1
);
const
Duration
_kFadeInDuration
=
const
Duration
(
milliseconds:
75
);
const
Duration
_kRadiusDuration
=
const
Duration
(
milliseconds:
225
);
const
Duration
_kFadeOutDuration
=
const
Duration
(
milliseconds:
450
);
const
Duration
_kCancelDuration
=
const
Duration
(
milliseconds:
75
);
// The fade out begins 300ms after the _fadeOutController starts. See confirm().
const
double
_kFadeOutIntervalStart
=
300.0
/
450.0
;
const
double
_kRippleConfirmedVelocity
=
1.0
;
// logical pixels per millisecond
RectCallback
_getClipCallback
(
RenderBox
referenceBox
,
bool
containedInkWell
,
RectCallback
rectCallback
)
{
if
(
rectCallback
!=
null
)
{
assert
(
containedInkWell
);
return
rectCallback
;
}
if
(
containedInkWell
)
return
()
=>
Offset
.
zero
&
referenceBox
.
size
;
return
null
;
}
double
_getTargetRadius
(
RenderBox
referenceBox
,
bool
containedInkWell
,
RectCallback
rectCallback
,
Offset
position
)
{
if
(
containedInkWell
)
{
final
Size
size
=
rectCallback
!=
null
?
rectCallback
().
size
:
referenceBox
.
size
;
return
_getRippleRadiusForPositionInSize
(
size
,
position
);
}
return
Material
.
defaultSplashRadius
;
}
double
_getRippleRadiusForPositionInSize
(
Size
bounds
,
Offset
position
)
{
final
double
d1
=
(
position
-
bounds
.
topLeft
(
Offset
.
zero
)).
distance
;
final
double
d2
=
(
position
-
bounds
.
topRight
(
Offset
.
zero
)).
distance
;
final
double
d3
=
(
position
-
bounds
.
bottomLeft
(
Offset
.
zero
)).
distance
;
final
double
d4
=
(
position
-
bounds
.
bottomRight
(
Offset
.
zero
)).
distance
;
return
math
.
max
(
math
.
max
(
d1
,
d2
),
math
.
max
(
d3
,
d4
)).
ceilToDouble
();
}
class
_InkRippleFactory
extends
InteractiveInkFeatureFactory
{
const
_InkRippleFactory
();
@override
InteractiveInkFeature
create
({
@required
MaterialInkController
controller
,
@required
RenderBox
referenceBox
,
@required
Offset
position
,
@required
Color
color
,
bool
containedInkWell:
false
,
RectCallback
rectCallback
,
BorderRadius
borderRadius
,
double
radius
,
VoidCallback
onRemoved
,
})
{
return
new
InkRipple
(
controller:
controller
,
referenceBox:
referenceBox
,
position:
position
,
color:
color
,
containedInkWell:
containedInkWell
,
rectCallback:
rectCallback
,
borderRadius:
borderRadius
,
radius:
radius
,
onRemoved:
onRemoved
,
);
}
}
/// A visual reaction on a piece of [Material] to user input.
///
/// A circular ink feature whose origin starts at the input touch point and
/// whose radius expands from 60% of the final radius. The splash origin
/// animates to the center of its [referenceBox].
///
/// This object is rarely created directly. Instead of creating an ink ripple,
/// consider using an [InkResponse] or [InkWell] widget, which uses
/// gestures (such as tap and long-press) to trigger ink splashes. This class
/// is used when the [Theme]'s [ThemeData.splashType] is [InkSplashType.ripple].
///
/// See also:
///
/// * [InkSplash], which is an ink splash feature that expands less
/// aggressively than the ripple.
/// * [InkResponse], which uses gestures to trigger ink highlights and ink
/// splashes in the parent [Material].
/// * [InkWell], which is a rectangular [InkResponse] (the most common type of
/// ink response).
/// * [Material], which is the widget on which the ink splash is painted.
/// * [InkHighlight], which is an ink feature that emphasizes a part of a
/// [Material].
class
InkRipple
extends
InteractiveInkFeature
{
/// Used to specify this type of ink splash for an [InkWell], [InkResponse]
/// or material [Theme].
static
const
InteractiveInkFeatureFactory
splashFactory
=
const
_InkRippleFactory
();
/// Begin a ripple, centered at [position] relative to [referenceBox].
///
/// The [controller] argument is typically obtained via
/// `Material.of(context)`.
///
/// If [containedInkWell] is true, then the ripple will be sized to fit
/// the well rectangle, then clipped to it when drawn. The well
/// rectangle is the box returned by [rectCallback], if provided, or
/// otherwise is the bounds of the [referenceBox].
///
/// If [containedInkWell] is false, then [rectCallback] should be null.
/// The ink ripple is clipped only to the edges of the [Material].
/// This is the default.
///
/// When the ripple is removed, [onRemoved] will be called.
InkRipple
({
@required
MaterialInkController
controller
,
@required
RenderBox
referenceBox
,
@required
Offset
position
,
@required
Color
color
,
bool
containedInkWell:
false
,
RectCallback
rectCallback
,
BorderRadius
borderRadius
,
double
radius
,
VoidCallback
onRemoved
,
})
:
assert
(
color
!=
null
),
assert
(
position
!=
null
),
_position
=
position
,
_borderRadius
=
borderRadius
??
BorderRadius
.
zero
,
_targetRadius
=
radius
??
_getTargetRadius
(
referenceBox
,
containedInkWell
,
rectCallback
,
position
),
_clipCallback
=
_getClipCallback
(
referenceBox
,
containedInkWell
,
rectCallback
),
super
(
controller:
controller
,
referenceBox:
referenceBox
,
color:
color
,
onRemoved:
onRemoved
)
{
assert
(
_borderRadius
!=
null
);
// Immediately begin fading-in the initial splash.
_fadeInController
=
new
AnimationController
(
duration:
_kFadeInDuration
,
vsync:
controller
.
vsync
)
..
addListener
(
controller
.
markNeedsPaint
)
..
forward
();
_fadeIn
=
new
IntTween
(
begin:
0
,
end:
color
.
alpha
,
).
animate
(
_fadeInController
);
// Controls the splash radius and its center. Starts upon confirm.
_radiusController
=
new
AnimationController
(
duration:
_kUnconfirmedRippleDuration
,
vsync:
controller
.
vsync
)
..
addListener
(
controller
.
markNeedsPaint
)
..
forward
();
// Initial splash diamater is 60% of the target diameter, final
// diameter is 10dps larger than the target diameter.
_radius
=
new
Tween
<
double
>(
begin:
_targetRadius
*
0.30
,
end:
_targetRadius
+
5.0
,
).
animate
(
new
CurvedAnimation
(
parent:
_radiusController
,
curve:
Curves
.
ease
,
)
);
// Controls the splash radius and its center. Starts upon confirm however its
// Interval delays changes until the radius expansion has completed.
_fadeOutController
=
new
AnimationController
(
duration:
_kFadeOutDuration
,
vsync:
controller
.
vsync
)
..
addListener
(
controller
.
markNeedsPaint
)
..
addStatusListener
(
_handleAlphaStatusChanged
);
_fadeOut
=
new
IntTween
(
begin:
color
.
alpha
,
end:
0
,
).
animate
(
new
CurvedAnimation
(
parent:
_fadeOutController
,
curve:
const
Interval
(
_kFadeOutIntervalStart
,
1.0
)
),
);
controller
.
addInkFeature
(
this
);
}
final
Offset
_position
;
final
BorderRadius
_borderRadius
;
final
double
_targetRadius
;
final
RectCallback
_clipCallback
;
Animation
<
double
>
_radius
;
AnimationController
_radiusController
;
Animation
<
int
>
_fadeIn
;
AnimationController
_fadeInController
;
Animation
<
int
>
_fadeOut
;
AnimationController
_fadeOutController
;
@override
void
confirm
()
{
_radiusController
..
duration
=
_kRadiusDuration
..
forward
();
_fadeOutController
.
forward
();
}
@override
void
cancel
()
{
_fadeInController
.
stop
();
_fadeOutController
.
animateTo
(
1.0
,
duration:
_kCancelDuration
);
}
void
_handleAlphaStatusChanged
(
AnimationStatus
status
)
{
if
(
status
==
AnimationStatus
.
completed
)
dispose
();
}
@override
void
dispose
()
{
_radiusController
.
dispose
();
_fadeInController
.
dispose
();
_fadeOutController
.
dispose
();
super
.
dispose
();
}
RRect
_clipRRectFromRect
(
Rect
rect
)
{
return
new
RRect
.
fromRectAndCorners
(
rect
,
topLeft:
_borderRadius
.
topLeft
,
topRight:
_borderRadius
.
topRight
,
bottomLeft:
_borderRadius
.
bottomLeft
,
bottomRight:
_borderRadius
.
bottomRight
,
);
}
void
_clipCanvasWithRect
(
Canvas
canvas
,
Rect
rect
,
{
Offset
offset
})
{
Rect
clipRect
=
rect
;
if
(
offset
!=
null
)
{
clipRect
=
clipRect
.
shift
(
offset
);
}
if
(
_borderRadius
!=
BorderRadius
.
zero
)
{
canvas
.
clipRRect
(
_clipRRectFromRect
(
clipRect
));
}
else
{
canvas
.
clipRect
(
clipRect
);
}
}
@override
void
paintFeature
(
Canvas
canvas
,
Matrix4
transform
)
{
final
int
alpha
=
_fadeInController
.
isAnimating
?
_fadeIn
.
value
:
_fadeOut
.
value
;
final
Paint
paint
=
new
Paint
()..
color
=
color
.
withAlpha
(
alpha
);
// Splash moves to the center of the reference box.
final
Offset
center
=
Offset
.
lerp
(
_position
,
referenceBox
.
size
.
center
(
Offset
.
zero
),
Curves
.
ease
.
transform
(
_radiusController
.
value
),
);
final
Offset
originOffset
=
MatrixUtils
.
getAsTranslation
(
transform
);
if
(
originOffset
==
null
)
{
canvas
.
save
();
canvas
.
transform
(
transform
.
storage
);
if
(
_clipCallback
!=
null
)
{
_clipCanvasWithRect
(
canvas
,
_clipCallback
());
}
canvas
.
drawCircle
(
center
,
_radius
.
value
,
paint
);
canvas
.
restore
();
}
else
{
if
(
_clipCallback
!=
null
)
{
canvas
.
save
();
_clipCanvasWithRect
(
canvas
,
_clipCallback
(),
offset:
originOffset
);
}
canvas
.
drawCircle
(
center
+
originOffset
,
_radius
.
value
,
paint
);
if
(
_clipCallback
!=
null
)
canvas
.
restore
();
}
}
}
packages/flutter/lib/src/material/ink_splash.dart
View file @
03e8ab1f
...
...
@@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart';
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
import
'ink_well.dart'
;
import
'material.dart'
;
const
Duration
_kUnconfirmedSplashDuration
=
const
Duration
(
seconds:
1
);
...
...
@@ -42,14 +43,48 @@ double _getSplashRadiusForPositionInSize(Size bounds, Offset position) {
return
math
.
max
(
math
.
max
(
d1
,
d2
),
math
.
max
(
d3
,
d4
)).
ceilToDouble
();
}
class
_InkSplashFactory
extends
InteractiveInkFeatureFactory
{
const
_InkSplashFactory
();
@override
InteractiveInkFeature
create
({
@required
MaterialInkController
controller
,
@required
RenderBox
referenceBox
,
@required
Offset
position
,
@required
Color
color
,
bool
containedInkWell:
false
,
RectCallback
rectCallback
,
BorderRadius
borderRadius
,
double
radius
,
VoidCallback
onRemoved
,
})
{
return
new
InkSplash
(
controller:
controller
,
referenceBox:
referenceBox
,
position:
position
,
color:
color
,
containedInkWell:
containedInkWell
,
rectCallback:
rectCallback
,
borderRadius:
borderRadius
,
radius:
radius
,
onRemoved:
onRemoved
,
);
}
}
/// A visual reaction on a piece of [Material] to user input.
///
/// A circular ink feature whose origin starts at the input touch point
/// and whose radius expands from zero.
///
/// This object is rarely created directly. Instead of creating an ink splash
/// directly, consider using an [InkResponse] or [InkWell] widget, which uses
/// gestures (such as tap and long-press) to trigger ink splashes.
///
/// See also:
///
/// * [InkRipple], which is an ink splash feature that expands more
/// aggressively than this class does.
/// * [InkResponse], which uses gestures to trigger ink highlights and ink
/// splashes in the parent [Material].
/// * [InkWell], which is a rectangular [InkResponse] (the most common type of
...
...
@@ -57,7 +92,11 @@ double _getSplashRadiusForPositionInSize(Size bounds, Offset position) {
/// * [Material], which is the widget on which the ink splash is painted.
/// * [InkHighlight], which is an ink feature that emphasizes a part of a
/// [Material].
class
InkSplash
extends
InkFeature
{
class
InkSplash
extends
InteractiveInkFeature
{
/// Used to specify this type of ink splash for an [InkWell], [InkResponse]
/// or material [Theme].
static
const
InteractiveInkFeatureFactory
splashFactory
=
const
_InkSplashFactory
();
/// Begin a splash, centered at position relative to [referenceBox].
///
/// The [controller] argument is typically obtained via
...
...
@@ -84,12 +123,11 @@ class InkSplash extends InkFeature {
double
radius
,
VoidCallback
onRemoved
,
})
:
_position
=
position
,
_color
=
color
,
_borderRadius
=
borderRadius
,
_targetRadius
=
radius
??
_getTargetRadius
(
referenceBox
,
containedInkWell
,
rectCallback
,
position
),
_clipCallback
=
_getClipCallback
(
referenceBox
,
containedInkWell
,
rectCallback
),
_repositionToReferenceBox
=
!
containedInkWell
,
super
(
controller:
controller
,
referenceBox:
referenceBox
,
onRemoved:
onRemoved
)
{
super
(
controller:
controller
,
referenceBox:
referenceBox
,
color:
color
,
onRemoved:
onRemoved
)
{
assert
(
_borderRadius
!=
null
);
_radiusController
=
new
AnimationController
(
duration:
_kUnconfirmedSplashDuration
,
vsync:
controller
.
vsync
)
..
addListener
(
controller
.
markNeedsPaint
)
...
...
@@ -121,20 +159,7 @@ class InkSplash extends InkFeature {
Animation
<
int
>
_alpha
;
AnimationController
_alphaController
;
/// The color of the splash.
Color
get
color
=>
_color
;
Color
_color
;
set
color
(
Color
value
)
{
if
(
value
==
_color
)
return
;
_color
=
value
;
controller
.
markNeedsPaint
();
}
/// The user input is confirmed.
///
/// Causes the reaction to propagate faster across the material.
@override
void
confirm
()
{
final
int
duration
=
(
_targetRadius
/
_kSplashConfirmedVelocity
).
floor
();
_radiusController
...
...
@@ -143,9 +168,7 @@ class InkSplash extends InkFeature {
_alphaController
.
forward
();
}
/// The user input was canceled.
///
/// Causes the reaction to gradually disappear.
@override
void
cancel
()
{
_alphaController
.
forward
();
}
...
...
@@ -184,7 +207,7 @@ class InkSplash extends InkFeature {
@override
void
paintFeature
(
Canvas
canvas
,
Matrix4
transform
)
{
final
Paint
paint
=
new
Paint
()..
color
=
_
color
.
withAlpha
(
_alpha
.
value
);
final
Paint
paint
=
new
Paint
()..
color
=
color
.
withAlpha
(
_alpha
.
value
);
Offset
center
=
_position
;
if
(
_repositionToReferenceBox
)
center
=
Offset
.
lerp
(
center
,
referenceBox
.
size
.
center
(
Offset
.
zero
),
_radiusController
.
value
);
...
...
packages/flutter/lib/src/material/ink_well.dart
View file @
03e8ab1f
...
...
@@ -12,10 +12,94 @@ import 'package:flutter/widgets.dart';
import
'debug.dart'
;
import
'feedback.dart'
;
import
'ink_highlight.dart'
;
import
'ink_splash.dart'
;
import
'material.dart'
;
import
'theme.dart'
;
/// An ink feature that displays a [color] "splash" in response to a user
/// gesture that can be confirmed or canceled.
///
/// Subclasses call [confirm] when an input gesture is recognized. For
/// example a press event might trigger an ink feature that's confirmed
/// when the corresponding up event is seen.
///
/// Subclasses call [cancel] when an input gesture is aborted before it
/// is recognized. For example a press event might trigger an ink feature
/// that's cancelled when the pointer is dragged out of the reference
/// box.
///
/// The [InkWell] and [InkResponse] widgets generate instances of this
/// class.
abstract
class
InteractiveInkFeature
extends
InkFeature
{
/// Creates an InteractiveInkFeature.
///
/// The [controller] and [referenceBox] arguments must not be null.
InteractiveInkFeature
({
@required
MaterialInkController
controller
,
@required
RenderBox
referenceBox
,
Color
color
,
VoidCallback
onRemoved
,
})
:
assert
(
controller
!=
null
),
assert
(
referenceBox
!=
null
),
_color
=
color
,
super
(
controller:
controller
,
referenceBox:
referenceBox
,
onRemoved:
onRemoved
);
/// Called when the user input that triggered this feature's appearance was confirmed.
///
/// Typically causes the ink to propagate faster across the material. By default this
/// method does nothing.
void
confirm
()
{
}
/// Called when the user input that triggered this feature's appearance was canceled.
///
/// Typically causes the ink to gradually disappear. By default this method does
/// nothing.
void
cancel
()
{
}
/// The ink's color.
Color
get
color
=>
_color
;
Color
_color
;
set
color
(
Color
value
)
{
if
(
value
==
_color
)
return
;
_color
=
value
;
controller
.
markNeedsPaint
();
}
}
/// An encapsulation of an [InteractiveInkFeature] constructor used by [InkWell]
/// [InkResponse] and [ThemeData].
///
/// Interactive ink feature implementations should provide a static const
/// `splashFactory` value that's an instance of this class. The `splashFactory`
/// can be used to configure an [InkWell], [InkResponse] or [ThemeData].
///
/// See also:
///
/// * [InkSplash.splashFactory]
/// * [InkRipple.splashFactory]
abstract
class
InteractiveInkFeatureFactory
{
/// Subclasses should provide a const constructor.
const
InteractiveInkFeatureFactory
();
/// The factory method.
///
/// Subclasses should override this method to return a new instance of an
/// [InteractiveInkFeature].
InteractiveInkFeature
create
({
@required
MaterialInkController
controller
,
@required
RenderBox
referenceBox
,
@required
Offset
position
,
@required
Color
color
,
bool
containedInkWell:
false
,
RectCallback
rectCallback
,
BorderRadius
borderRadius
,
double
radius
,
VoidCallback
onRemoved
,
});
}
/// An area of a [Material] that responds to touch. Has a configurable shape and
/// can be configured to clip splashes that extend outside its bounds or not.
///
...
...
@@ -95,6 +179,7 @@ class InkResponse extends StatefulWidget {
this
.
borderRadius
:
BorderRadius
.
zero
,
this
.
highlightColor
,
this
.
splashColor
,
this
.
splashFactory
,
this
.
enableFeedback
:
true
,
this
.
excludeFromSemantics
:
false
,
})
:
assert
(
enableFeedback
!=
null
),
super
(
key:
key
);
...
...
@@ -162,6 +247,7 @@ class InkResponse extends StatefulWidget {
/// See also:
///
/// * [splashColor], the color of the splash.
/// * [splashFactory], which defines the appearance of the splash.
final
double
radius
;
/// The clipping radius of the containing rect.
...
...
@@ -174,6 +260,7 @@ class InkResponse extends StatefulWidget {
///
/// * [highlightShape], the shape of the highlight.
/// * [splashColor], the color of the splash.
/// * [splashFactory], which defines the appearance of the splash.
final
Color
highlightColor
;
/// The splash color of the ink response. If this property is null then the
...
...
@@ -181,10 +268,25 @@ class InkResponse extends StatefulWidget {
///
/// See also:
///
/// * [splashFactory], which defines the appearance of the splash.
/// * [radius], the (maximum) size of the ink splash.
/// * [highlightColor], the color of the highlight.
final
Color
splashColor
;
/// Defines the appearance of the splash.
///
/// Defaults to the value of the theme's splash factory: [ThemeData.splashFactory].
///
/// See also:
///
/// * [radius], the (maximum) size of the ink splash.
/// * [splashColor], the color of the splash.
/// * [highlightColor], the color of the highlight.
/// * [InkSplash.splashFactory], which defines the default splash.
/// * [InkRipple.splashFactory], which defines a splash that spreads out
/// more aggresively than the default.
final
InteractiveInkFeatureFactory
splashFactory
;
/// Whether detected gestures should provide acoustic and/or haptic feedback.
///
/// For example, on Android a tap will produce a clicking sound and a
...
...
@@ -255,8 +357,8 @@ class InkResponse extends StatefulWidget {
}
class
_InkResponseState
<
T
extends
InkResponse
>
extends
State
<
T
>
with
AutomaticKeepAliveClientMixin
{
Set
<
In
kSplash
>
_splashes
;
In
kSplash
_currentSplash
;
Set
<
In
teractiveInkFeature
>
_splashes
;
In
teractiveInkFeature
_currentSplash
;
InkHighlight
_lastHighlight
;
@override
...
...
@@ -295,30 +397,43 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
updateKeepAlive
();
}
void
_handleTapDown
(
TapDownDetails
details
)
{
InteractiveInkFeature
_createInkFeature
(
TapDownDetails
details
)
{
final
MaterialInkController
inkController
=
Material
.
of
(
context
);
final
RenderBox
referenceBox
=
context
.
findRenderObject
();
final
RectCallback
rectCallback
=
widget
.
getRectCallback
(
referenceBox
);
InkSplash
splash
;
splash
=
new
InkSplash
(
controller:
Material
.
of
(
context
),
final
Offset
position
=
referenceBox
.
globalToLocal
(
details
.
globalPosition
);
final
Color
color
=
widget
.
splashColor
??
Theme
.
of
(
context
).
splashColor
;
final
RectCallback
rectCallback
=
widget
.
containedInkWell
?
widget
.
getRectCallback
(
referenceBox
)
:
null
;
final
BorderRadius
borderRadius
=
widget
.
borderRadius
??
BorderRadius
.
zero
;
InteractiveInkFeature
splash
;
void
onRemoved
()
{
if
(
_splashes
!=
null
)
{
assert
(
_splashes
.
contains
(
splash
));
_splashes
.
remove
(
splash
);
if
(
_currentSplash
==
splash
)
_currentSplash
=
null
;
updateKeepAlive
();
}
// else we're probably in deactivate()
}
splash
=
(
widget
.
splashFactory
??
Theme
.
of
(
context
).
splashFactory
).
create
(
controller:
inkController
,
referenceBox:
referenceBox
,
position:
referenceBox
.
globalToLocal
(
details
.
globalPosition
)
,
color:
widget
.
splashColor
??
Theme
.
of
(
context
).
splashC
olor
,
position:
position
,
color:
c
olor
,
containedInkWell:
widget
.
containedInkWell
,
rectCallback:
widget
.
containedInkWell
?
rectCallback
:
null
,
rectCallback:
rectCallback
,
radius:
widget
.
radius
,
borderRadius:
widget
.
borderRadius
??
BorderRadius
.
zero
,
onRemoved:
()
{
if
(
_splashes
!=
null
)
{
assert
(
_splashes
.
contains
(
splash
));
_splashes
.
remove
(
splash
);
if
(
_currentSplash
==
splash
)
_currentSplash
=
null
;
updateKeepAlive
();
}
// else we're probably in deactivate()
}
borderRadius:
borderRadius
,
onRemoved:
onRemoved
,
);
_splashes
??=
new
HashSet
<
InkSplash
>();
return
splash
;
}
void
_handleTapDown
(
TapDownDetails
details
)
{
final
InteractiveInkFeature
splash
=
_createInkFeature
(
details
);
_splashes
??=
new
HashSet
<
InteractiveInkFeature
>();
_splashes
.
add
(
splash
);
_currentSplash
=
splash
;
updateKeepAlive
();
...
...
@@ -362,9 +477,9 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
@override
void
deactivate
()
{
if
(
_splashes
!=
null
)
{
final
Set
<
In
kSplash
>
splashes
=
_splashes
;
final
Set
<
In
teractiveInkFeature
>
splashes
=
_splashes
;
_splashes
=
null
;
for
(
In
kSplash
splash
in
splashes
)
for
(
In
teractiveInkFeature
splash
in
splashes
)
splash
.
dispose
();
_currentSplash
=
null
;
}
...
...
@@ -436,6 +551,8 @@ class InkWell extends InkResponse {
ValueChanged
<
bool
>
onHighlightChanged
,
Color
highlightColor
,
Color
splashColor
,
InteractiveInkFeatureFactory
splashFactory
,
double
radius
,
BorderRadius
borderRadius
,
bool
enableFeedback:
true
,
bool
excludeFromSemantics:
false
,
...
...
@@ -450,6 +567,8 @@ class InkWell extends InkResponse {
highlightShape:
BoxShape
.
rectangle
,
highlightColor:
highlightColor
,
splashColor:
splashColor
,
splashFactory:
splashFactory
,
radius:
radius
,
borderRadius:
borderRadius
,
enableFeedback:
enableFeedback
,
excludeFromSemantics:
excludeFromSemantics
,
...
...
packages/flutter/lib/src/material/theme_data.dart
View file @
03e8ab1f
...
...
@@ -8,6 +8,8 @@ import 'package:flutter/foundation.dart';
import
'package:flutter/widgets.dart'
;
import
'colors.dart'
;
import
'ink_splash.dart'
;
import
'ink_well.dart'
show
InteractiveInkFeatureFactory
;
import
'typography.dart'
;
/// Describes the contrast needs of a color.
...
...
@@ -82,6 +84,7 @@ class ThemeData {
Color
dividerColor
,
Color
highlightColor
,
Color
splashColor
,
InteractiveInkFeatureFactory
splashFactory
,
Color
selectedRowColor
,
Color
unselectedWidgetColor
,
Color
disabledColor
,
...
...
@@ -118,6 +121,7 @@ class ThemeData {
dividerColor
??=
isDark
?
const
Color
(
0x1FFFFFFF
)
:
const
Color
(
0x1F000000
);
highlightColor
??=
isDark
?
_kDarkThemeHighlightColor
:
_kLightThemeHighlightColor
;
splashColor
??=
isDark
?
_kDarkThemeSplashColor
:
_kLightThemeSplashColor
;
splashFactory
??=
InkSplash
.
splashFactory
;
selectedRowColor
??=
Colors
.
grey
[
100
];
unselectedWidgetColor
??=
isDark
?
Colors
.
white70
:
Colors
.
black54
;
disabledColor
??=
isDark
?
Colors
.
white30
:
Colors
.
black26
;
...
...
@@ -156,6 +160,7 @@ class ThemeData {
dividerColor:
dividerColor
,
highlightColor:
highlightColor
,
splashColor:
splashColor
,
splashFactory:
splashFactory
,
selectedRowColor:
selectedRowColor
,
unselectedWidgetColor:
unselectedWidgetColor
,
disabledColor:
disabledColor
,
...
...
@@ -196,6 +201,7 @@ class ThemeData {
@required
this
.
dividerColor
,
@required
this
.
highlightColor
,
@required
this
.
splashColor
,
@required
this
.
splashFactory
,
@required
this
.
selectedRowColor
,
@required
this
.
unselectedWidgetColor
,
@required
this
.
disabledColor
,
...
...
@@ -226,6 +232,7 @@ class ThemeData {
assert
(
dividerColor
!=
null
),
assert
(
highlightColor
!=
null
),
assert
(
splashColor
!=
null
),
assert
(
splashFactory
!=
null
),
assert
(
selectedRowColor
!=
null
),
assert
(
unselectedWidgetColor
!=
null
),
assert
(
disabledColor
!=
null
),
...
...
@@ -317,6 +324,16 @@ class ThemeData {
/// The color of ink splashes. See [InkWell].
final
Color
splashColor
;
/// Defines the appearance of ink splashes produces by [InkWell]
/// and [InkResponse].
///
/// See also:
///
/// * [InkSplash.splashFactory], which defines the default splash.
/// * [InkRipple.splashFactory], which defines a splash that spreads out
/// more aggresively than the default.
final
InteractiveInkFeatureFactory
splashFactory
;
/// The color used to highlight selected rows.
final
Color
selectedRowColor
;
...
...
@@ -398,6 +415,7 @@ class ThemeData {
Color
dividerColor
,
Color
highlightColor
,
Color
splashColor
,
InteractiveInkFeatureFactory
splashFactory
,
Color
selectedRowColor
,
Color
unselectedWidgetColor
,
Color
disabledColor
,
...
...
@@ -430,6 +448,7 @@ class ThemeData {
dividerColor:
dividerColor
??
this
.
dividerColor
,
highlightColor:
highlightColor
??
this
.
highlightColor
,
splashColor:
splashColor
??
this
.
splashColor
,
splashFactory:
splashFactory
??
this
.
splashFactory
,
selectedRowColor:
selectedRowColor
??
this
.
selectedRowColor
,
unselectedWidgetColor:
unselectedWidgetColor
??
this
.
unselectedWidgetColor
,
disabledColor:
disabledColor
??
this
.
disabledColor
,
...
...
@@ -545,6 +564,7 @@ class ThemeData {
dividerColor:
Color
.
lerp
(
a
.
dividerColor
,
b
.
dividerColor
,
t
),
highlightColor:
Color
.
lerp
(
a
.
highlightColor
,
b
.
highlightColor
,
t
),
splashColor:
Color
.
lerp
(
a
.
splashColor
,
b
.
splashColor
,
t
),
splashFactory:
t
<
0.5
?
a
.
splashFactory
:
b
.
splashFactory
,
selectedRowColor:
Color
.
lerp
(
a
.
selectedRowColor
,
b
.
selectedRowColor
,
t
),
unselectedWidgetColor:
Color
.
lerp
(
a
.
unselectedWidgetColor
,
b
.
unselectedWidgetColor
,
t
),
disabledColor:
Color
.
lerp
(
a
.
disabledColor
,
b
.
disabledColor
,
t
),
...
...
@@ -583,6 +603,7 @@ class ThemeData {
(
otherData
.
dividerColor
==
dividerColor
)
&&
(
otherData
.
highlightColor
==
highlightColor
)
&&
(
otherData
.
splashColor
==
splashColor
)
&&
(
otherData
.
splashFactory
==
splashFactory
)
&&
(
otherData
.
selectedRowColor
==
selectedRowColor
)
&&
(
otherData
.
unselectedWidgetColor
==
unselectedWidgetColor
)
&&
(
otherData
.
disabledColor
==
disabledColor
)
&&
...
...
@@ -618,6 +639,7 @@ class ThemeData {
dividerColor
,
highlightColor
,
splashColor
,
splashFactory
,
selectedRowColor
,
unselectedWidgetColor
,
disabledColor
,
...
...
@@ -627,8 +649,8 @@ class ThemeData {
textSelectionHandleColor
,
backgroundColor
,
accentColor
,
accentColorBrightness
,
hashValues
(
// Too many values.
accentColorBrightness
,
indicatorColor
,
dialogBackgroundColor
,
hintColor
,
...
...
packages/flutter/test/material/ink_paint_test.dart
View file @
03e8ab1f
...
...
@@ -9,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart';
import
'../rendering/mock_canvas.dart'
;
void
main
(
)
{
testWidgets
(
'
Does the ink widget render a border radius
'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'
The inkwell widget renders an ink splash
'
,
(
WidgetTester
tester
)
async
{
final
Color
highlightColor
=
const
Color
(
0xAAFF0000
);
final
Color
splashColor
=
const
Color
(
0xAA0000FF
);
final
BorderRadius
borderRadius
=
new
BorderRadius
.
circular
(
6.0
);
...
...
@@ -50,4 +50,128 @@ void main() {
await
gesture
.
up
();
});
testWidgets
(
'The inkwell widget renders an ink ripple'
,
(
WidgetTester
tester
)
async
{
final
Color
highlightColor
=
const
Color
(
0xAAFF0000
);
final
Color
splashColor
=
const
Color
(
0xB40000FF
);
final
BorderRadius
borderRadius
=
new
BorderRadius
.
circular
(
6.0
);
await
tester
.
pumpWidget
(
new
Material
(
child:
new
Center
(
child:
new
Container
(
width:
100.0
,
height:
100.0
,
child:
new
InkWell
(
borderRadius:
borderRadius
,
highlightColor:
highlightColor
,
splashColor:
splashColor
,
onTap:
()
{
},
radius:
100.0
,
splashFactory:
InkRipple
.
splashFactory
,
),
),
),
),
);
final
Offset
tapDownOffset
=
tester
.
getTopLeft
(
find
.
byType
(
InkWell
));
final
Offset
inkWellCenter
=
tester
.
getCenter
(
find
.
byType
(
InkWell
));
//final TestGesture gesture = await tester.startGesture(tapDownOffset);
await
tester
.
tapAt
(
tapDownOffset
);
await
tester
.
pump
();
// start gesture
final
RenderBox
box
=
Material
.
of
(
tester
.
element
(
find
.
byType
(
InkWell
)))
as
dynamic
;
bool
offsetsAreClose
(
Offset
a
,
Offset
b
)
=>
(
a
-
b
).
distance
<
1.0
;
bool
radiiAreClose
(
double
a
,
double
b
)
=>
(
a
-
b
).
abs
()
<
1.0
;
// Initially the ripple's center is where the tap occurred,
expect
(
box
,
paints
..
something
((
Symbol
method
,
List
<
dynamic
>
arguments
)
{
if
(
method
!=
#drawCircle
)
return
false
;
final
Offset
center
=
arguments
[
0
];
final
double
radius
=
arguments
[
1
];
final
Paint
paint
=
arguments
[
2
];
if
(
offsetsAreClose
(
center
,
tapDownOffset
)
&&
radius
==
30.0
&&
paint
.
color
.
alpha
==
0
)
return
true
;
throw
'''
Expected: center ==
$tapDownOffset
, radius == 30.0, alpha == 0
Found: center ==
$center
radius ==
$radius
alpha ==
${paint.color.alpha}
'''
;
}));
// The ripple fades in for 75ms. During that time its alpha is eased from
// 0 to the splashColor's alpha value and its center moves towards the
// center of the ink well.
await
tester
.
pump
(
const
Duration
(
milliseconds:
50
));
expect
(
box
,
paints
..
something
((
Symbol
method
,
List
<
dynamic
>
arguments
)
{
if
(
method
!=
#drawCircle
)
return
false
;
final
Offset
center
=
arguments
[
0
];
final
double
radius
=
arguments
[
1
];
final
Paint
paint
=
arguments
[
2
];
final
Offset
expectedCenter
=
tapDownOffset
+
const
Offset
(
17.0
,
17.0
);
final
double
expectedRadius
=
56.0
;
if
(
offsetsAreClose
(
center
,
expectedCenter
)
&&
radiiAreClose
(
radius
,
expectedRadius
)
&&
paint
.
color
.
alpha
==
120
)
return
true
;
throw
'''
Expected: center ==
$expectedCenter
, radius ==
$expectedRadius
, alpha == 120
Found: center ==
$center
radius ==
$radius
alpha ==
${paint.color.alpha}
'''
;
}));
// At 75ms the ripple has fade in: it's alpha matches the splashColor's
// alpha and its center has moved closer to the ink well's center.
await
tester
.
pump
(
const
Duration
(
milliseconds:
25
));
expect
(
box
,
paints
..
something
((
Symbol
method
,
List
<
dynamic
>
arguments
)
{
if
(
method
!=
#drawCircle
)
return
false
;
final
Offset
center
=
arguments
[
0
];
final
double
radius
=
arguments
[
1
];
final
Paint
paint
=
arguments
[
2
];
final
Offset
expectedCenter
=
tapDownOffset
+
const
Offset
(
29.0
,
29.0
);
final
double
expectedRadius
=
73.0
;
if
(
offsetsAreClose
(
center
,
expectedCenter
)
&&
radiiAreClose
(
radius
,
expectedRadius
)
&&
paint
.
color
.
alpha
==
180
)
return
true
;
throw
'''
Expected: center ==
$expectedCenter
, radius ==
$expectedRadius
, alpha == 180
Found: center ==
$center
radius ==
$radius
alpha ==
${paint.color.alpha}
'''
;
}));
// At this point the splash radius has expanded to its limit: 5 past the
// ink well's radius parameter. The splash center has moved to its final
// location at the inkwell's center and the fade-out is about to start.
await
tester
.
pump
(
const
Duration
(
milliseconds:
225
));
expect
(
box
,
paints
..
something
((
Symbol
method
,
List
<
dynamic
>
arguments
)
{
if
(
method
!=
#drawCircle
)
return
false
;
final
Offset
center
=
arguments
[
0
];
final
double
radius
=
arguments
[
1
];
final
Paint
paint
=
arguments
[
2
];
final
Offset
expectedCenter
=
inkWellCenter
;
final
double
expectedRadius
=
105.0
;
if
(
offsetsAreClose
(
center
,
expectedCenter
)
&&
radiiAreClose
(
radius
,
expectedRadius
)
&&
paint
.
color
.
alpha
==
180
)
return
true
;
throw
'''
Expected: center ==
$expectedCenter
, radius ==
$expectedRadius
, alpha == 180
Found: center ==
$center
radius ==
$radius
alpha ==
${paint.color.alpha}
'''
;
}));
// After another 150ms the fade-out is complete.
await
tester
.
pump
(
const
Duration
(
milliseconds:
150
));
expect
(
box
,
paints
..
something
((
Symbol
method
,
List
<
dynamic
>
arguments
)
{
if
(
method
!=
#drawCircle
)
return
false
;
final
Offset
center
=
arguments
[
0
];
final
double
radius
=
arguments
[
1
];
final
Paint
paint
=
arguments
[
2
];
final
Offset
expectedCenter
=
inkWellCenter
;
final
double
expectedRadius
=
105.0
;
if
(
offsetsAreClose
(
center
,
expectedCenter
)
&&
radiiAreClose
(
radius
,
expectedRadius
)
&&
paint
.
color
.
alpha
==
0
)
return
true
;
throw
'''
Expected: center ==
$expectedCenter
, radius ==
$expectedRadius
, alpha == 0
Found: center ==
$center
radius ==
$radius
alpha ==
${paint.color.alpha}
'''
;
}));
});
}
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