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
ff15d04f
Unverified
Commit
ff15d04f
authored
Apr 30, 2021
by
Kate Lovett
Committed by
GitHub
Apr 30, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add support for pointer scrolling to trigger floats & snaps (#76145)
parent
3cbfe82d
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
381 additions
and
62 deletions
+381
-62
app_bar.dart
packages/flutter/lib/src/material/app_bar.dart
+1
-53
sliver_persistent_header.dart
...s/flutter/lib/src/rendering/sliver_persistent_header.dart
+11
-1
nested_scroll_view.dart
packages/flutter/lib/src/widgets/nested_scroll_view.dart
+7
-0
scroll_position_with_single_context.dart
.../lib/src/widgets/scroll_position_with_single_context.dart
+1
-0
sliver_persistent_header.dart
...ges/flutter/lib/src/widgets/sliver_persistent_header.dart
+80
-7
nested_scroll_view_test.dart
packages/flutter/test/widgets/nested_scroll_view_test.dart
+54
-0
slivers_appbar_floating_test.dart
...es/flutter/test/widgets/slivers_appbar_floating_test.dart
+227
-1
No files found.
packages/flutter/lib/src/material/app_bar.dart
View file @
ff15d04f
...
...
@@ -1067,58 +1067,6 @@ class _AppBarState extends State<AppBar> {
}
}
class
_FloatingAppBar
extends
StatefulWidget
{
const
_FloatingAppBar
({
Key
?
key
,
required
this
.
child
})
:
super
(
key:
key
);
final
Widget
child
;
@override
_FloatingAppBarState
createState
()
=>
_FloatingAppBarState
();
}
// A wrapper for the widget created by _SliverAppBarDelegate that starts and
// stops the floating app bar's snap-into-view or snap-out-of-view animation.
class
_FloatingAppBarState
extends
State
<
_FloatingAppBar
>
{
ScrollPosition
?
_position
;
@override
void
didChangeDependencies
()
{
super
.
didChangeDependencies
();
if
(
_position
!=
null
)
_position
!.
isScrollingNotifier
.
removeListener
(
_isScrollingListener
);
_position
=
Scrollable
.
of
(
context
)?.
position
;
if
(
_position
!=
null
)
_position
!.
isScrollingNotifier
.
addListener
(
_isScrollingListener
);
}
@override
void
dispose
()
{
if
(
_position
!=
null
)
_position
!.
isScrollingNotifier
.
removeListener
(
_isScrollingListener
);
super
.
dispose
();
}
RenderSliverFloatingPersistentHeader
?
_headerRenderer
()
{
return
context
.
findAncestorRenderObjectOfType
<
RenderSliverFloatingPersistentHeader
>();
}
void
_isScrollingListener
()
{
if
(
_position
==
null
)
return
;
// When a scroll stops, then maybe snap the appbar into view.
// Similarly, when a scroll starts, then maybe stop the snap animation.
final
RenderSliverFloatingPersistentHeader
?
header
=
_headerRenderer
();
if
(
_position
!.
isScrollingNotifier
.
value
)
header
?.
maybeStopSnapAnimation
(
_position
!.
userScrollDirection
);
else
header
?.
maybeStartSnapAnimation
(
_position
!.
userScrollDirection
);
}
@override
Widget
build
(
BuildContext
context
)
=>
widget
.
child
;
}
class
_SliverAppBarDelegate
extends
SliverPersistentHeaderDelegate
{
_SliverAppBarDelegate
({
required
this
.
leading
,
...
...
@@ -1264,7 +1212,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
systemOverlayStyle:
systemOverlayStyle
,
),
);
return
floating
?
_FloatingAppBar
(
child:
appBar
)
:
appBar
;
return
appBar
;
}
@override
...
...
packages/flutter/lib/src/rendering/sliver_persistent_header.dart
View file @
ff15d04f
...
...
@@ -550,6 +550,10 @@ abstract class RenderSliverFloatingPersistentHeader extends RenderSliverPersiste
late
Animation
<
double
>
_animation
;
double
?
_lastActualScrollOffset
;
double
?
_effectiveScrollOffset
;
// Important for pointer scrolling, which does not have the same concept of
// a hold and release scroll movement, like dragging.
// This keeps track of the last ScrollDirection when scrolling started.
ScrollDirection
?
_lastStartedScrollDirection
;
// Distance from our leading edge to the child's leading edge, in the axis
// direction. Negative if we're scrolled off the top.
...
...
@@ -647,6 +651,11 @@ abstract class RenderSliverFloatingPersistentHeader extends RenderSliverPersiste
);
}
/// Update the last known ScrollDirection when scrolling began.
void
updateScrollStartDirection
(
ScrollDirection
direction
)
{
_lastStartedScrollDirection
=
direction
;
}
/// If the header isn't already fully exposed, then scroll it into view.
void
maybeStartSnapAnimation
(
ScrollDirection
direction
)
{
final
FloatingHeaderSnapConfiguration
?
snap
=
snapConfiguration
;
...
...
@@ -680,7 +689,8 @@ abstract class RenderSliverFloatingPersistentHeader extends RenderSliverPersiste
(
_effectiveScrollOffset
!
<
maxExtent
)))
{
// some part of it is visible, so should shrink or reveal as appropriate.
double
delta
=
_lastActualScrollOffset
!
-
constraints
.
scrollOffset
;
final
bool
allowFloatingExpansion
=
constraints
.
userScrollDirection
==
ScrollDirection
.
forward
;
final
bool
allowFloatingExpansion
=
constraints
.
userScrollDirection
==
ScrollDirection
.
forward
||
(
_lastStartedScrollDirection
!=
null
&&
_lastStartedScrollDirection
==
ScrollDirection
.
forward
);
if
(
allowFloatingExpansion
)
{
if
(
_effectiveScrollOffset
!
>
maxExtent
)
// We're scrolled off-screen, but should reveal, so
_effectiveScrollOffset
=
maxExtent
;
// pretend we're just at the limit.
...
...
packages/flutter/lib/src/widgets/nested_scroll_view.dart
View file @
ff15d04f
...
...
@@ -1103,6 +1103,13 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont
delta
<
0.0
?
ScrollDirection
.
forward
:
ScrollDirection
.
reverse
,
);
// Set the isScrollingNotifier. Even if only one position actually receives
// the delta, the NestedScrollView's intention is to treat multiple
// ScrollPositions as one.
_outerPosition
!.
isScrollingNotifier
.
value
=
true
;
for
(
final
_NestedScrollPosition
position
in
_innerPositions
)
position
.
isScrollingNotifier
.
value
=
true
;
if
(
_innerPositions
.
isEmpty
)
{
// Does not enter overscroll.
_outerPosition
!.
applyClampedPointerSignalUpdate
(
delta
);
...
...
packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart
View file @
ff15d04f
...
...
@@ -216,6 +216,7 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
);
final
double
oldPixels
=
pixels
;
forcePixels
(
targetPixels
);
isScrollingNotifier
.
value
=
true
;
didStartScroll
();
didUpdateScrollPositionBy
(
pixels
-
oldPixels
);
didEndScroll
();
...
...
packages/flutter/lib/src/widgets/sliver_persistent_header.dart
View file @
ff15d04f
...
...
@@ -7,6 +7,8 @@ import 'package:flutter/rendering.dart';
import
'package:flutter/scheduler.dart'
show
TickerProvider
;
import
'framework.dart'
;
import
'scroll_position.dart'
;
import
'scrollable.dart'
;
/// Delegate for configuring a [SliverPersistentHeader].
abstract
class
SliverPersistentHeaderDelegate
{
...
...
@@ -185,8 +187,72 @@ class SliverPersistentHeader extends StatelessWidget {
}
}
class
_FloatingHeader
extends
StatefulWidget
{
const
_FloatingHeader
({
Key
?
key
,
required
this
.
child
})
:
super
(
key:
key
);
final
Widget
child
;
@override
_FloatingHeaderState
createState
()
=>
_FloatingHeaderState
();
}
// A wrapper for the widget created by _SliverPersistentHeaderElement that
// starts and stops the floating app bar's snap-into-view or snap-out-of-view
// animation. It also informs the float when pointer scrolling by updating the
// last known ScrollDirection when scrolling began.
class
_FloatingHeaderState
extends
State
<
_FloatingHeader
>
{
ScrollPosition
?
_position
;
@override
void
didChangeDependencies
()
{
super
.
didChangeDependencies
();
if
(
_position
!=
null
)
_position
!.
isScrollingNotifier
.
removeListener
(
_isScrollingListener
);
_position
=
Scrollable
.
of
(
context
)?.
position
;
if
(
_position
!=
null
)
_position
!.
isScrollingNotifier
.
addListener
(
_isScrollingListener
);
}
@override
void
dispose
()
{
if
(
_position
!=
null
)
_position
!.
isScrollingNotifier
.
removeListener
(
_isScrollingListener
);
super
.
dispose
();
}
RenderSliverFloatingPersistentHeader
?
_headerRenderer
()
{
return
context
.
findAncestorRenderObjectOfType
<
RenderSliverFloatingPersistentHeader
>();
}
void
_isScrollingListener
()
{
assert
(
_position
!=
null
);
// When a scroll stops, then maybe snap the app bar into view.
// Similarly, when a scroll starts, then maybe stop the snap animation.
// Update the scrolling direction as well for pointer scrolling updates.
final
RenderSliverFloatingPersistentHeader
?
header
=
_headerRenderer
();
if
(
_position
!.
isScrollingNotifier
.
value
)
{
header
?.
updateScrollStartDirection
(
_position
!.
userScrollDirection
);
// Only SliverAppBars support snapping, headers will not snap.
header
?.
maybeStopSnapAnimation
(
_position
!.
userScrollDirection
);
}
else
{
// Only SliverAppBars support snapping, headers will not snap.
header
?.
maybeStartSnapAnimation
(
_position
!.
userScrollDirection
);
}
}
@override
Widget
build
(
BuildContext
context
)
=>
widget
.
child
;
}
class
_SliverPersistentHeaderElement
extends
RenderObjectElement
{
_SliverPersistentHeaderElement
(
_SliverPersistentHeaderRenderObjectWidget
widget
)
:
super
(
widget
);
_SliverPersistentHeaderElement
(
_SliverPersistentHeaderRenderObjectWidget
widget
,
{
this
.
floating
=
false
,
})
:
assert
(
floating
!=
null
),
super
(
widget
);
final
bool
floating
;
@override
_SliverPersistentHeaderRenderObjectWidget
get
widget
=>
super
.
widget
as
_SliverPersistentHeaderRenderObjectWidget
;
...
...
@@ -229,11 +295,13 @@ class _SliverPersistentHeaderElement extends RenderObjectElement {
owner
!.
buildScope
(
this
,
()
{
child
=
updateChild
(
child
,
widget
.
delegate
.
build
(
this
,
shrinkOffset
,
overlapsContent
,
),
floating
?
_FloatingHeader
(
child:
widget
.
delegate
.
build
(
this
,
shrinkOffset
,
overlapsContent
))
:
widget
.
delegate
.
build
(
this
,
shrinkOffset
,
overlapsContent
),
null
,
);
});
...
...
@@ -273,13 +341,16 @@ abstract class _SliverPersistentHeaderRenderObjectWidget extends RenderObjectWid
const
_SliverPersistentHeaderRenderObjectWidget
({
Key
?
key
,
required
this
.
delegate
,
this
.
floating
=
false
,
})
:
assert
(
delegate
!=
null
),
assert
(
floating
!=
null
),
super
(
key:
key
);
final
SliverPersistentHeaderDelegate
delegate
;
final
bool
floating
;
@override
_SliverPersistentHeaderElement
createElement
()
=>
_SliverPersistentHeaderElement
(
this
);
_SliverPersistentHeaderElement
createElement
()
=>
_SliverPersistentHeaderElement
(
this
,
floating:
floating
);
@override
_RenderSliverPersistentHeaderForWidgetsMixin
createRenderObject
(
BuildContext
context
);
...
...
@@ -383,6 +454,7 @@ class _SliverFloatingPersistentHeader extends _SliverPersistentHeaderRenderObjec
})
:
super
(
key:
key
,
delegate:
delegate
,
floating:
true
,
);
@override
...
...
@@ -428,6 +500,7 @@ class _SliverFloatingPinnedPersistentHeader extends _SliverPersistentHeaderRende
})
:
super
(
key:
key
,
delegate:
delegate
,
floating:
true
,
);
@override
...
...
packages/flutter/test/widgets/nested_scroll_view_test.dart
View file @
ff15d04f
...
...
@@ -1490,6 +1490,60 @@ void main() {
verifyGeometry
(
key:
appBarKey
,
paintExtent:
56.0
,
visible:
true
);
});
testWidgets
(
'snap with pointer signal'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
appBarKey
=
GlobalKey
();
await
tester
.
pumpWidget
(
buildFloatTest
(
floating:
true
,
snap:
true
,
appBarKey:
appBarKey
,
));
final
Offset
scrollEventLocation
=
tester
.
getCenter
(
find
.
byType
(
NestedScrollView
));
final
TestPointer
testPointer
=
TestPointer
(
1
,
ui
.
PointerDeviceKind
.
mouse
);
// Create a hover event so that |testPointer| has a location when generating the scroll.
testPointer
.
hover
(
scrollEventLocation
);
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
56.0
,
visible:
true
);
// Scroll away the outer scroll view and some of the inner scroll view.
// We will not scroll back the same amount to indicate that we are
// snapping in before reaching the top of the inner scrollable.
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
300.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsNothing
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
0.0
,
visible:
false
);
// The snap animation should be triggered to expand the app bar
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
30.0
)));
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
56.0
,
visible:
true
);
// Scroll away a bit more to trigger the snap close animation.
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
30.0
)));
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'Test Title'
),
findsNothing
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
find
.
byType
(
AppBar
),
findsNothing
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
0.0
,
visible:
false
);
});
testWidgets
(
'float expanded with pointer signal'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
appBarKey
=
GlobalKey
();
await
tester
.
pumpWidget
(
buildFloatTest
(
...
...
packages/flutter/test/widgets/slivers_appbar_floating_test.dart
View file @
ff15d04f
...
...
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:ui'
as
ui
;
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
verifyPaintPosition
(
GlobalKey
key
,
Offset
ideal
,
bool
visible
)
{
...
...
@@ -226,6 +228,230 @@ void main() {
expect
(
tester
.
getTopLeft
(
find
.
byType
(
Container
)),
Offset
.
zero
);
expect
(
tester
.
getTopLeft
(
find
.
text
(
'X'
)),
const
Offset
(
0.0
,
250.0
));
});
group
(
'Pointer scrolled floating'
,
()
{
Widget
buildTest
(
Widget
sliver
)
{
return
MaterialApp
(
home:
CustomScrollView
(
slivers:
<
Widget
>[
sliver
,
SliverFixedExtentList
(
itemExtent:
50.0
,
delegate:
SliverChildBuilderDelegate
(
(
BuildContext
context
,
int
index
)
=>
Text
(
'Item
$index
'
),
childCount:
30
,
)
),
],
),
);
}
void
verifyGeometry
({
required
GlobalKey
key
,
required
bool
visible
,
required
double
paintExtent
})
{
final
RenderSliver
target
=
key
.
currentContext
!.
findRenderObject
()!
as
RenderSliver
;
final
SliverGeometry
geometry
=
target
.
geometry
!;
expect
(
geometry
.
visible
,
visible
);
expect
(
geometry
.
paintExtent
,
paintExtent
);
}
testWidgets
(
'SliverAppBar'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
appBarKey
=
GlobalKey
();
await
tester
.
pumpWidget
(
buildTest
(
SliverAppBar
(
key:
appBarKey
,
floating:
true
,
title:
const
Text
(
'Test Title'
),
)));
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
visible:
true
,
paintExtent:
56.0
);
// Pointer scroll the app bar away, we will scroll back less to validate the
// app bar floats back in.
final
Offset
point1
=
tester
.
getCenter
(
find
.
text
(
'Item 5'
));
final
TestPointer
testPointer
=
TestPointer
(
1
,
ui
.
PointerDeviceKind
.
mouse
);
testPointer
.
hover
(
point1
);
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
300.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsNothing
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
0.0
,
visible:
false
);
// Scroll back to float in appbar
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
50.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
50.0
,
visible:
true
);
// Float the rest of the way in.
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
250.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
56.0
,
visible:
true
);
});
testWidgets
(
'SliverPersistentHeader'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
headerKey
=
GlobalKey
();
await
tester
.
pumpWidget
(
buildTest
(
SliverPersistentHeader
(
key:
headerKey
,
floating:
true
,
delegate:
HeaderDelegate
(),
)));
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
verifyGeometry
(
key:
headerKey
,
visible:
true
,
paintExtent:
56.0
);
// Pointer scroll the app bar away, we will scroll back less to validate the
// app bar floats back in.
final
Offset
point1
=
tester
.
getCenter
(
find
.
text
(
'Item 5'
));
final
TestPointer
testPointer
=
TestPointer
(
1
,
ui
.
PointerDeviceKind
.
mouse
);
testPointer
.
hover
(
point1
);
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
300.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsNothing
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
verifyGeometry
(
key:
headerKey
,
paintExtent:
0.0
,
visible:
false
);
// Scroll back to float in appbar
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
50.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
verifyGeometry
(
key:
headerKey
,
paintExtent:
50.0
,
visible:
true
);
// Float the rest of the way in.
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
250.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
verifyGeometry
(
key:
headerKey
,
paintExtent:
56.0
,
visible:
true
);
});
testWidgets
(
'and snapping SliverAppBar'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
appBarKey
=
GlobalKey
();
await
tester
.
pumpWidget
(
buildTest
(
SliverAppBar
(
key:
appBarKey
,
floating:
true
,
snap:
true
,
title:
const
Text
(
'Test Title'
),
)));
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
visible:
true
,
paintExtent:
56.0
);
// Pointer scroll the app bar away, we will scroll back less to validate the
// app bar floats back in and then snaps to full size.
final
Offset
point1
=
tester
.
getCenter
(
find
.
text
(
'Item 5'
));
final
TestPointer
testPointer
=
TestPointer
(
1
,
ui
.
PointerDeviceKind
.
mouse
);
testPointer
.
hover
(
point1
);
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
300.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsNothing
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
0.0
,
visible:
false
);
// Scroll back to float in appbar
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
30.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
30.0
,
visible:
true
);
await
tester
.
pumpAndSettle
();
// The snap animation should have completed and the app bar should be
// fully expanded.
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
56.0
,
visible:
true
);
// Float back out a bit and trigger snap close animation.
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
50.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
6.0
,
visible:
true
);
await
tester
.
pumpAndSettle
();
// The snap animation should have completed and the app bar should no
// longer be visible.
expect
(
find
.
text
(
'Test Title'
),
findsNothing
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
find
.
byType
(
AppBar
),
findsNothing
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
0.0
,
visible:
false
);
});
});
}
class
HeaderDelegate
extends
SliverPersistentHeaderDelegate
{
@override
Widget
build
(
BuildContext
context
,
double
shrinkOffset
,
bool
overlapsContent
)
{
return
Container
(
height:
56
,
color:
Colors
.
red
,
child:
const
Text
(
'Test Title'
),
);
}
@override
double
get
maxExtent
=>
56
;
@override
double
get
minExtent
=>
56
;
@override
bool
shouldRebuild
(
SliverPersistentHeaderDelegate
oldDelegate
)
=>
false
;
}
class
TestDelegate
extends
SliverPersistentHeaderDelegate
{
...
...
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