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
24efb55b
Unverified
Commit
24efb55b
authored
Feb 21, 2018
by
Hans Muller
Committed by
GitHub
Feb 21, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Generalized TabBar selected tab indicator (#14741)
parent
21c514fc
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
578 additions
and
181 deletions
+578
-181
material.dart
packages/flutter/lib/material.dart
+1
-0
tab_indicator.dart
packages/flutter/lib/src/material/tab_indicator.dart
+95
-0
tabs.dart
packages/flutter/lib/src/material/tabs.dart
+180
-49
decoration.dart
packages/flutter/lib/src/painting/decoration.dart
+1
-1
tabs_test.dart
packages/flutter/test/material/tabs_test.dart
+267
-124
mock_canvas.dart
packages/flutter/test/rendering/mock_canvas.dart
+34
-7
No files found.
packages/flutter/lib/material.dart
View file @
24efb55b
...
...
@@ -81,6 +81,7 @@ export 'src/material/stepper.dart';
export
'src/material/switch.dart'
;
export
'src/material/switch_list_tile.dart'
;
export
'src/material/tab_controller.dart'
;
export
'src/material/tab_indicator.dart'
;
export
'src/material/tabs.dart'
;
export
'src/material/text_field.dart'
;
export
'src/material/text_form_field.dart'
;
...
...
packages/flutter/lib/src/material/tab_indicator.dart
0 → 100644
View file @
24efb55b
// Copyright 2018 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
'package:flutter/widgets.dart'
;
import
'colors.dart'
;
/// Used with [TabBar.indicator] to draw a horizontal line below the
/// selected tab.
///
/// The selected tab underline is inset from the tab's boundary by [insets].
/// The [borderSide] defines the line's color and weight.
///
/// The [TabBar.indicatorSize] property can be used to define the indicator's
/// bounds in terms of its (centered) widget with [TabIndicatorSize.label],
/// or the entire tab with [TabIndicatorSize.tab].
class
UnderlineTabIndicator
extends
Decoration
{
/// Create an underline style selected tab indicator.
///
/// The [borderSide] and [insets] arguments must not be null.
const
UnderlineTabIndicator
({
this
.
borderSide
:
const
BorderSide
(
width:
2.0
,
color:
Colors
.
white
),
this
.
insets
:
EdgeInsets
.
zero
,
})
:
assert
(
borderSide
!=
null
),
assert
(
insets
!=
null
);
/// The color and weight of the horizontal line drawn below the selected tab.
final
BorderSide
borderSide
;
/// Locates the selected tab's underline relative to the tab's boundary.
///
/// The [TabBar.indicatorSize] property can be used to define the
/// tab indicator's bounds in terms of its (centered) tab widget with
/// [TabIndicatorSize.label], or the entire tab with [TabIndicatorSize.tab].
final
EdgeInsetsGeometry
insets
;
@override
Decoration
lerpFrom
(
Decoration
a
,
double
t
)
{
if
(
a
is
UnderlineTabIndicator
)
{
return
new
UnderlineTabIndicator
(
borderSide:
BorderSide
.
lerp
(
a
.
borderSide
,
borderSide
,
t
),
insets:
EdgeInsetsGeometry
.
lerp
(
a
.
insets
,
insets
,
t
),
);
}
return
super
.
lerpFrom
(
a
,
t
);
}
@override
Decoration
lerpTo
(
Decoration
b
,
double
t
)
{
if
(
b
is
UnderlineTabIndicator
)
{
return
new
UnderlineTabIndicator
(
borderSide:
BorderSide
.
lerp
(
borderSide
,
b
.
borderSide
,
t
),
insets:
EdgeInsetsGeometry
.
lerp
(
insets
,
b
.
insets
,
t
),
);
}
return
super
.
lerpTo
(
b
,
t
);
}
@override
_UnderlinePainter
createBoxPainter
([
VoidCallback
onChanged
])
{
return
new
_UnderlinePainter
(
this
,
onChanged
);
}
}
class
_UnderlinePainter
extends
BoxPainter
{
_UnderlinePainter
(
this
.
decoration
,
VoidCallback
onChanged
)
:
assert
(
decoration
!=
null
),
super
(
onChanged
);
final
UnderlineTabIndicator
decoration
;
BorderSide
get
borderSide
=>
decoration
.
borderSide
;
EdgeInsetsGeometry
get
insets
=>
decoration
.
insets
;
Rect
_indicatorRectFor
(
Rect
rect
,
TextDirection
textDirection
)
{
assert
(
rect
!=
null
);
assert
(
textDirection
!=
null
);
final
Rect
indicator
=
insets
.
resolve
(
textDirection
).
deflateRect
(
rect
);
return
new
Rect
.
fromLTWH
(
indicator
.
left
,
indicator
.
bottom
-
borderSide
.
width
,
indicator
.
width
,
borderSide
.
width
,
);
}
@override
void
paint
(
Canvas
canvas
,
Offset
offset
,
ImageConfiguration
configuration
)
{
assert
(
configuration
!=
null
);
assert
(
configuration
.
size
!=
null
);
final
Rect
rect
=
offset
&
configuration
.
size
;
final
TextDirection
textDirection
=
configuration
.
textDirection
;
final
Rect
indicator
=
_indicatorRectFor
(
rect
,
textDirection
).
deflate
(
borderSide
.
width
/
2.0
);
canvas
.
drawLine
(
indicator
.
bottomLeft
,
indicator
.
bottomRight
,
borderSide
.
toPaint
());
}
}
packages/flutter/lib/src/material/tabs.dart
View file @
24efb55b
...
...
@@ -3,7 +3,6 @@
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:math'
as
math
;
import
'dart:ui'
show
lerpDouble
;
import
'package:flutter/foundation.dart'
;
...
...
@@ -18,11 +17,31 @@ import 'ink_well.dart';
import
'material.dart'
;
import
'material_localizations.dart'
;
import
'tab_controller.dart'
;
import
'tab_indicator.dart'
;
import
'theme.dart'
;
const
double
_kTabHeight
=
46.0
;
const
double
_kTextAndIconTabHeight
=
72.0
;
const
double
_kMinTabWidth
=
72.0
;
/// Defines how the bounds of the selected tab indicator are computed.
///
/// See also:
/// * [TabBar], which displays a row of tabs.
/// * [TabBarView], which displays a widget for the currently selected tab.
/// * [TabBar.indicator], which defines the appearance of the selected tab
/// indicator relative to the tab's bounds.
enum
TabBarIndicatorSize
{
/// The tab indicator's bounds are as wide as the space occupied by the tab
/// in the tab bar: from the right edge of the previous tab to the left edge
/// of the next tab.
tab
,
/// The tab's bounds are only as wide as the (centered) tab widget itself.
///
/// This value is used to align the tab's label, typically a [Tab]
/// widget's text or icon, with the selected tab indicator.
label
,
}
/// A material design [TabBar] tab. If both [icon] and [text] are
/// provided, the text is displayed below the icon.
...
...
@@ -85,18 +104,19 @@ class Tab extends StatelessWidget {
children:
<
Widget
>[
new
Container
(
child:
icon
,
margin:
const
EdgeInsets
.
only
(
bottom:
10.0
)
margin:
const
EdgeInsets
.
only
(
bottom:
10.0
)
,
),
_buildLabelText
()
]
);
}
return
new
Container
(
padding:
kTabLabelPadding
,
return
new
SizedBox
(
height:
height
,
constraints:
const
BoxConstraints
(
minWidth:
_kMinTabWidth
),
child:
new
Center
(
child:
label
),
child:
new
Center
(
child:
label
,
widthFactor:
1.0
,
),
);
}
...
...
@@ -266,32 +286,38 @@ double _indexChangeProgress(TabController controller) {
class
_IndicatorPainter
extends
CustomPainter
{
_IndicatorPainter
({
@required
this
.
controller
,
@required
this
.
indicatorWeight
,
@required
this
.
indicatorPadding
,
@required
this
.
indicator
,
@required
this
.
indicatorSize
,
@required
this
.
tabKeys
,
_IndicatorPainter
old
,
})
:
assert
(
controller
!=
null
),
assert
(
indicatorWeight
!=
null
),
assert
(
indicatorPadding
!=
null
),
assert
(
indicator
!=
null
),
super
(
repaint:
controller
.
animation
)
{
if
(
old
!=
null
)
saveTabOffsets
(
old
.
_currentTabOffsets
,
old
.
_currentTextDirection
);
}
final
TabController
controller
;
final
double
indicatorWeight
;
final
EdgeInsetsGeometry
indicatorPadding
;
final
Decoration
indicator
;
final
TabBarIndicatorSize
indicatorSize
;
final
List
<
GlobalKey
>
tabKeys
;
List
<
double
>
_currentTabOffsets
;
TextDirection
_currentTextDirection
;
EdgeInsets
_resolvedIndicatorPadding
;
Color
_color
;
Rect
_currentRect
;
BoxPainter
_painter
;
bool
_needsPaint
=
false
;
void
markNeedsPaint
()
{
_needsPaint
=
true
;
}
void
dispose
()
{
_painter
?.
dispose
();
}
void
saveTabOffsets
(
List
<
double
>
tabOffsets
,
TextDirection
textDirection
)
{
_currentTabOffsets
=
tabOffsets
;
_currentTextDirection
=
textDirection
;
_resolvedIndicatorPadding
=
indicatorPadding
.
resolve
(
_currentTextDirection
);
}
// _currentTabOffsets[index] is the offset of the start edge of the tab at index, and
...
...
@@ -323,14 +349,22 @@ class _IndicatorPainter extends CustomPainter {
tabRight
=
_currentTabOffsets
[
tabIndex
+
1
];
break
;
}
tabLeft
=
math
.
min
(
tabLeft
+
_resolvedIndicatorPadding
.
left
,
tabRight
);
tabRight
=
math
.
max
(
tabRight
-
_resolvedIndicatorPadding
.
right
,
tabLeft
);
final
double
tabTop
=
tabBarSize
.
height
-
indicatorWeight
;
return
new
Rect
.
fromLTWH
(
tabLeft
,
tabTop
,
tabRight
-
tabLeft
,
indicatorWeight
);
if
(
indicatorSize
==
TabBarIndicatorSize
.
label
)
{
final
double
tabWidth
=
tabKeys
[
tabIndex
].
currentContext
.
size
.
width
;
final
double
delta
=
((
tabRight
-
tabLeft
)
-
tabWidth
)
/
2.0
;
tabLeft
+=
delta
;
tabRight
-=
delta
;
}
return
new
Rect
.
fromLTWH
(
tabLeft
,
0.0
,
tabRight
-
tabLeft
,
tabBarSize
.
height
);
}
@override
void
paint
(
Canvas
canvas
,
Size
size
)
{
_needsPaint
=
false
;
_painter
??=
indicator
.
createBoxPainter
(
markNeedsPaint
);
if
(
controller
.
indexIsChanging
)
{
// The user tapped on a tab, the tab controller's animation is running.
final
Rect
targetRect
=
indicatorRect
(
size
,
controller
.
index
);
...
...
@@ -355,7 +389,12 @@ class _IndicatorPainter extends CustomPainter {
_currentRect
=
next
==
null
?
middle
:
Rect
.
lerp
(
middle
,
next
,
value
-
index
);
}
assert
(
_currentRect
!=
null
);
canvas
.
drawRect
(
_currentRect
,
new
Paint
()..
color
=
_color
);
final
ImageConfiguration
configuration
=
new
ImageConfiguration
(
size:
_currentRect
.
size
,
textDirection:
_currentTextDirection
,
);
_painter
.
paint
(
canvas
,
_currentRect
.
topLeft
,
configuration
);
}
static
bool
_tabOffsetsEqual
(
List
<
double
>
a
,
List
<
double
>
b
)
{
...
...
@@ -370,9 +409,10 @@ class _IndicatorPainter extends CustomPainter {
@override
bool
shouldRepaint
(
_IndicatorPainter
old
)
{
return
controller
!=
old
.
controller
||
indicatorWeight
!=
old
.
indicatorWeight
||
indicatorPadding
!=
old
.
indicatorPadding
return
_needsPaint
||
controller
!=
old
.
controller
||
indicator
!=
old
.
indicator
||
tabKeys
.
length
!=
old
.
tabKeys
.
length
||
(!
_tabOffsetsEqual
(
_currentTabOffsets
,
old
.
_currentTabOffsets
))
||
_currentTextDirection
!=
old
.
_currentTextDirection
;
}
...
...
@@ -480,6 +520,9 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
/// The [indicatorWeight] parameter defaults to 2, and must not be null.
///
/// The [indicatorPadding] parameter defaults to [EdgeInsets.zero], and must not be null.
///
/// If [indicator] is not null, then [indicatorWeight], [indicatorPadding], and
/// [indicatorColor] are ignored.
const
TabBar
({
Key
key
,
@required
this
.
tabs
,
...
...
@@ -488,14 +531,16 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
this
.
indicatorColor
,
this
.
indicatorWeight
:
2.0
,
this
.
indicatorPadding
:
EdgeInsets
.
zero
,
this
.
indicator
,
this
.
indicatorSize
,
this
.
labelColor
,
this
.
labelStyle
,
this
.
unselectedLabelColor
,
this
.
unselectedLabelStyle
,
})
:
assert
(
tabs
!=
null
),
assert
(
isScrollable
!=
null
),
assert
(
indicator
Weight
!=
null
&&
indicatorWeight
>
0.0
),
assert
(
indicator
Padding
!=
null
),
assert
(
indicator
!=
null
||
(
indicatorWeight
!=
null
&&
indicatorWeight
>
0.0
)
),
assert
(
indicator
!=
null
||
(
indicatorPadding
!=
null
)
),
super
(
key:
key
);
/// Typically a list of two or more [Tab] widgets.
...
...
@@ -518,12 +563,16 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
/// The color of the line that appears below the selected tab. If this parameter
/// is null then the value of the Theme's indicatorColor property is used.
///
/// If [indicator] is specified, this property is ignored.
final
Color
indicatorColor
;
/// The thickness of the line that appears below the selected tab. The value
/// of this parameter must be greater than zero.
///
/// The default value of [indicatorWeight] is 2.0.
///
/// If [indicator] is specified, this property is ignored.
final
double
indicatorWeight
;
/// The horizontal padding for the line that appears below the selected tab.
...
...
@@ -535,8 +584,37 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
/// [indicatorPadding] are ignored.
///
/// The default value of [indicatorPadding] is [EdgeInsets.zero].
///
/// If [indicator] is specified, this property is ignored.
final
EdgeInsetsGeometry
indicatorPadding
;
/// Defines the appearance of the selected tab indicator.
///
/// If [indicator] is specified, the [indicatorColor], [indicatorWeight],
/// and [indicatorPadding] properties are ignored.
///
/// The default, underline-style, selected tab indicator can be defined with
/// [UnderlineTabIndicator].
///
/// The indicator's size is based on the tab's bounds. If [indicatorSize]
/// is [TabBarIndicatorSize.tab] the tab's bounds are as wide as the space
/// occupied by the tab in the tab bar. If [indicatorSize] is
/// [TabBarIndicatorSize.label] then the tab's bounds are only as wide as
/// the tab widget itself.
final
Decoration
indicator
;
/// Defines how the selected tab indicator's size is computed.
///
/// The size of the selected tab indicator is defined relative to the
/// tab's overall bounds if [indicatorSize] is [TabBarIndicatorSize.tab]
/// (the default) or relative to the bounds of the tab's widget if
/// [indicatorSize] is [TabBarIndicatorSize.label].
///
/// The selected tab's location appearance can be refined further with
/// the [indicatorColor], [indicatorWeight], [indicatorPadding], and
/// [indicator] properties.
final
TabBarIndicatorSize
indicatorSize
;
/// The color of selected tab labels.
///
/// Unselected tab labels are rendered with the same color rendered at 70%
...
...
@@ -591,6 +669,39 @@ class _TabBarState extends State<TabBar> {
_IndicatorPainter
_indicatorPainter
;
int
_currentIndex
;
double
_tabStripWidth
;
List
<
GlobalKey
>
_tabKeys
;
@override
void
initState
()
{
super
.
initState
();
// If indicatorSize is TabIndicatorSize.label, _tabKeys[i] is used to find
// the width of tab widget i. See _IndicatorPainter.indicatorRect().
_tabKeys
=
widget
.
tabs
.
map
((
Widget
tab
)
=>
new
GlobalKey
()).
toList
();
}
Decoration
get
_indicator
{
if
(
widget
.
indicator
!=
null
)
return
widget
.
indicator
;
Color
color
=
widget
.
indicatorColor
??
Theme
.
of
(
context
).
indicatorColor
;
// ThemeData tries to avoid this by having indicatorColor avoid being the
// primaryColor. However, it's possible that the tab bar is on a
// Material that isn't the primaryColor. In that case, if the indicator
// color ends up matching the material's color, then this overrides it.
// When that happens, automatic transitions of the theme will likely look
// ugly as the indicator color suddenly snaps to white at one end, but it's
// not clear how to avoid that any further.
if
(
color
==
Material
.
of
(
context
).
color
)
color
=
Colors
.
white
;
return
new
UnderlineTabIndicator
(
insets:
widget
.
indicatorPadding
,
borderSide:
new
BorderSide
(
width:
widget
.
indicatorWeight
,
color:
color
,
),
);
}
void
_updateTabController
()
{
final
TabController
newController
=
widget
.
controller
??
DefaultTabController
.
of
(
context
);
...
...
@@ -618,19 +729,24 @@ class _TabBarState extends State<TabBar> {
_controller
.
animation
.
addListener
(
_handleTabControllerAnimationTick
);
_controller
.
addListener
(
_handleTabControllerTick
);
_currentIndex
=
_controller
.
index
;
_indicatorPainter
=
new
_IndicatorPainter
(
controller:
_controller
,
indicatorWeight:
widget
.
indicatorWeight
,
indicatorPadding:
widget
.
indicatorPadding
,
old:
_indicatorPainter
,
);
}
}
void
_initIndicatorPainter
()
{
_indicatorPainter
=
_controller
==
null
?
null
:
new
_IndicatorPainter
(
controller:
_controller
,
indicator:
_indicator
,
indicatorSize:
widget
.
indicatorSize
,
tabKeys:
_tabKeys
,
old:
_indicatorPainter
,
);
}
@override
void
didChangeDependencies
()
{
super
.
didChangeDependencies
();
_updateTabController
();
_initIndicatorPainter
();
}
@override
...
...
@@ -638,10 +754,24 @@ class _TabBarState extends State<TabBar> {
super
.
didUpdateWidget
(
oldWidget
);
if
(
widget
.
controller
!=
oldWidget
.
controller
)
_updateTabController
();
if
(
widget
.
indicatorColor
!=
oldWidget
.
indicatorColor
||
widget
.
indicatorWeight
!=
oldWidget
.
indicatorWeight
||
widget
.
indicatorSize
!=
oldWidget
.
indicatorSize
||
widget
.
indicator
!=
oldWidget
.
indicator
)
_initIndicatorPainter
();
if
(
widget
.
tabs
.
length
>
oldWidget
.
tabs
.
length
)
{
final
int
delta
=
widget
.
tabs
.
length
-
oldWidget
.
tabs
.
length
;
_tabKeys
.
addAll
(
new
List
<
GlobalKey
>.
generate
(
delta
,
(
int
n
)
=>
new
GlobalKey
()));
}
else
if
(
widget
.
tabs
.
length
<
oldWidget
.
tabs
.
length
)
{
_tabKeys
.
removeRange
(
widget
.
tabs
.
length
,
oldWidget
.
tabs
.
length
);
}
}
@override
void
dispose
()
{
_indicatorPainter
.
dispose
();
if
(
_controller
!=
null
)
{
_controller
.
animation
.
removeListener
(
_handleTabControllerAnimationTick
);
_controller
.
removeListener
(
_handleTabControllerTick
);
...
...
@@ -755,24 +885,25 @@ class _TabBarState extends State<TabBar> {
);
}
final
List
<
Widget
>
wrappedTabs
=
new
List
<
Widget
>.
from
(
widget
.
tabs
,
growable:
false
);
final
List
<
Widget
>
wrappedTabs
=
new
List
<
Widget
>(
widget
.
tabs
.
length
);
for
(
int
i
=
0
;
i
<
widget
.
tabs
.
length
;
i
+=
1
)
{
wrappedTabs
[
i
]
=
new
Center
(
heightFactor:
1.0
,
child:
new
Padding
(
padding:
kTabLabelPadding
,
child:
new
KeyedSubtree
(
key:
_tabKeys
[
i
],
child:
widget
.
tabs
[
i
],
),
),
);
}
// If the controller was provided by DefaultTabController and we're part
// of a Hero (typically the AppBar), then we will not be able to find the
// controller during a Hero transition. See https://github.com/flutter/flutter/issues/213.
if
(
_controller
!=
null
)
{
_indicatorPainter
.
_color
=
widget
.
indicatorColor
??
Theme
.
of
(
context
).
indicatorColor
;
if
(
_indicatorPainter
.
_color
==
Material
.
of
(
context
).
color
)
{
// ThemeData tries to avoid this by having indicatorColor avoid being the
// primaryColor. However, it's possible that the tab bar is on a
// Material that isn't the primaryColor. In that case, if the indicator
// color ends up clashing, then this overrides it. When that happens,
// automatic transitions of the theme will likely look ugly as the
// indicator color suddenly snaps to white at one end, but it's not clear
// how to avoid that any further.
_indicatorPainter
.
_color
=
Colors
.
white
;
}
final
int
previousIndex
=
_controller
.
previousIndex
;
if
(
_controller
.
indexIsChanging
)
{
...
...
@@ -799,9 +930,9 @@ class _TabBarState extends State<TabBar> {
}
}
// Add the tap handler to each tab. If the tab bar is scrollable
// then give all of the tabs equal flexibility so that the
ir widths
//
reflect the intrinsic width of their labels
.
// Add the tap handler to each tab. If the tab bar is
not
scrollable
// then give all of the tabs equal flexibility so that the
y each occupy
//
the same share of the tab bar's overall width
.
final
int
tabCount
=
widget
.
tabs
.
length
;
for
(
int
index
=
0
;
index
<
tabCount
;
index
+=
1
)
{
wrappedTabs
[
index
]
=
new
InkWell
(
...
...
packages/flutter/lib/src/painting/decoration.dart
View file @
24efb55b
...
...
@@ -188,7 +188,7 @@ abstract class Decoration extends Diagnosticable {
abstract
class
BoxPainter
{
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
BoxPainter
([
this
.
onChanged
]);
const
BoxPainter
([
this
.
onChanged
]);
/// Paints the [Decoration] for which this object was created on the
/// given canvas using the given configuration.
...
...
packages/flutter/test/material/tabs_test.dart
View file @
24efb55b
...
...
@@ -140,9 +140,11 @@ class TabIndicatorRecordingCanvas extends TestRecordingCanvas {
Rect
indicatorRect
;
@override
void
drawRect
(
Rect
rect
,
Paint
paint
)
{
void
drawLine
(
Offset
p1
,
Offset
p2
,
Paint
paint
)
{
// Assuming that the indicatorWeight is 2.0, the default.
const
double
indicatorWeight
=
2.0
;
if
(
paint
.
color
==
indicatorColor
)
indicatorRect
=
rect
;
indicatorRect
=
new
Rect
.
fromPoints
(
p1
,
p2
).
inflate
(
indicatorWeight
/
2.0
)
;
}
}
...
...
@@ -926,21 +928,22 @@ void main() {
// The initialIndex tab should be visible and right justified
expect
(
find
.
text
(
'TAB #19'
),
findsOneWidget
);
expect
(
tester
.
getTopRight
(
find
.
widgetWithText
(
Tab
,
'TAB #19'
)).
dx
,
800.0
);
// Tabs have a minimum width of 72.0 and 'TAB #19' is wider than
// that. Tabs are padded horizontally with kTabLabelPadding.
final
double
tabRight
=
800.0
-
kTabLabelPadding
.
right
;
expect
(
tester
.
getTopRight
(
find
.
widgetWithText
(
Tab
,
'TAB #19'
)).
dx
,
tabRight
);
});
testWidgets
(
'TabBar with indicatorWeight, indicatorPadding (LTR)'
,
(
WidgetTester
tester
)
async
{
const
Color
color
=
const
Color
(
0xFF00FF00
);
const
double
height
=
100.0
;
const
double
weight
=
8.0
;
const
Color
indicatorColor
=
const
Color
(
0xFF00FF00
);
const
double
indicatorWeight
=
8.0
;
const
double
padLeft
=
8.0
;
const
double
padRight
=
4.0
;
final
List
<
Widget
>
tabs
=
new
List
<
Widget
>.
generate
(
4
,
(
int
index
)
{
return
new
Container
(
key:
new
ValueKey
<
int
>(
index
),
height:
height
,
);
return
new
Tab
(
text:
'Tab
$index
'
);
});
final
TabController
controller
=
new
TabController
(
...
...
@@ -950,61 +953,56 @@ void main() {
await
tester
.
pumpWidget
(
boilerplate
(
child:
new
Column
(
children:
<
Widget
>[
new
TabBar
(
indicatorWeight:
8.0
,
indicatorColor:
color
,
indicatorPadding:
const
EdgeInsets
.
only
(
left:
padLeft
,
right:
padRight
),
controller:
controller
,
tabs:
tabs
,
),
new
Flexible
(
child:
new
Container
()),
],
child:
new
Container
(
alignment:
Alignment
.
topLeft
,
child:
new
TabBar
(
indicatorWeight:
indicatorWeight
,
indicatorColor:
indicatorColor
,
indicatorPadding:
const
EdgeInsets
.
only
(
left:
padLeft
,
right:
padRight
),
controller:
controller
,
tabs:
tabs
,
),
),
),
);
final
RenderBox
tabBarBox
=
tester
.
firstRenderObject
<
RenderBox
>(
find
.
byType
(
TabBar
));
expect
(
tabBarBox
.
size
.
height
,
54.0
);
// 54 = _kTabHeight(46) + indicatorWeight(8.0)
// Selected tab dimensions
double
tabWidth
=
tester
.
getSize
(
find
.
byKey
(
const
ValueKey
<
int
>(
0
))).
width
;
double
tabLeft
=
tester
.
getTopLeft
(
find
.
byKey
(
const
ValueKey
<
int
>(
0
))).
dx
;
double
tabRight
=
tabLeft
+
tabWidth
;
final
double
indicatorY
=
54.0
-
indicatorWeight
/
2.0
;
double
indicatorLeft
=
padLeft
+
indicatorWeight
/
2.0
;
double
indicatorRight
=
200.0
-
(
padRight
+
indicatorWeight
/
2.0
);
expect
(
tabBarBox
,
paints
..
rect
(
style:
PaintingStyle
.
fill
,
color:
color
,
rect:
new
Rect
.
fromLTRB
(
tabLeft
+
padLeft
,
height
,
tabRight
-
padRight
,
height
+
weight
)
expect
(
tabBarBox
,
paints
..
line
(
color:
indicatorColor
,
strokeWidth:
indicatorWeight
,
p1:
new
Offset
(
indicatorLeft
,
indicatorY
),
p2:
new
Offset
(
indicatorRight
,
indicatorY
),
));
// Select tab 3
controller
.
index
=
3
;
await
tester
.
pumpAndSettle
();
tabWidth
=
tester
.
getSize
(
find
.
byKey
(
const
ValueKey
<
int
>(
3
))).
width
;
tabLeft
=
tester
.
getTopLeft
(
find
.
byKey
(
const
ValueKey
<
int
>(
3
))).
dx
;
tabRight
=
tabLeft
+
tabWidth
;
indicatorLeft
=
600.0
+
padLeft
+
indicatorWeight
/
2.0
;
indicatorRight
=
800.0
-
(
padRight
+
indicatorWeight
/
2.0
);
expect
(
tabBarBox
,
paints
..
rect
(
style:
PaintingStyle
.
fill
,
color:
color
,
rect:
new
Rect
.
fromLTRB
(
tabLeft
+
padLeft
,
height
,
tabRight
-
padRight
,
height
+
weight
)
expect
(
tabBarBox
,
paints
..
line
(
color:
indicatorColor
,
strokeWidth:
indicatorWeight
,
p1:
new
Offset
(
indicatorLeft
,
indicatorY
),
p2:
new
Offset
(
indicatorRight
,
indicatorY
),
));
});
testWidgets
(
'TabBar with indicatorWeight, indicatorPadding (RTL)'
,
(
WidgetTester
tester
)
async
{
const
Color
color
=
const
Color
(
0xFF00FF00
);
const
double
height
=
100.0
;
const
double
weight
=
8.0
;
const
Color
indicatorColor
=
const
Color
(
0xFF00FF00
);
const
double
indicatorWeight
=
8.0
;
const
double
padLeft
=
8.0
;
const
double
padRight
=
4.0
;
final
List
<
Widget
>
tabs
=
new
List
<
Widget
>.
generate
(
4
,
(
int
index
)
{
return
new
Container
(
key:
new
ValueKey
<
int
>(
index
),
height:
height
,
);
return
new
Tab
(
text:
'Tab
$index
'
);
});
final
TabController
controller
=
new
TabController
(
...
...
@@ -1015,46 +1013,113 @@ void main() {
await
tester
.
pumpWidget
(
boilerplate
(
textDirection:
TextDirection
.
rtl
,
child:
new
Column
(
children:
<
Widget
>[
new
TabBar
(
indicatorWeight:
8.0
,
indicatorColor:
color
,
indicatorPadding:
const
EdgeInsets
.
only
(
left:
padLeft
,
right:
padRight
),
controller:
controller
,
tabs:
tabs
,
),
new
Flexible
(
child:
new
Container
()),
],
child:
new
Container
(
alignment:
Alignment
.
topLeft
,
child:
new
TabBar
(
indicatorWeight:
indicatorWeight
,
indicatorColor:
indicatorColor
,
indicatorPadding:
const
EdgeInsets
.
only
(
left:
padLeft
,
right:
padRight
),
controller:
controller
,
tabs:
tabs
,
),
),
),
);
final
RenderBox
tabBarBox
=
tester
.
firstRenderObject
<
RenderBox
>(
find
.
byType
(
TabBar
));
// Selected tab dimensions
double
tabWidth
=
tester
.
getSize
(
find
.
byKey
(
const
ValueKey
<
int
>(
0
))).
width
;
double
tabLeft
=
tester
.
getTopLeft
(
find
.
byKey
(
const
ValueKey
<
int
>(
0
))).
dx
;
double
tabRight
=
tabLeft
+
tabWidth
;
expect
(
tabBarBox
,
paints
..
rect
(
style:
PaintingStyle
.
fill
,
color:
color
,
rect:
new
Rect
.
fromLTRB
(
tabLeft
+
padLeft
,
height
,
tabRight
-
padRight
,
height
+
weight
)
expect
(
tabBarBox
.
size
.
height
,
54.0
);
// 54 = _kTabHeight(46) + indicatorWeight(8.0)
expect
(
tabBarBox
.
size
.
width
,
800.0
);
final
double
indicatorY
=
54.0
-
indicatorWeight
/
2.0
;
double
indicatorLeft
=
600.0
+
padLeft
+
indicatorWeight
/
2.0
;
double
indicatorRight
=
800.0
-
padRight
-
indicatorWeight
/
2.0
;
expect
(
tabBarBox
,
paints
..
line
(
color:
indicatorColor
,
strokeWidth:
indicatorWeight
,
p1:
new
Offset
(
indicatorLeft
,
indicatorY
),
p2:
new
Offset
(
indicatorRight
,
indicatorY
),
));
// Select tab 3
controller
.
index
=
3
;
await
tester
.
pumpAndSettle
();
tabWidth
=
tester
.
getSize
(
find
.
byKey
(
const
ValueKey
<
int
>(
3
))).
width
;
tabLeft
=
tester
.
getTopLeft
(
find
.
byKey
(
const
ValueKey
<
int
>(
3
))).
dx
;
tabRight
=
tabLeft
+
tabWidth
;
indicatorLeft
=
padLeft
+
indicatorWeight
/
2.0
;
indicatorRight
=
200.0
-
padRight
-
indicatorWeight
/
2.0
;
expect
(
tabBarBox
,
paints
..
line
(
color:
indicatorColor
,
strokeWidth:
indicatorWeight
,
p1:
new
Offset
(
indicatorLeft
,
indicatorY
),
p2:
new
Offset
(
indicatorRight
,
indicatorY
),
));
});
testWidgets
(
'TabBar changes indicator attributes'
,
(
WidgetTester
tester
)
async
{
final
List
<
Widget
>
tabs
=
new
List
<
Widget
>.
generate
(
4
,
(
int
index
)
{
return
new
Tab
(
text:
'Tab
$index
'
);
});
final
TabController
controller
=
new
TabController
(
vsync:
const
TestVSync
(),
length:
tabs
.
length
,
);
Color
indicatorColor
=
const
Color
(
0xFF00FF00
);
double
indicatorWeight
=
8.0
;
double
padLeft
=
8.0
;
double
padRight
=
4.0
;
Widget
buildFrame
()
{
return
boilerplate
(
child:
new
Container
(
alignment:
Alignment
.
topLeft
,
child:
new
TabBar
(
indicatorWeight:
indicatorWeight
,
indicatorColor:
indicatorColor
,
indicatorPadding:
new
EdgeInsets
.
only
(
left:
padLeft
,
right:
padRight
),
controller:
controller
,
tabs:
tabs
,
),
),
);
}
await
tester
.
pumpWidget
(
buildFrame
());
final
RenderBox
tabBarBox
=
tester
.
firstRenderObject
<
RenderBox
>(
find
.
byType
(
TabBar
));
expect
(
tabBarBox
.
size
.
height
,
54.0
);
// 54 = _kTabHeight(46) + indicatorWeight(8.0)
expect
(
tabBarBox
,
paints
..
rect
(
style:
PaintingStyle
.
fill
,
color:
color
,
rect:
new
Rect
.
fromLTRB
(
tabLeft
+
padLeft
,
height
,
tabRight
-
padRight
,
height
+
weight
)
double
indicatorY
=
54.0
-
indicatorWeight
/
2.0
;
double
indicatorLeft
=
padLeft
+
indicatorWeight
/
2.0
;
double
indicatorRight
=
200.0
-
(
padRight
+
indicatorWeight
/
2.0
);
expect
(
tabBarBox
,
paints
..
line
(
color:
indicatorColor
,
strokeWidth:
indicatorWeight
,
p1:
new
Offset
(
indicatorLeft
,
indicatorY
),
p2:
new
Offset
(
indicatorRight
,
indicatorY
),
));
indicatorColor
=
const
Color
(
0xFF0000FF
);
indicatorWeight
=
4.0
;
padLeft
=
4.0
;
padRight
=
8.0
;
await
tester
.
pumpWidget
(
buildFrame
());
expect
(
tabBarBox
.
size
.
height
,
50.0
);
// 54 = _kTabHeight(46) + indicatorWeight(4.0)
indicatorY
=
50.0
-
indicatorWeight
/
2.0
;
indicatorLeft
=
padLeft
+
indicatorWeight
/
2.0
;
indicatorRight
=
200.0
-
(
padRight
+
indicatorWeight
/
2.0
);
expect
(
tabBarBox
,
paints
..
line
(
color:
indicatorColor
,
strokeWidth:
indicatorWeight
,
p1:
new
Offset
(
indicatorLeft
,
indicatorY
),
p2:
new
Offset
(
indicatorRight
,
indicatorY
),
));
});
...
...
@@ -1065,6 +1130,8 @@ void main() {
new
SizedBox
(
key:
new
UniqueKey
(),
width:
150.0
,
height:
50.0
),
];
const
double
indicatorWeight
=
2.0
;
// the default
final
TabController
controller
=
new
TabController
(
vsync:
const
TestVSync
(),
length:
tabs
.
length
,
...
...
@@ -1072,27 +1139,56 @@ void main() {
await
tester
.
pumpWidget
(
boilerplate
(
child:
new
Center
(
child:
new
SizedBox
(
width:
800.0
,
child:
new
TabBar
(
indicatorPadding:
const
EdgeInsetsDirectional
.
only
(
start:
100.0
),
isScrollable:
true
,
controller:
controller
,
tabs:
tabs
,
),
child:
new
Container
(
alignment:
Alignment
.
topLeft
,
child:
new
TabBar
(
indicatorPadding:
const
EdgeInsetsDirectional
.
only
(
start:
100.0
),
isScrollable:
true
,
controller:
controller
,
tabs:
tabs
,
),
),
),
);
expect
(
tester
.
getRect
(
find
.
byKey
(
tabs
[
0
].
key
)),
new
Rect
.
fromLTRB
(
0.0
,
284.0
,
130.0
,
314.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
tabs
[
1
].
key
)),
new
Rect
.
fromLTRB
(
130.0
,
279.0
,
270.0
,
319.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
tabs
[
2
].
key
)),
new
Rect
.
fromLTRB
(
270.0
,
274.0
,
420.0
,
324.0
));
expect
(
tester
.
firstRenderObject
<
RenderBox
>(
find
.
byType
(
TabBar
)),
paints
..
rect
(
style:
PaintingStyle
.
fill
,
rect:
new
Rect
.
fromLTRB
(
100.0
,
50.0
,
130.0
,
52.0
),
final
RenderBox
tabBarBox
=
tester
.
firstRenderObject
<
RenderBox
>(
find
.
byType
(
TabBar
));
const
double
tabBarHeight
=
50.0
+
indicatorWeight
;
// 50 = max tab height
expect
(
tabBarBox
.
size
.
height
,
tabBarHeight
);
// Tab0 width = 130, height = 30
double
tabLeft
=
kTabLabelPadding
.
left
;
double
tabRight
=
tabLeft
+
130.0
;
double
tabTop
=
(
tabBarHeight
-
indicatorWeight
-
30.0
)
/
2.0
;
double
tabBottom
=
tabTop
+
30.0
;
Rect
tabRect
=
new
Rect
.
fromLTRB
(
tabLeft
,
tabTop
,
tabRight
,
tabBottom
);
expect
(
tester
.
getRect
(
find
.
byKey
(
tabs
[
0
].
key
)),
tabRect
);
// Tab1 width = 140, height = 40
tabLeft
=
tabRight
+
kTabLabelPadding
.
right
+
kTabLabelPadding
.
left
;
tabRight
=
tabLeft
+
140.0
;
tabTop
=
(
tabBarHeight
-
indicatorWeight
-
40.0
)
/
2.0
;
tabBottom
=
tabTop
+
40.0
;
tabRect
=
new
Rect
.
fromLTRB
(
tabLeft
,
tabTop
,
tabRight
,
tabBottom
);
expect
(
tester
.
getRect
(
find
.
byKey
(
tabs
[
1
].
key
)),
tabRect
);
// Tab2 width = 150, height = 50
tabLeft
=
tabRight
+
kTabLabelPadding
.
right
+
kTabLabelPadding
.
left
;
tabRight
=
tabLeft
+
150.0
;
tabTop
=
(
tabBarHeight
-
indicatorWeight
-
50.0
)
/
2.0
;
tabBottom
=
tabTop
+
50.0
;
tabRect
=
new
Rect
.
fromLTRB
(
tabLeft
,
tabTop
,
tabRight
,
tabBottom
);
expect
(
tester
.
getRect
(
find
.
byKey
(
tabs
[
2
].
key
)),
tabRect
);
// Tab 0 selected, indicator padding resolves to left: 100.0
final
double
indicatorLeft
=
100.0
+
indicatorWeight
/
2.0
;
final
double
indicatorRight
=
130.0
+
kTabLabelPadding
.
horizontal
-
indicatorWeight
/
2.0
;
final
double
indicatorY
=
tabBottom
+
indicatorWeight
/
2.0
;
expect
(
tabBarBox
,
paints
..
line
(
strokeWidth:
indicatorWeight
,
p1:
new
Offset
(
indicatorLeft
,
indicatorY
),
p2:
new
Offset
(
indicatorRight
,
indicatorY
),
));
});
...
...
@@ -1103,6 +1199,8 @@ void main() {
new
SizedBox
(
key:
new
UniqueKey
(),
width:
150.0
,
height:
50.0
),
];
const
double
indicatorWeight
=
2.0
;
// the default
final
TabController
controller
=
new
TabController
(
vsync:
const
TestVSync
(),
length:
tabs
.
length
,
...
...
@@ -1111,36 +1209,62 @@ void main() {
await
tester
.
pumpWidget
(
boilerplate
(
textDirection:
TextDirection
.
rtl
,
child:
new
Center
(
child:
new
SizedBox
(
width:
800.0
,
child:
new
TabBar
(
indicatorPadding:
const
EdgeInsetsDirectional
.
only
(
start:
100.0
),
isScrollable:
true
,
controller:
controller
,
tabs:
tabs
,
),
child:
new
Container
(
alignment:
Alignment
.
topLeft
,
child:
new
TabBar
(
indicatorPadding:
const
EdgeInsetsDirectional
.
only
(
start:
100.0
),
isScrollable:
true
,
controller:
controller
,
tabs:
tabs
,
),
),
),
);
expect
(
tester
.
getRect
(
find
.
byKey
(
tabs
[
0
].
key
)),
new
Rect
.
fromLTRB
(
670.0
,
284.0
,
800.0
,
314.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
tabs
[
1
].
key
)),
new
Rect
.
fromLTRB
(
530.0
,
279.0
,
670.0
,
319.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
tabs
[
2
].
key
)),
new
Rect
.
fromLTRB
(
380.0
,
274.0
,
530.0
,
324.0
));
final
RenderBox
tabBar
=
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
CustomPaint
).
at
(
1
));
expect
(
tabBar
.
size
,
const
Size
(
420.0
,
52.0
));
expect
(
tabBar
,
paints
..
rect
(
style:
PaintingStyle
.
fill
,
rect:
new
Rect
.
fromLTRB
(
tabBar
.
size
.
width
-
130.0
,
50.0
,
tabBar
.
size
.
width
-
100.0
,
52.0
),
final
RenderBox
tabBarBox
=
tester
.
firstRenderObject
<
RenderBox
>(
find
.
byType
(
TabBar
));
const
double
tabBarHeight
=
50.0
+
indicatorWeight
;
// 50 = max tab height
expect
(
tabBarBox
.
size
.
height
,
tabBarHeight
);
// Tab2 width = 150, height = 50
double
tabLeft
=
kTabLabelPadding
.
left
;
double
tabRight
=
tabLeft
+
150.0
;
double
tabTop
=
(
tabBarHeight
-
indicatorWeight
-
50.0
)
/
2.0
;
double
tabBottom
=
tabTop
+
50.0
;
Rect
tabRect
=
new
Rect
.
fromLTRB
(
tabLeft
,
tabTop
,
tabRight
,
tabBottom
);
expect
(
tester
.
getRect
(
find
.
byKey
(
tabs
[
2
].
key
)),
tabRect
);
// Tab1 width = 140, height = 40
tabLeft
=
tabRight
+
kTabLabelPadding
.
right
+
kTabLabelPadding
.
left
;
tabRight
=
tabLeft
+
140.0
;
tabTop
=
(
tabBarHeight
-
indicatorWeight
-
40.0
)
/
2.0
;
tabBottom
=
tabTop
+
40.0
;
tabRect
=
new
Rect
.
fromLTRB
(
tabLeft
,
tabTop
,
tabRight
,
tabBottom
);
expect
(
tester
.
getRect
(
find
.
byKey
(
tabs
[
1
].
key
)),
tabRect
);
// Tab0 width = 130, height = 30
tabLeft
=
tabRight
+
kTabLabelPadding
.
right
+
kTabLabelPadding
.
left
;
tabRight
=
tabLeft
+
130.0
;
tabTop
=
(
tabBarHeight
-
indicatorWeight
-
30.0
)
/
2.0
;
tabBottom
=
tabTop
+
30.0
;
tabRect
=
new
Rect
.
fromLTRB
(
tabLeft
,
tabTop
,
tabRight
,
tabBottom
);
expect
(
tester
.
getRect
(
find
.
byKey
(
tabs
[
0
].
key
)),
tabRect
);
// Tab 0 selected, indicator padding resolves to right: 100.0
final
double
indicatorLeft
=
tabLeft
-
kTabLabelPadding
.
left
+
indicatorWeight
/
2.0
;
final
double
indicatorRight
=
tabRight
+
kTabLabelPadding
.
left
-
indicatorWeight
/
2.0
-
100.0
;
final
double
indicatorY
=
50.0
+
indicatorWeight
/
2.0
;
expect
(
tabBarBox
,
paints
..
line
(
strokeWidth:
indicatorWeight
,
p1:
new
Offset
(
indicatorLeft
,
indicatorY
),
p2:
new
Offset
(
indicatorRight
,
indicatorY
),
));
});
testWidgets
(
'Overflowing RTL tab bar'
,
(
WidgetTester
tester
)
async
{
final
List
<
Widget
>
tabs
=
new
List
<
Widget
>.
filled
(
100
,
new
SizedBox
(
key:
new
UniqueKey
(),
width:
30.0
,
height:
20.0
),
// For convenience padded width of each tab will equal 100:
// 76 + kTabLabelPadding.horizontal(24)
new
SizedBox
(
key:
new
UniqueKey
(),
width:
76.0
,
height:
40.0
),
);
final
TabController
controller
=
new
TabController
(
...
...
@@ -1148,10 +1272,13 @@ void main() {
length:
tabs
.
length
,
);
const
double
indicatorWeight
=
2.0
;
// the default
await
tester
.
pumpWidget
(
boilerplate
(
textDirection:
TextDirection
.
rtl
,
child:
new
Center
(
child:
new
Container
(
alignment:
Alignment
.
topLeft
,
child:
new
TabBar
(
isScrollable:
true
,
controller:
controller
,
...
...
@@ -1161,25 +1288,40 @@ void main() {
),
);
expect
(
tester
.
firstRenderObject
<
RenderBox
>(
find
.
byType
(
TabBar
)),
paints
..
rect
(
style:
PaintingStyle
.
fill
,
rect:
new
Rect
.
fromLTRB
(
2970.0
,
20.0
,
3000.0
,
22.0
),
final
RenderBox
tabBarBox
=
tester
.
firstRenderObject
<
RenderBox
>(
find
.
byType
(
TabBar
));
const
double
tabBarHeight
=
40.0
+
indicatorWeight
;
// 40 = tab height
expect
(
tabBarBox
.
size
.
height
,
tabBarHeight
);
// Tab 0 out of 100 selected
double
indicatorLeft
=
99.0
*
100.0
+
indicatorWeight
/
2.0
;
double
indicatorRight
=
100.0
*
100.0
-
indicatorWeight
/
2.0
;
final
double
indicatorY
=
40.0
+
indicatorWeight
/
2.0
;
expect
(
tabBarBox
,
paints
..
line
(
strokeWidth:
indicatorWeight
,
p1:
new
Offset
(
indicatorLeft
,
indicatorY
),
p2:
new
Offset
(
indicatorRight
,
indicatorY
),
));
controller
.
animateTo
(
tabs
.
length
-
1
,
duration:
const
Duration
(
seconds:
1
),
curve:
Curves
.
linear
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
tester
.
firstRenderObject
<
RenderBox
>(
find
.
byType
(
TabBar
)),
paints
..
rect
(
style:
PaintingStyle
.
fill
,
rect:
new
Rect
.
fromLTRB
(
742.5
,
20.0
,
772.5
,
22.0
),
// (these values were derived empirically, not analytically)
// The x coordinates of p1 and p2 were derived empirically, not analytically.
expect
(
tabBarBox
,
paints
..
line
(
strokeWidth:
indicatorWeight
,
p1:
new
Offset
(
2476.0
,
indicatorY
),
p2:
new
Offset
(
2574.0
,
indicatorY
),
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
501
));
expect
(
tester
.
firstRenderObject
<
RenderBox
>(
find
.
byType
(
TabBar
)),
paints
..
rect
(
style:
PaintingStyle
.
fill
,
rect:
new
Rect
.
fromLTRB
(
0.0
,
20.0
,
30.0
,
22.0
),
// Tab 99 out of 100 selected, appears on the far left because RTL
indicatorLeft
=
indicatorWeight
/
2.0
;
indicatorRight
=
100.0
-
indicatorWeight
/
2.0
;
expect
(
tabBarBox
,
paints
..
line
(
strokeWidth:
indicatorWeight
,
p1:
new
Offset
(
indicatorLeft
,
indicatorY
),
p2:
new
Offset
(
indicatorRight
,
indicatorY
),
));
});
...
...
@@ -1372,22 +1514,23 @@ void main() {
expect
(
tester
.
getSize
(
find
.
byType
(
TabBar
)),
const
Size
(
800.0
,
48.0
));
expect
(
tester
.
getSize
(
find
.
byType
(
TabBarView
)),
const
Size
(
800.0
,
600.0
-
48.0
));
// The one tab spans the app's width
expect
(
tester
.
getTopLeft
(
find
.
widgetWithText
(
Tab
,
'TAB'
)).
dx
,
0
);
expect
(
tester
.
getTopRight
(
find
.
widgetWithText
(
Tab
,
'TAB'
)).
dx
,
800
);
// The one tab should be center vis the app's width (800).
final
double
tabLeft
=
tester
.
getTopLeft
(
find
.
widgetWithText
(
Tab
,
'TAB'
)).
dx
;
final
double
tabRight
=
tester
.
getTopRight
(
find
.
widgetWithText
(
Tab
,
'TAB'
)).
dx
;
expect
(
tabLeft
+
(
tabRight
-
tabLeft
)
/
2.0
,
400.0
);
// A fling in the TabBar or TabBarView, shouldn't move the tab.
await
tester
.
fling
(
find
.
byType
(
TabBar
),
const
Offset
(-
100.0
,
0.0
),
5000.0
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
50
));
expect
(
tester
.
getTopLeft
(
find
.
widgetWithText
(
Tab
,
'TAB'
)).
dx
,
0
);
expect
(
tester
.
getTopRight
(
find
.
widgetWithText
(
Tab
,
'TAB'
)).
dx
,
800
);
expect
(
tester
.
getTopLeft
(
find
.
widgetWithText
(
Tab
,
'TAB'
)).
dx
,
tabLeft
);
expect
(
tester
.
getTopRight
(
find
.
widgetWithText
(
Tab
,
'TAB'
)).
dx
,
tabRight
);
await
tester
.
pumpAndSettle
();
await
tester
.
fling
(
find
.
byType
(
TabBarView
),
const
Offset
(
100.0
,
0.0
),
5000.0
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
50
));
expect
(
tester
.
getTopLeft
(
find
.
widgetWithText
(
Tab
,
'TAB'
)).
dx
,
0
);
expect
(
tester
.
getTopRight
(
find
.
widgetWithText
(
Tab
,
'TAB'
)).
dx
,
800
);
expect
(
tester
.
getTopLeft
(
find
.
widgetWithText
(
Tab
,
'TAB'
)).
dx
,
tabLeft
);
expect
(
tester
.
getTopRight
(
find
.
widgetWithText
(
Tab
,
'TAB'
)).
dx
,
tabRight
);
await
tester
.
pumpAndSettle
();
expect
(
controller
.
index
,
0
);
...
...
packages/flutter/test/rendering/mock_canvas.dart
View file @
24efb55b
...
...
@@ -270,8 +270,8 @@ abstract class PaintPattern {
/// Indicates that a line is expected next.
///
/// The next line is examined. Any arguments that are passed to this method
/// are compared to the actual [Canvas.drawLine] call's `p
aint` argument
, and
/// any mismatches result in failure.
/// are compared to the actual [Canvas.drawLine] call's `p
1`, `p2`
, and
///
`paint` arguments, and
any mismatches result in failure.
///
/// If no call to [Canvas.drawLine] was made, then this results in failure.
///
...
...
@@ -283,7 +283,7 @@ abstract class PaintPattern {
/// painting has completed, not at the time of the call. If the same [Paint]
/// object is reused multiple times, then this may not match the actual
/// arguments as they were seen by the method.
void
line
({
Color
color
,
double
strokeWidth
,
bool
hasMaskFilter
,
PaintingStyle
style
});
void
line
({
Offset
p1
,
Offset
p2
,
Color
color
,
double
strokeWidth
,
bool
hasMaskFilter
,
PaintingStyle
style
});
/// Indicates that an arc is expected next.
///
...
...
@@ -690,8 +690,8 @@ class _TestRecordingCanvasPatternMatcher extends _TestRecordingCanvasMatcher imp
}
@override
void
line
({
Color
color
,
double
strokeWidth
,
bool
hasMaskFilter
,
PaintingStyle
style
})
{
_predicates
.
add
(
new
_LinePaintPredicate
(
color:
color
,
strokeWidth:
strokeWidth
,
hasMaskFilter:
hasMaskFilter
,
style:
style
));
void
line
({
Offset
p1
,
Offset
p2
,
Color
color
,
double
strokeWidth
,
bool
hasMaskFilter
,
PaintingStyle
style
})
{
_predicates
.
add
(
new
_LinePaintPredicate
(
p1:
p1
,
p2:
p2
,
color:
color
,
strokeWidth:
strokeWidth
,
hasMaskFilter:
hasMaskFilter
,
style:
style
));
}
@override
...
...
@@ -1073,11 +1073,38 @@ class _PathPaintPredicate extends _DrawCommandPaintPredicate {
}
}
// TODO(ianh): add arguments to test the
points,
length, angle, that kind of thing
// TODO(ianh): add arguments to test the length, angle, that kind of thing
class
_LinePaintPredicate
extends
_DrawCommandPaintPredicate
{
_LinePaintPredicate
({
Color
color
,
double
strokeWidth
,
bool
hasMaskFilter
,
PaintingStyle
style
})
:
super
(
_LinePaintPredicate
({
this
.
p1
,
this
.
p2
,
Color
color
,
double
strokeWidth
,
bool
hasMaskFilter
,
PaintingStyle
style
})
:
super
(
#drawLine
,
'a line'
,
3
,
2
,
color:
color
,
strokeWidth:
strokeWidth
,
hasMaskFilter:
hasMaskFilter
,
style:
style
);
final
Offset
p1
;
final
Offset
p2
;
@override
void
verifyArguments
(
List
<
dynamic
>
arguments
)
{
super
.
verifyArguments
(
arguments
);
// Checks the 3rd argument, a Paint
if
(
arguments
.
length
!=
3
)
throw
'It called
$methodName
with
${arguments.length}
arguments; expected 3.'
;
final
Offset
p1Argument
=
arguments
[
0
];
final
Offset
p2Argument
=
arguments
[
1
];
if
(
p1
!=
null
&&
p1Argument
!=
p1
)
{
throw
'It called
$methodName
with p1 endpoint,
$p1Argument
, which was not exactly the expected endpoint (
$p1
).'
;
}
if
(
p2
!=
null
&&
p2Argument
!=
p2
)
{
throw
'It called
$methodName
with p2 endpoint,
$p2Argument
, which was not exactly the expected endpoint (
$p2
).'
;
}
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
if
(
p1
!=
null
)
description
.
add
(
'end point p1:
$p1
'
);
if
(
p2
!=
null
)
description
.
add
(
'end point p2:
$p2
'
);
}
}
class
_ArcPaintPredicate
extends
_DrawCommandPaintPredicate
{
...
...
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