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
c4ae13ed
Commit
c4ae13ed
authored
Apr 16, 2016
by
Hans Muller
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refresh indicator (#3354)
parent
9ce995f6
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
537 additions
and
40 deletions
+537
-40
overscroll_demo.dart
examples/material_gallery/lib/demo/overscroll_demo.dart
+93
-0
home.dart
examples/material_gallery/lib/gallery/home.dart
+2
-0
material.dart
packages/flutter/lib/material.dart
+1
-0
overscroll_indicator.dart
packages/flutter/lib/src/material/overscroll_indicator.dart
+1
-0
progress_indicator.dart
packages/flutter/lib/src/material/progress_indicator.dart
+148
-34
refresh_indicator.dart
packages/flutter/lib/src/material/refresh_indicator.dart
+242
-0
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+1
-1
transitions.dart
packages/flutter/lib/src/widgets/transitions.dart
+1
-1
refresh_indicator_test.dart
packages/flutter/test/material/refresh_indicator_test.dart
+44
-0
widget_tester.dart
packages/flutter_test/lib/src/widget_tester.dart
+4
-4
No files found.
examples/material_gallery/lib/demo/overscroll_demo.dart
0 → 100644
View file @
c4ae13ed
// Copyright 2016 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:async'
;
import
'package:flutter/material.dart'
;
enum
IndicatorType
{
overscroll
,
refresh
}
class
OverscrollDemo
extends
StatefulWidget
{
OverscrollDemo
({
Key
key
})
:
super
(
key:
key
);
@override
OverscrollDemoState
createState
()
=>
new
OverscrollDemoState
();
}
class
OverscrollDemoState
extends
State
<
OverscrollDemo
>
{
static
final
List
<
String
>
_items
=
<
String
>[
'A'
,
'B'
,
'C'
,
'D'
,
'E'
,
'F'
,
'G'
,
'H'
,
'I'
,
'J'
,
'K'
,
'L'
,
'M'
,
'N'
];
IndicatorType
_type
=
IndicatorType
.
refresh
;
Future
<
Null
>
refresh
()
{
Completer
<
Null
>
completer
=
new
Completer
<
Null
>();
new
Timer
(
new
Duration
(
seconds:
3
),
()
{
completer
.
complete
(
null
);
});
return
completer
.
future
;
}
@override
Widget
build
(
BuildContext
context
)
{
String
indicatorTypeText
;
switch
(
_type
)
{
case
IndicatorType
.
overscroll
:
indicatorTypeText
=
'Over-scroll indicator'
;
break
;
case
IndicatorType
.
refresh
:
indicatorTypeText
=
'Refresh indicator'
;
break
;
}
Widget
body
=
new
MaterialList
(
type:
MaterialListType
.
threeLine
,
padding:
const
EdgeInsets
.
all
(
8.0
),
children:
_items
.
map
((
String
item
)
{
return
new
ListItem
(
isThreeLine:
true
,
leading:
new
CircleAvatar
(
child:
new
Text
(
item
)),
title:
new
Text
(
'This item represents
$item
.'
),
subtitle:
new
Text
(
'Even more additional list item information appears on line three.'
)
);
})
);
switch
(
_type
)
{
case
IndicatorType
.
overscroll
:
body
=
new
OverscrollIndicator
(
child:
body
);
break
;
case
IndicatorType
.
refresh
:
body
=
new
RefreshIndicator
(
child:
body
,
refresh:
refresh
);
break
;
}
return
new
Scaffold
(
appBar:
new
AppBar
(
title:
new
Text
(
'
$indicatorTypeText
'
),
actions:
<
Widget
>[
new
IconButton
(
icon:
Icons
.
refresh
,
tooltip:
'Pull to refresh'
,
onPressed:
()
{
setState
(()
{
_type
=
IndicatorType
.
refresh
;
});
}
),
new
IconButton
(
icon:
Icons
.
play_for_work
,
tooltip:
'Over-scroll indicator'
,
onPressed:
()
{
setState
(()
{
_type
=
IndicatorType
.
overscroll
;
});
}
)
]
),
body:
body
);
}
}
examples/material_gallery/lib/gallery/home.dart
View file @
c4ae13ed
...
...
@@ -26,6 +26,7 @@ import '../demo/leave_behind_demo.dart';
import
'../demo/list_demo.dart'
;
import
'../demo/modal_bottom_sheet_demo.dart'
;
import
'../demo/menu_demo.dart'
;
import
'../demo/overscroll_demo.dart'
;
import
'../demo/page_selector_demo.dart'
;
import
'../demo/persistent_bottom_sheet_demo.dart'
;
import
'../demo/progress_indicator_demo.dart'
;
...
...
@@ -133,6 +134,7 @@ class GalleryHomeState extends State<GalleryHome> {
new
GalleryItem
(
title:
'List'
,
builder:
()
=>
new
ListDemo
()),
new
GalleryItem
(
title:
'Menus'
,
builder:
()
=>
new
MenuDemo
()),
new
GalleryItem
(
title:
'Modal bottom sheet'
,
builder:
()
=>
new
ModalBottomSheetDemo
()),
new
GalleryItem
(
title:
'Over-scroll'
,
builder:
()
=>
new
OverscrollDemo
()),
new
GalleryItem
(
title:
'Page selector'
,
builder:
()
=>
new
PageSelectorDemo
()),
new
GalleryItem
(
title:
'Persistent bottom sheet'
,
builder:
()
=>
new
PersistentBottomSheetDemo
()),
new
GalleryItem
(
title:
'Progress indicators'
,
builder:
()
=>
new
ProgressIndicatorDemo
()),
...
...
packages/flutter/lib/material.dart
View file @
c4ae13ed
...
...
@@ -49,6 +49,7 @@ export 'src/material/popup_menu.dart';
export
'src/material/progress_indicator.dart'
;
export
'src/material/radio.dart'
;
export
'src/material/raised_button.dart'
;
export
'src/material/refresh_indicator.dart'
;
export
'src/material/scaffold.dart'
;
export
'src/material/scrollbar.dart'
;
export
'src/material/shadows.dart'
;
...
...
packages/flutter/lib/src/material/overscroll_indicator.dart
View file @
c4ae13ed
...
...
@@ -174,6 +174,7 @@ class _OverscrollIndicatorState extends State<OverscrollIndicator> {
void
dispose
()
{
_hideTimer
?.
cancel
();
_hideTimer
=
null
;
_extentAnimation
.
dispose
();
super
.
dispose
();
}
...
...
packages/flutter/lib/src/material/progress_indicator.dart
View file @
c4ae13ed
...
...
@@ -6,6 +6,7 @@ import 'dart:math' as math;
import
'package:flutter/widgets.dart'
;
import
'material.dart'
;
import
'theme.dart'
;
const
double
_kLinearProgressIndicatorHeight
=
6.0
;
...
...
@@ -31,7 +32,9 @@ abstract class ProgressIndicator extends StatefulWidget {
/// indicator). See [value] for details.
ProgressIndicator
({
Key
key
,
this
.
value
this
.
value
,
this
.
backgroundColor
,
this
.
valueColor
})
:
super
(
key:
key
);
/// If non-null, the value of this progress indicator with 0.0 corresponding
...
...
@@ -43,8 +46,18 @@ abstract class ProgressIndicator extends StatefulWidget {
/// much actual progress is being made.
final
double
value
;
Color
_getBackgroundColor
(
BuildContext
context
)
=>
Theme
.
of
(
context
).
backgroundColor
;
Color
_getValueColor
(
BuildContext
context
)
=>
Theme
.
of
(
context
).
primaryColor
;
/// The progress indicator's background color. If null, the background color is
/// the current theme's backgroundColor.
final
Color
backgroundColor
;
/// The indicator's color is the animation's value. To specify a constant
/// color use: `new AlwaysStoppedAnimation<Color>(color)`.
///
/// If null, the progress indicator is rendered with the current theme's primaryColor.
final
Animation
<
Color
>
valueColor
;
Color
_getBackgroundColor
(
BuildContext
context
)
=>
backgroundColor
??
Theme
.
of
(
context
).
backgroundColor
;
Color
_getValueColor
(
BuildContext
context
)
=>
valueColor
?.
value
??
Theme
.
of
(
context
).
primaryColor
;
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
...
...
@@ -143,7 +156,7 @@ class _LinearProgressIndicatorState extends State<LinearProgressIndicator> {
@override
void
dispose
()
{
_controller
.
stop
();
_controller
.
dispose
();
super
.
dispose
();
}
...
...
@@ -185,14 +198,25 @@ class _CircularProgressIndicatorPainter extends CustomPainter {
static
const
double
_kSweep
=
_kTwoPI
-
_kEpsilon
;
static
const
double
_kStartAngle
=
-
math
.
PI
/
2.0
;
const
_CircularProgressIndicatorPainter
({
_CircularProgressIndicatorPainter
({
this
.
valueColor
,
this
.
value
,
this
.
headValue
,
this
.
tailValue
,
this
.
stepValue
,
this
.
rotationValue
});
double
value
,
double
headValue
,
double
tailValue
,
int
stepValue
,
double
rotationValue
,
this
.
strokeWidth
})
:
this
.
value
=
value
,
this
.
headValue
=
headValue
,
this
.
tailValue
=
tailValue
,
this
.
stepValue
=
stepValue
,
this
.
rotationValue
=
rotationValue
,
arcStart
=
value
!=
null
?
_kStartAngle
:
_kStartAngle
+
tailValue
*
3
/
2
*
math
.
PI
+
rotationValue
*
math
.
PI
*
1.7
-
stepValue
*
0.8
*
math
.
PI
,
arcSweep
=
value
!=
null
?
value
.
clamp
(
0.0
,
1.0
)
*
_kSweep
:
math
.
max
(
headValue
*
3
/
2
*
math
.
PI
-
tailValue
*
3
/
2
*
math
.
PI
,
_kEpsilon
);
final
Color
valueColor
;
final
double
value
;
...
...
@@ -200,33 +224,23 @@ class _CircularProgressIndicatorPainter extends CustomPainter {
final
double
tailValue
;
final
int
stepValue
;
final
double
rotationValue
;
final
double
strokeWidth
;
final
double
arcStart
;
final
double
arcSweep
;
@override
void
paint
(
Canvas
canvas
,
Size
size
)
{
Paint
paint
=
new
Paint
()
..
color
=
valueColor
..
strokeWidth
=
_kCircularProgressIndicatorS
trokeWidth
..
strokeWidth
=
s
trokeWidth
..
style
=
PaintingStyle
.
stroke
;
if
(
value
!=
null
)
{
// Determinate
double
angle
=
value
.
clamp
(
0.0
,
1.0
)
*
_kSweep
;
Path
path
=
new
Path
()
..
arcTo
(
Point
.
origin
&
size
,
_kStartAngle
,
angle
,
false
);
canvas
.
drawPath
(
path
,
paint
);
}
else
{
// Indeterminate
if
(
value
==
null
)
// Indeterminate
paint
.
strokeCap
=
StrokeCap
.
square
;
double
arcSweep
=
math
.
max
(
headValue
*
3
/
2
*
math
.
PI
-
tailValue
*
3
/
2
*
math
.
PI
,
_kEpsilon
);
Path
path
=
new
Path
()
..
arcTo
(
Point
.
origin
&
size
,
_kStartAngle
+
tailValue
*
3
/
2
*
math
.
PI
+
rotationValue
*
math
.
PI
*
1.7
-
stepValue
*
0.8
*
math
.
PI
,
arcSweep
,
false
);
canvas
.
drawPath
(
path
,
paint
);
}
Path
path
=
new
Path
()
..
arcTo
(
Point
.
origin
&
size
,
arcStart
,
arcSweep
,
false
);
canvas
.
drawPath
(
path
,
paint
);
}
@override
...
...
@@ -236,7 +250,8 @@ class _CircularProgressIndicatorPainter extends CustomPainter {
||
oldPainter
.
headValue
!=
headValue
||
oldPainter
.
tailValue
!=
tailValue
||
oldPainter
.
stepValue
!=
stepValue
||
oldPainter
.
rotationValue
!=
rotationValue
;
||
oldPainter
.
rotationValue
!=
rotationValue
||
oldPainter
.
strokeWidth
!=
strokeWidth
;
}
}
...
...
@@ -266,8 +281,10 @@ class CircularProgressIndicator extends ProgressIndicator {
/// indicator). See [value] for details.
CircularProgressIndicator
({
Key
key
,
double
value
})
:
super
(
key:
key
,
value:
value
);
double
value
,
Color
backgroundColor
,
Animation
<
Color
>
valueColor
})
:
super
(
key:
key
,
value:
value
,
backgroundColor:
backgroundColor
,
valueColor:
valueColor
);
@override
_CircularProgressIndicatorState
createState
()
=>
new
_CircularProgressIndicatorState
();
...
...
@@ -303,7 +320,7 @@ class _CircularProgressIndicatorState extends State<CircularProgressIndicator> {
@override
void
dispose
()
{
_controller
.
stop
();
_controller
.
dispose
();
super
.
dispose
();
}
...
...
@@ -320,7 +337,8 @@ class _CircularProgressIndicatorState extends State<CircularProgressIndicator> {
headValue:
headValue
,
// remaining arguments are ignored if config.value is not null
tailValue:
tailValue
,
stepValue:
stepValue
,
rotationValue:
rotationValue
rotationValue:
rotationValue
,
strokeWidth:
_kCircularProgressIndicatorStrokeWidth
)
)
);
...
...
@@ -345,3 +363,99 @@ class _CircularProgressIndicatorState extends State<CircularProgressIndicator> {
);
}
}
class
_RefreshProgressIndicatorPainter
extends
_CircularProgressIndicatorPainter
{
_RefreshProgressIndicatorPainter
({
Color
valueColor
,
double
value
,
double
headValue
,
double
tailValue
,
int
stepValue
,
double
rotationValue
,
double
strokeWidth
})
:
super
(
valueColor:
valueColor
,
value:
value
,
headValue:
headValue
,
tailValue:
tailValue
,
stepValue:
stepValue
,
rotationValue:
rotationValue
,
strokeWidth:
strokeWidth
);
void
paintArrowhead
(
Canvas
canvas
,
Size
size
)
{
// ux, uy: a unit vector whose direction parallels the base of the arrowhead.
// Note that -ux, uy points in the direction the arrowhead points.
final
double
arcEnd
=
arcStart
+
arcSweep
;
final
double
ux
=
math
.
cos
(
arcEnd
);
final
double
uy
=
math
.
sin
(
arcEnd
);
assert
(
size
.
width
==
size
.
height
);
final
double
radius
=
size
.
width
/
2.0
;
final
double
arrowHeadRadius
=
strokeWidth
*
1.5
;
final
double
innerRadius
=
radius
-
arrowHeadRadius
;
final
double
outerRadius
=
radius
+
arrowHeadRadius
;
Path
path
=
new
Path
()
..
moveTo
(
radius
+
ux
*
innerRadius
,
radius
+
uy
*
innerRadius
)
..
lineTo
(
radius
+
ux
*
outerRadius
,
radius
+
uy
*
outerRadius
)
..
lineTo
(
radius
+
ux
*
radius
+
-
uy
*
strokeWidth
*
2.0
,
radius
+
uy
*
radius
+
ux
*
strokeWidth
*
2.0
)
..
close
();
Paint
paint
=
new
Paint
()
..
color
=
valueColor
..
strokeWidth
=
strokeWidth
..
style
=
PaintingStyle
.
fill
;
canvas
.
drawPath
(
path
,
paint
);
}
@override
void
paint
(
Canvas
canvas
,
Size
size
)
{
super
.
paint
(
canvas
,
size
);
paintArrowhead
(
canvas
,
size
);
}
}
class
RefreshProgressIndicator
extends
CircularProgressIndicator
{
RefreshProgressIndicator
({
Key
key
,
double
value
,
Color
backgroundColor
,
Animation
<
Color
>
valueColor
})
:
super
(
key:
key
,
value:
value
,
backgroundColor:
backgroundColor
,
valueColor:
valueColor
);
@override
_RefreshProgressIndicatorState
createState
()
=>
new
_RefreshProgressIndicatorState
();
}
class
_RefreshProgressIndicatorState
extends
_CircularProgressIndicatorState
{
static
double
_kIndicatorSize
=
40.0
;
@override
Widget
_buildIndicator
(
BuildContext
context
,
double
headValue
,
double
tailValue
,
int
stepValue
,
double
rotationValue
)
{
return
new
Container
(
width:
_kIndicatorSize
,
height:
_kIndicatorSize
,
margin:
const
EdgeInsets
.
all
(
4.0
),
// acommodate the shadow
child:
new
Material
(
type:
MaterialType
.
circle
,
color:
Theme
.
of
(
context
).
canvasColor
,
elevation:
2
,
child:
new
Padding
(
padding:
const
EdgeInsets
.
all
(
12.0
),
child:
new
CustomPaint
(
painter:
new
_RefreshProgressIndicatorPainter
(
valueColor:
config
.
_getValueColor
(
context
),
value:
config
.
value
,
// may be null
headValue:
headValue
,
// remaining arguments are ignored if config.value is not null
tailValue:
tailValue
,
stepValue:
stepValue
,
rotationValue:
rotationValue
,
strokeWidth:
2.0
)
)
)
)
);
}
}
packages/flutter/lib/src/material/refresh_indicator.dart
0 → 100644
View file @
c4ae13ed
// Copyright 2016 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:async'
;
import
'package:flutter/widgets.dart'
;
import
'theme.dart'
;
import
'progress_indicator.dart'
;
// The over-scroll distance that moves the indicator to its maximum
// displacement, as a percentage of the scrollable's container extent.
const
double
_kDragContainerExtentPercentage
=
0.25
;
// How much the scroll's drag gesture can overshoot the RefreshIndicator's
// displacement; max displacement = _kDragSizeFactorLimit * displacement.
const
double
_kDragSizeFactorLimit
=
1.5
;
// How far the indicator must be dragged to trigger the refresh callback.
const
double
_kDragThresholdFactor
=
0.75
;
// When the scroll ends, the duration of the refresh indicator's animation
// to the RefreshIndicator's displacment.
const
Duration
_kIndicatorSnapDuration
=
const
Duration
(
milliseconds:
150
);
// The duration of the ScaleTransition that starts when the refresh action
// has completed.
const
Duration
_kIndicatorScaleDuration
=
const
Duration
(
milliseconds:
200
);
/// The signature for a function that's called when the user has dragged the
/// refresh indicator far enough to demonstrate that they want the app to
/// refresh. The returned Future must complete when the refresh operation
/// is finished.
typedef
Future
<
Null
>
RefreshCallback
();
/// Where the refresh indicator appears: top for over-scrolls at the
/// start of the scrollable, bottom for over-scrolls at the end.
enum
RefreshIndicatorLocation
{
top
,
bottom
}
/// A widget that supports the Material "swipe to refresh" idiom.
///
/// When the child's vertical Scrollable descendant overscrolls, an
/// animated circular progress indicator is faded into view. When the scroll
/// ends, if the indicator has been dragged far enough for it to become
/// completely opaque, the refresh callback is called. The callback is
/// expected to udpate the scrollback and then complete the Future it
/// returns. The refresh indicator disappears after the callback's
/// Future has completed.
///
/// See also:
///
/// * <https://www.google.com/design/spec/patterns/swipe-to-refresh.html>
class
RefreshIndicator
extends
StatefulWidget
{
RefreshIndicator
({
Key
key
,
this
.
scrollableKey
,
this
.
child
,
this
.
displacement
:
40.0
,
this
.
refresh
})
:
super
(
key:
key
)
{
assert
(
child
!=
null
);
assert
(
refresh
!=
null
);
}
/// Identifies the [Scrollable] descendant of child that will cause the
/// refresh indicator to appear. Can be null if there's only one
/// Scrollable descendant.
final
Key
scrollableKey
;
/// The distance from the child's top or bottom edge to where the refresh indicator
/// will settle. During the drag that exposes the refresh indicator, its actual
/// displacement may significantly exceed this value.
final
double
displacement
;
/// A function that's called when the user has dragged the refresh indicator
/// far enough to demonstrate that they want the app to refresh. The returned
/// Future must complete when the refresh operation is finished.
final
RefreshCallback
refresh
;
/// The refresh indicator will be stacked on top of this child. The indicator
/// will appear when child's Scrollable descendant is over-scrolled.
final
Widget
child
;
@override
_RefreshIndicatorState
createState
()
=>
new
_RefreshIndicatorState
();
}
class
_RefreshIndicatorState
extends
State
<
RefreshIndicator
>
{
final
AnimationController
_sizeController
=
new
AnimationController
();
final
AnimationController
_scaleController
=
new
AnimationController
();
Animation
<
double
>
_sizeFactor
;
Animation
<
double
>
_scaleFactor
;
Animation
<
Color
>
_valueColor
;
double
_scrollOffset
;
double
_containerExtent
;
double
_minScrollOffset
;
double
_maxScrollOffset
;
RefreshIndicatorLocation
_location
=
RefreshIndicatorLocation
.
top
;
@override
void
initState
()
{
super
.
initState
();
_sizeFactor
=
new
Tween
<
double
>(
begin:
0.0
,
end:
_kDragSizeFactorLimit
).
animate
(
_sizeController
);
_scaleFactor
=
new
Tween
<
double
>(
begin:
1.0
,
end:
0.0
).
animate
(
_scaleController
);
final
ThemeData
theme
=
Theme
.
of
(
context
);
// Fully opaque when we've reached config.displacement.
_valueColor
=
new
ColorTween
(
begin:
theme
.
primaryColor
.
withOpacity
(
0.0
),
end:
theme
.
primaryColor
.
withOpacity
(
1.0
)
)
.
animate
(
new
CurvedAnimation
(
parent:
_sizeController
,
curve:
new
Interval
(
0.0
,
1.0
/
_kDragSizeFactorLimit
)
));
}
@override
void
dispose
()
{
_sizeController
.
dispose
();
_scaleController
.
dispose
();
super
.
dispose
();
}
void
_updateState
(
ScrollableState
scrollable
)
{
final
Axis
axis
=
scrollable
.
config
.
scrollDirection
;
if
(
axis
!=
Axis
.
vertical
||
scrollable
.
scrollBehavior
is
!
ExtentScrollBehavior
)
return
;
final
ExtentScrollBehavior
scrollBehavior
=
scrollable
.
scrollBehavior
;
_scrollOffset
=
scrollable
.
scrollOffset
;
_containerExtent
=
scrollBehavior
.
containerExtent
;
_minScrollOffset
=
scrollBehavior
.
minScrollOffset
;
_maxScrollOffset
=
scrollBehavior
.
maxScrollOffset
;
}
void
_onScrollStarted
(
ScrollableState
scrollable
)
{
_updateState
(
scrollable
);
_scaleController
.
value
=
0.0
;
_sizeController
.
value
=
0.0
;
}
RefreshIndicatorLocation
get
_locationForScrollOffset
{
return
_scrollOffset
<
_minScrollOffset
?
RefreshIndicatorLocation
.
top
:
RefreshIndicatorLocation
.
bottom
;
}
void
_onScrollUpdated
(
ScrollableState
scrollable
)
{
final
double
value
=
scrollable
.
scrollOffset
;
if
((
value
<
_minScrollOffset
||
value
>
_maxScrollOffset
)
&&
((
value
-
_scrollOffset
).
abs
()
>
kPixelScrollTolerance
.
distance
))
{
final
double
overScroll
=
value
<
_minScrollOffset
?
_minScrollOffset
-
value
:
value
-
_maxScrollOffset
;
final
double
newValue
=
overScroll
/
(
_containerExtent
*
_kDragContainerExtentPercentage
);
if
(
newValue
>
_sizeController
.
value
)
{
_sizeController
.
value
=
newValue
;
if
(
_location
!=
_locationForScrollOffset
)
{
setState
(()
{
_location
=
_locationForScrollOffset
;
});
}
}
}
_updateState
(
scrollable
);
}
Future
<
Null
>
_doOnScrollEnded
(
ScrollableState
scrollable
)
async
{
if
(
_valueColor
.
value
.
alpha
==
0xFF
)
{
await
_sizeController
.
animateTo
(
1.0
/
_kDragSizeFactorLimit
,
duration:
_kIndicatorSnapDuration
);
await
config
.
refresh
();
}
return
_scaleController
.
animateTo
(
1.0
,
duration:
_kIndicatorScaleDuration
);
}
void
_onScrollEnded
(
ScrollableState
scrollable
)
{
_doOnScrollEnded
(
scrollable
);
}
bool
_handleScrollNotification
(
ScrollNotification
notification
)
{
if
(
config
.
scrollableKey
==
null
||
config
.
scrollableKey
==
notification
.
scrollable
.
config
.
key
)
{
final
ScrollableState
scrollable
=
notification
.
scrollable
;
if
(
scrollable
.
config
.
scrollDirection
!=
Axis
.
vertical
)
return
false
;
switch
(
notification
.
kind
)
{
case
ScrollNotificationKind
.
started
:
_onScrollStarted
(
scrollable
);
break
;
case
ScrollNotificationKind
.
updated
:
_onScrollUpdated
(
scrollable
);
break
;
case
ScrollNotificationKind
.
ended
:
_onScrollEnded
(
scrollable
);
break
;
}
}
return
false
;
}
@override
Widget
build
(
BuildContext
context
)
{
final
bool
isAtTop
=
_location
==
RefreshIndicatorLocation
.
top
;
return
new
NotificationListener
<
ScrollNotification
>(
onNotification:
_handleScrollNotification
,
child:
new
Stack
(
children:
<
Widget
>[
new
ClampOverscrolls
(
child:
config
.
child
,
value:
true
),
new
Positioned
(
top:
isAtTop
?
0.0
:
null
,
bottom:
isAtTop
?
null
:
0.0
,
left:
0.0
,
right:
0.0
,
child:
new
SizeTransition
(
axisAlignment:
isAtTop
?
1.0
:
0.0
,
sizeFactor:
_sizeFactor
,
child:
new
Container
(
padding:
isAtTop
?
new
EdgeInsets
.
only
(
top:
config
.
displacement
)
:
new
EdgeInsets
.
only
(
bottom:
config
.
displacement
),
child:
new
Align
(
alignment:
isAtTop
?
FractionalOffset
.
bottomCenter
:
FractionalOffset
.
topCenter
,
child:
new
ScaleTransition
(
scale:
_scaleFactor
,
child:
new
RefreshProgressIndicator
(
value:
null
,
valueColor:
_valueColor
)
)
)
)
)
)
]
)
);
}
}
packages/flutter/lib/src/widgets/basic.dart
View file @
c4ae13ed
...
...
@@ -1242,7 +1242,7 @@ class Stack extends StackRenderObjectWidgetBase {
}
}
/// A [Stack] that shows a single child
at once
.
/// A [Stack] that shows a single child
from a list of children
.
class
IndexedStack
extends
StackRenderObjectWidgetBase
{
IndexedStack
({
Key
key
,
...
...
packages/flutter/lib/src/widgets/transitions.dart
View file @
c4ae13ed
...
...
@@ -183,7 +183,7 @@ class RotationTransition extends AnimatedWidget {
}
}
/// Animates
a widget's width or height
.
/// Animates
its own size and clips and aligns the child
.
class
SizeTransition
extends
AnimatedWidget
{
SizeTransition
({
Key
key
,
...
...
packages/flutter/test/material/refresh_indicator_test.dart
0 → 100644
View file @
c4ae13ed
// 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
'dart:async'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/material.dart'
;
import
'package:test/test.dart'
;
void
main
(
)
{
bool
refreshCalled
=
false
;
Future
<
Null
>
refresh
()
{
refreshCalled
=
true
;
return
new
Future
<
Null
>.
value
();
}
test
(
'RefreshIndicator'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
tester
.
pumpWidget
(
new
RefreshIndicator
(
refresh:
refresh
,
child:
new
Block
(
children:
<
String
>[
'A'
,
'B'
,
'C'
,
'D'
,
'E'
,
'F'
].
map
((
String
item
)
{
return
new
SizedBox
(
height:
200.0
,
child:
new
Text
(
item
)
);
}).
toList
()
)
)
);
tester
.
fling
(
find
.
text
(
'A'
),
const
Offset
(
0.0
,
200.0
),
-
1000.0
);
tester
.
pump
();
tester
.
pump
(
const
Duration
(
seconds:
1
));
// finish the scroll animation
tester
.
pump
(
const
Duration
(
seconds:
1
));
// finish the indicator settle animation
tester
.
pump
(
const
Duration
(
seconds:
1
));
// finish the indicator hide animation
expect
(
refreshCalled
,
true
);
});
});
}
packages/flutter_test/lib/src/widget_tester.dart
View file @
c4ae13ed
...
...
@@ -20,8 +20,8 @@ import 'instrumentation.dart';
/// test('MyWidget', () {
/// testWidgets((WidgetTester tester) {
/// tester.pumpWidget(new MyWidget());
/// tester.tap(find.
byT
ext('Save'));
/// expect(tester, hasWidget(find.
byT
ext('Success')));
/// tester.tap(find.
t
ext('Save'));
/// expect(tester, hasWidget(find.
t
ext('Success')));
/// });
/// });
void
testWidgets
(
void
callback
(
WidgetTester
widgetTester
))
{
...
...
@@ -34,7 +34,7 @@ void testWidgets(void callback(WidgetTester widgetTester)) {
///
/// Examples:
///
/// tester.tap(find.
byT
ext('Save'));
/// tester.tap(find.
t
ext('Save'));
/// tester.widget(find.byType(MyWidget));
/// tester.stateOf(find.byConfig(config));
/// tester.getSize(find.byKey(new ValueKey('save-button')));
...
...
@@ -44,7 +44,7 @@ const CommonFinders find = const CommonFinders._();
///
/// Example:
///
/// expect(tester, hasWidget(find.
byT
ext('Save')));
/// expect(tester, hasWidget(find.
t
ext('Save')));
Matcher
hasWidget
(
Finder
finder
)
=>
new
_HasWidgetMatcher
(
finder
);
/// Opposite of [hasWidget].
...
...
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