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
4e957015
Commit
4e957015
authored
Feb 08, 2017
by
Adam Barth
Committed by
GitHub
Feb 08, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Switch TabBarView to PageView (#7982)
Tabs are now fully driven by slivers.
parent
6d41c704
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
68 additions
and
141 deletions
+68
-141
date_picker.dart
packages/flutter/lib/src/material/date_picker.dart
+2
-2
tabs.dart
packages/flutter/lib/src/material/tabs.dart
+39
-97
page_view.dart
packages/flutter/lib/src/widgets/page_view.dart
+7
-7
scrollable.dart
packages/flutter/lib/src/widgets/scrollable.dart
+11
-10
tabs_test.dart
packages/flutter/test/material/tabs_test.dart
+6
-22
page_view_test.dart
packages/flutter/test/widgets/page_view_test.dart
+1
-1
widget_tester.dart
packages/flutter_test/lib/src/widget_tester.dart
+2
-2
No files found.
packages/flutter/lib/src/material/date_picker.dart
View file @
4e957015
...
...
@@ -365,7 +365,7 @@ class _MonthPickerState extends State<MonthPicker> {
void
initState
()
{
super
.
initState
();
// Initially display the pre-selected date.
_dayPickerController
=
new
PageController
(
initialPage:
_monthDelta
(
config
.
firstDate
,
config
.
selectedDate
)
.
toDouble
()
);
_dayPickerController
=
new
PageController
(
initialPage:
_monthDelta
(
config
.
firstDate
,
config
.
selectedDate
));
_currentDisplayedMonthDate
=
new
DateTime
(
config
.
selectedDate
.
year
,
config
.
selectedDate
.
month
);
_updateCurrentDate
();
}
...
...
@@ -373,7 +373,7 @@ class _MonthPickerState extends State<MonthPicker> {
@override
void
didUpdateConfig
(
MonthPicker
oldConfig
)
{
if
(
config
.
selectedDate
!=
oldConfig
.
selectedDate
)
{
_dayPickerController
=
new
PageController
(
initialPage:
_monthDelta
(
config
.
firstDate
,
config
.
selectedDate
)
.
toDouble
()
);
_dayPickerController
=
new
PageController
(
initialPage:
_monthDelta
(
config
.
firstDate
,
config
.
selectedDate
));
_currentDisplayedMonthDate
=
new
DateTime
(
config
.
selectedDate
.
year
,
config
.
selectedDate
.
month
);
}
...
...
packages/flutter/lib/src/material/tabs.dart
View file @
4e957015
...
...
@@ -590,56 +590,6 @@ class _TabBarState extends State<TabBar> {
}
}
class
_PageableTabBarView
extends
PageableList
{
_PageableTabBarView
({
Key
key
,
List
<
Widget
>
children
,
double
initialScrollOffset:
0.0
,
})
:
super
(
key:
key
,
scrollDirection:
Axis
.
horizontal
,
children:
children
,
initialScrollOffset:
initialScrollOffset
,
);
@override
_PageableTabBarViewState
createState
()
=>
new
_PageableTabBarViewState
();
}
class
_PageableTabBarViewState
extends
PageableListState
<
_PageableTabBarView
>
{
BoundedBehavior
_boundedBehavior
;
@override
ExtentScrollBehavior
get
scrollBehavior
{
_boundedBehavior
??=
new
BoundedBehavior
(
platform:
platform
,
containerExtent:
1.0
,
contentExtent:
config
.
children
.
length
.
toDouble
(),
);
return
_boundedBehavior
;
}
@override
TargetPlatform
get
platform
=>
Theme
.
of
(
context
).
platform
;
@override
Future
<
Null
>
fling
(
double
scrollVelocity
)
{
final
double
newScrollOffset
=
snapScrollOffset
(
scrollOffset
+
scrollVelocity
.
sign
)
.
clamp
(
snapScrollOffset
(
scrollOffset
-
0.5
),
snapScrollOffset
(
scrollOffset
+
0.5
))
.
clamp
(
0.0
,
(
config
.
children
.
length
-
1
).
toDouble
());
return
scrollTo
(
newScrollOffset
,
duration:
config
.
duration
,
curve:
config
.
curve
);
}
@override
Widget
buildContent
(
BuildContext
context
)
{
return
new
PageViewport
(
mainAxis:
config
.
scrollDirection
,
startOffset:
scrollOffset
,
children:
config
.
children
,
);
}
}
/// A pageable list that displays the widget which corresponds to the currently
/// selected tab. Typically used in conjuction with a [TabBar].
///
...
...
@@ -670,10 +620,11 @@ class TabBarView extends StatefulWidget {
_TabBarViewState
createState
()
=>
new
_TabBarViewState
();
}
class
_TabBarViewState
extends
State
<
TabBarView
>
{
final
GlobalKey
<
ScrollableState
>
viewportKey
=
new
GlobalKey
<
ScrollableState
>();
final
PageScrollPhysics
_kTabBarViewPhysics
=
const
PageScrollPhysics
().
applyTo
(
const
ClampingScrollPhysics
());
class
_TabBarViewState
extends
State
<
TabBarView
>
{
TabController
_controller
;
PageController
_pageController
;
List
<
Widget
>
_children
;
double
_offsetAnchor
;
double
_offsetBias
=
0.0
;
...
...
@@ -703,6 +654,7 @@ class _TabBarViewState extends State<TabBarView> {
super
.
dependenciesChanged
();
_updateTabController
();
_currentIndex
=
_controller
?.
index
;
_pageController
=
new
PageController
(
initialPage:
_currentIndex
??
0
);
}
@override
...
...
@@ -736,33 +688,30 @@ class _TabBarViewState extends State<TabBarView> {
if
(!
mounted
)
return
new
Future
<
Null
>.
value
();
final
ScrollableState
viewport
=
viewportKey
.
currentState
;
if
(
viewport
.
scrollOffset
==
_currentIndex
.
toDouble
())
if
(
_pageController
.
page
==
_currentIndex
.
toDouble
())
return
new
Future
<
Null
>.
value
();
final
int
previousIndex
=
_controller
.
previousIndex
;
if
((
_currentIndex
-
previousIndex
).
abs
()
==
1
)
return
viewport
.
scrollTo
(
_currentIndex
.
toDouble
(),
duration:
kTabScrollDuration
);
return
_pageController
.
animateToPage
(
_currentIndex
,
duration:
kTabScrollDuration
,
curve:
Curves
.
ease
);
assert
((
_currentIndex
-
previousIndex
).
abs
()
>
1
);
double
initialScroll
;
int
initialPage
;
setState
(()
{
_warpUnderwayCount
+=
1
;
_children
=
new
List
<
Widget
>.
from
(
config
.
children
,
growable:
false
);
if
(
_currentIndex
>
previousIndex
)
{
_children
[
_currentIndex
-
1
]
=
_children
[
previousIndex
];
initial
Scroll
=
(
_currentIndex
-
1
).
toDouble
()
;
initial
Page
=
_currentIndex
-
1
;
}
else
{
_children
[
_currentIndex
+
1
]
=
_children
[
previousIndex
];
initial
Scroll
=
(
_currentIndex
+
1
).
toDouble
()
;
initial
Page
=
_currentIndex
+
1
;
}
});
await
viewport
.
scrollTo
(
initialScroll
);
if
(!
mounted
)
return
new
Future
<
Null
>.
value
();
_pageController
.
jumpToPage
(
initialPage
);
await
viewport
.
scrollTo
(
_currentIndex
.
toDouble
(),
duration:
kTabScrollDuration
);
await
_pageController
.
animateToPage
(
_currentIndex
,
duration:
kTabScrollDuration
,
curve:
Curves
.
ease
);
if
(!
mounted
)
return
new
Future
<
Null
>.
value
();
...
...
@@ -772,45 +721,38 @@ class _TabBarViewState extends State<TabBarView> {
});
}
// Called when the
_PageableTabBar
View scrolls
bool
_handleScrollNotification
(
ScrollNotification
notification
)
{
// Called when the
Page
View scrolls
bool
_handleScrollNotification
(
ScrollNotification
2
notification
)
{
if
(
_warpUnderwayCount
>
0
)
return
false
;
final
ScrollableState
scrollable
=
notification
.
scrollable
;
if
(
scrollable
.
config
.
key
!=
viewportKey
)
if
(
notification
.
depth
!=
1
)
return
false
;
switch
(
notification
.
kind
)
{
case
ScrollNotificationKind
.
started
:
_offsetAnchor
=
null
;
break
;
case
ScrollNotificationKind
.
updated
:
if
(!
_controller
.
indexIsChanging
)
{
_offsetAnchor
??=
scrollable
.
scrollOffset
;
_controller
.
offset
=
(
_offsetBias
+
scrollable
.
scrollOffset
-
_offsetAnchor
).
clamp
(-
1.0
,
1.0
);
}
break
;
if
(
notification
is
ScrollStartNotification
)
{
_offsetAnchor
=
null
;
}
else
if
(
notification
is
ScrollUpdateNotification
)
{
if
(!
_controller
.
indexIsChanging
)
{
_offsetAnchor
??=
_pageController
.
page
;
_controller
.
offset
=
(
_offsetBias
+
_pageController
.
page
-
_offsetAnchor
).
clamp
(-
1.0
,
1.0
);
}
}
else
if
(
notification
is
ScrollEndNotification
)
{
// Either the the animation that follows a fling has completed and we've landed
// on a new tab view, or a new pointer gesture has interrupted the fling
// animation before it has completed.
case
ScrollNotificationKind
.
ended
:
final
double
integralScrollOffset
=
scrollable
.
scrollOffset
.
floorToDouble
();
if
(
integralScrollOffset
==
scrollable
.
scrollOffset
)
{
_offsetBias
=
0.0
;
// The animation duration is short since the tab indicator and this
// pageable list have already moved.
_controller
.
animateTo
(
integralScrollOffset
.
floor
(),
duration:
const
Duration
(
milliseconds:
30
)
);
}
else
{
// The fling scroll animation was interrupted.
_offsetBias
=
_controller
.
offset
;
}
break
;
final
double
integralScrollOffset
=
_pageController
.
page
.
floorToDouble
();
if
(
integralScrollOffset
==
_pageController
.
page
)
{
_offsetBias
=
0.0
;
// The animation duration is short since the tab indicator and this
// pageable list have already moved.
_controller
.
animateTo
(
integralScrollOffset
.
floor
(),
duration:
const
Duration
(
milliseconds:
30
)
);
}
else
{
// The fling scroll animation was interrupted.
_offsetBias
=
_controller
.
offset
;
}
}
return
false
;
...
...
@@ -818,12 +760,12 @@ class _TabBarViewState extends State<TabBarView> {
@override
Widget
build
(
BuildContext
context
)
{
return
new
NotificationListener
<
ScrollNotification
>(
return
new
NotificationListener
<
ScrollNotification
2
>(
onNotification:
_handleScrollNotification
,
child:
new
_PageableTabBarView
(
key:
viewportKey
,
child:
new
PageView
(
controller:
_pageController
,
physics:
_kTabBarViewPhysics
,
children:
_children
,
initialScrollOffset:
(
_controller
?.
index
??
0
).
toDouble
(),
),
);
}
...
...
packages/flutter/lib/src/widgets/page_view.dart
View file @
4e957015
...
...
@@ -18,10 +18,10 @@ import 'sliver.dart';
class
PageController
extends
ScrollController
{
PageController
({
this
.
initialPage
:
0
.0
,
this
.
initialPage
:
0
,
});
final
double
initialPage
;
final
int
initialPage
;
double
get
page
{
final
ScrollPosition
position
=
this
.
position
;
...
...
@@ -36,7 +36,7 @@ class PageController extends ScrollController {
return
position
.
animateTo
(
page
*
position
.
viewportDimension
,
duration:
duration
,
curve:
curve
);
}
void
jumpToPage
(
double
page
)
{
void
jumpToPage
(
int
page
)
{
final
ScrollPosition
position
=
this
.
position
;
position
.
jumpTo
(
page
*
position
.
viewportDimension
);
}
...
...
@@ -64,7 +64,7 @@ class _PagePosition extends ScrollPosition {
_PagePosition
({
ScrollPhysics
physics
,
AbstractScrollState
state
,
this
.
initialPage
:
0
.0
,
this
.
initialPage
:
0
,
ScrollPosition
oldPosition
,
})
:
super
(
physics:
physics
,
...
...
@@ -73,14 +73,14 @@ class _PagePosition extends ScrollPosition {
oldPosition:
oldPosition
,
);
final
double
initialPage
;
final
int
initialPage
;
@override
bool
applyViewportDimension
(
double
viewportDimension
)
{
final
double
oldViewportDimensions
=
this
.
viewportDimension
;
final
bool
result
=
super
.
applyViewportDimension
(
viewportDimension
);
final
double
oldPixels
=
pixels
;
final
double
page
=
oldPixels
==
null
?
initialPage
:
oldPixels
/
oldViewportDimensions
;
final
double
page
=
oldPixels
==
null
?
initialPage
.
toDouble
()
:
oldPixels
/
oldViewportDimensions
;
final
double
newPixels
=
page
*
viewportDimension
;
if
(
newPixels
!=
oldPixels
)
{
correctPixels
(
newPixels
);
...
...
@@ -186,7 +186,7 @@ class PageView extends BoxScrollView {
final
ScrollableMetrics
metrics
=
notification
.
metrics
;
onPageChanged
(
metrics
.
extentBefore
~/
metrics
.
viewportDimension
);
}
return
tru
e
;
return
fals
e
;
},
child:
scrollable
,
);
...
...
packages/flutter/lib/src/widgets/scrollable.dart
View file @
4e957015
...
...
@@ -242,12 +242,6 @@ class Scrollable2State extends State<Scrollable2> with TickerProviderStateMixin
}
}
@override
@protected
void
didEndDrag
()
{
_drag
=
null
;
}
@override
@protected
void
dispatchNotification
(
Notification
notification
)
{
...
...
@@ -280,19 +274,26 @@ class Scrollable2State extends State<Scrollable2> with TickerProviderStateMixin
void
_handleDragStart
(
DragStartDetails
details
)
{
assert
(
_drag
==
null
);
_drag
=
position
.
beginDragActivity
(
details
);
assert
(
_drag
!=
null
);
}
void
_handleDragUpdate
(
DragUpdateDetails
details
)
{
assert
(
_drag
!=
null
);
_drag
.
update
(
details
,
reverse:
_reverseDirection
);
// _drag might be null if the drag activity ended and called didEndDrag.
_drag
?
.
update
(
details
,
reverse:
_reverseDirection
);
}
void
_handleDragEnd
(
DragEndDetails
details
)
{
assert
(
_drag
!=
null
);
_drag
.
end
(
details
,
reverse:
_reverseDirection
);
// _drag might be null if the drag activity ended and called didEndDrag.
_drag
?
.
end
(
details
,
reverse:
_reverseDirection
);
assert
(
_drag
==
null
);
}
@override
@protected
void
didEndDrag
()
{
_drag
=
null
;
}
// DESCRIPTION
...
...
packages/flutter/test/material/tabs_test.dart
View file @
4e957015
...
...
@@ -294,8 +294,7 @@ void main() {
// Fling to the left, switch from the 'LEFT' tab to the 'RIGHT'
Point
flingStart
=
tester
.
getCenter
(
find
.
text
(
'LEFT CHILD'
));
await
tester
.
flingFrom
(
flingStart
,
const
Offset
(-
200.0
,
0.0
),
10000.0
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
// finish the scroll animation
await
tester
.
pumpUntilNoTransientCallbacks
();
expect
(
controller
.
index
,
1
);
expect
(
find
.
text
(
'LEFT CHILD'
),
findsNothing
);
expect
(
find
.
text
(
'RIGHT CHILD'
),
findsOneWidget
);
...
...
@@ -303,8 +302,7 @@ void main() {
// Fling to the right, switch back to the 'LEFT' tab
flingStart
=
tester
.
getCenter
(
find
.
text
(
'RIGHT CHILD'
));
await
tester
.
flingFrom
(
flingStart
,
const
Offset
(
200.0
,
0.0
),
10000.0
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
// finish the scroll animation
await
tester
.
pumpUntilNoTransientCallbacks
();
expect
(
controller
.
index
,
0
);
expect
(
find
.
text
(
'LEFT CHILD'
),
findsOneWidget
);
expect
(
find
.
text
(
'RIGHT CHILD'
),
findsNothing
);
...
...
@@ -389,36 +387,22 @@ void main() {
value
=
tabs
[
controller
.
index
];
});
// TODO(hixie) - the new scrolling framework should eliminate most of the pump
// calls that follow. Currently they exist to complete chains of future.then
// in the implementation.
await
tester
.
tap
(
find
.
text
(
'RIGHT'
));
await
tester
.
pump
();
// start the animation
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pumpUntilNoTransientCallbacks
();
expect
(
value
,
'RIGHT'
);
await
tester
.
tap
(
find
.
text
(
'LEFT'
));
await
tester
.
pump
();
// start the animation
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pumpUntilNoTransientCallbacks
();
expect
(
value
,
'LEFT'
);
Point
leftFlingStart
=
tester
.
getCenter
(
find
.
text
(
'LEFT CHILD'
));
await
tester
.
flingFrom
(
leftFlingStart
,
const
Offset
(-
200.0
,
0.0
),
10000.0
);
await
tester
.
pump
();
// start the animation
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pumpUntilNoTransientCallbacks
();
expect
(
value
,
'RIGHT'
);
Point
rightFlingStart
=
tester
.
getCenter
(
find
.
text
(
'RIGHT CHILD'
));
await
tester
.
flingFrom
(
rightFlingStart
,
const
Offset
(
200.0
,
0.0
),
10000.0
);
await
tester
.
pump
();
// start the animation
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pumpUntilNoTransientCallbacks
();
expect
(
value
,
'LEFT'
);
});
...
...
packages/flutter/test/widgets/page_view_test.dart
View file @
4e957015
...
...
@@ -110,7 +110,7 @@ void main() {
});
testWidgets
(
'PageController control test'
,
(
WidgetTester
tester
)
async
{
PageController
controller
=
new
PageController
(
initialPage:
4
.0
);
PageController
controller
=
new
PageController
(
initialPage:
4
);
await
tester
.
pumpWidget
(
new
Center
(
child:
new
SizedBox
(
...
...
packages/flutter_test/lib/src/widget_tester.dart
View file @
4e957015
...
...
@@ -213,8 +213,8 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
///
/// Alternatively, one can check that the return value from this function
/// matches the expected number of pumps.
Future
<
int
>
pumpUntilNoTransientCallbacks
(
Duration
duration
,
[
Future
<
int
>
pumpUntilNoTransientCallbacks
(
[
Duration
duration
=
const
Duration
(
milliseconds:
100
),
EnginePhase
phase
=
EnginePhase
.
sendSemanticsTree
])
{
assert
(
duration
!=
null
);
...
...
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