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
b23aed7a
Commit
b23aed7a
authored
Jan 09, 2017
by
Hans Muller
Committed by
GitHub
Jan 09, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
New Tabs API (#7387)
parent
e82b18d4
Changes
14
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
1361 additions
and
1407 deletions
+1361
-1407
main.dart
dev/benchmarks/complex_layout/lib/main.dart
+4
-4
material_arc.dart
dev/manual_tests/material_arc.dart
+16
-16
colors_demo.dart
examples/flutter_gallery/lib/demo/colors_demo.dart
+9
-19
page_selector_demo.dart
examples/flutter_gallery/lib/demo/page_selector_demo.dart
+68
-63
scrollable_tabs_demo.dart
examples/flutter_gallery/lib/demo/scrollable_tabs_demo.dart
+82
-76
tabs_demo.dart
examples/flutter_gallery/lib/demo/tabs_demo.dart
+11
-20
tabs_fab_demo.dart
examples/flutter_gallery/lib/demo/tabs_fab_demo.dart
+50
-40
demo.dart
examples/flutter_gallery/lib/gallery/demo.dart
+16
-22
stock_home.dart
examples/stocks/lib/stock_home.dart
+8
-8
material.dart
packages/flutter/lib/material.dart
+1
-0
constants.dart
packages/flutter/lib/src/material/constants.dart
+3
-0
tab_controller.dart
packages/flutter/lib/src/material/tab_controller.dart
+202
-0
tabs.dart
packages/flutter/lib/src/material/tabs.dart
+612
-1057
tabs_test.dart
packages/flutter/test/material/tabs_test.dart
+279
-82
No files found.
dev/benchmarks/complex_layout/lib/main.dart
View file @
b23aed7a
...
...
@@ -470,12 +470,12 @@ class ItemGalleryBox extends StatelessWidget {
return
new
SizedBox
(
height:
200.0
,
child:
new
TabBarSelection
<
String
>
(
values:
tabNames
,
child:
new
DefaultTabController
(
length:
tabNames
.
length
,
child:
new
Column
(
children:
<
Widget
>[
new
Expanded
(
child:
new
TabBarView
<
String
>
(
child:
new
TabBarView
(
children:
tabNames
.
map
((
String
tabName
)
{
return
new
Container
(
key:
new
Key
(
'Tab
$index
-
$tabName
'
),
...
...
@@ -521,7 +521,7 @@ class ItemGalleryBox extends StatelessWidget {
)
),
new
Container
(
child:
new
TabPageSelector
<
String
>
()
child:
new
TabPageSelector
()
)
]
)
...
...
dev/manual_tests/material_arc.dart
View file @
b23aed7a
...
...
@@ -412,8 +412,6 @@ class AnimationDemo extends StatefulWidget {
}
class
_AnimationDemoState
extends
State
<
AnimationDemo
>
with
TickerProviderStateMixin
{
static
final
GlobalKey
<
TabBarSelectionState
<
_ArcDemo
>>
_tabsKey
=
new
GlobalKey
<
TabBarSelectionState
<
_ArcDemo
>>();
List
<
_ArcDemo
>
_allDemos
;
@override
...
...
@@ -435,8 +433,7 @@ class _AnimationDemoState extends State<AnimationDemo> with TickerProviderStateM
];
}
Future
<
Null
>
_play
()
async
{
_ArcDemo
demo
=
_tabsKey
.
currentState
.
value
;
Future
<
Null
>
_play
(
_ArcDemo
demo
)
async
{
await
demo
.
controller
.
forward
();
if
(
demo
.
key
.
currentState
!=
null
&&
demo
.
key
.
currentState
.
mounted
)
demo
.
controller
.
reverse
();
...
...
@@ -444,23 +441,26 @@ class _AnimationDemoState extends State<AnimationDemo> with TickerProviderStateM
@override
Widget
build
(
BuildContext
context
)
{
return
new
TabBarSelection
<
_ArcDemo
>(
key:
_tabsKey
,
values:
_allDemos
,
return
new
DefaultTabController
(
length:
_allDemos
.
length
,
child:
new
Scaffold
(
appBar:
new
AppBar
(
title:
new
Text
(
'Animation'
),
bottom:
new
TabBar
<
_ArcDemo
>(
labels:
new
Map
<
_ArcDemo
,
TabLabel
>.
fromIterable
(
_allDemos
,
value:
(
_ArcDemo
demo
)
{
return
new
TabLabel
(
text:
demo
.
title
);
})
)
bottom:
new
TabBar
(
tabs:
_allDemos
.
map
((
_ArcDemo
demo
)
=>
new
Tab
(
text:
demo
.
title
)).
toList
(),
),
),
floatingActionButton:
new
FloatingActionButton
(
onPressed:
_play
,
child:
new
Icon
(
Icons
.
refresh
)
floatingActionButton:
new
Builder
(
builder:
(
BuildContext
context
)
{
return
new
FloatingActionButton
(
child:
new
Icon
(
Icons
.
refresh
),
onPressed:
()
{
_play
(
_allDemos
[
DefaultTabController
.
of
(
context
).
index
]);
},
);
},
),
body:
new
TabBarView
<
_ArcDemo
>
(
body:
new
TabBarView
(
children:
_allDemos
.
map
((
_ArcDemo
demo
)
=>
demo
.
builder
(
demo
)).
toList
()
)
)
...
...
examples/flutter_gallery/lib/demo/colors_demo.dart
View file @
b23aed7a
...
...
@@ -107,38 +107,28 @@ class ColorSwatchTabView extends StatelessWidget {
}
}
class
ColorsDemo
extends
StatefulWidget
{
ColorsDemo
({
Key
key
})
:
super
(
key:
key
);
class
ColorsDemo
extends
StatelessWidget
{
static
const
String
routeName
=
'/colors'
;
@override
_ColorsDemoState
createState
()
=>
new
_ColorsDemoState
();
}
class
_ColorsDemoState
extends
State
<
ColorsDemo
>
{
@override
Widget
build
(
BuildContext
context
)
{
return
new
TabBarSelection
<
ColorSwatch
>
(
values:
colorSwatches
,
return
new
DefaultTabController
(
length:
colorSwatches
.
length
,
child:
new
Scaffold
(
appBar:
new
AppBar
(
elevation:
0
,
title:
new
Text
(
'Colors'
),
bottom:
new
TabBar
<
ColorSwatch
>
(
bottom:
new
TabBar
(
isScrollable:
true
,
labels:
new
Map
<
ColorSwatch
,
TabLabel
>.
fromIterable
(
colorSwatches
,
value:
(
ColorSwatch
swatch
)
{
return
new
TabLabel
(
text:
swatch
.
name
);
})
tabs:
colorSwatches
.
map
((
ColorSwatch
swatch
)
=>
new
Tab
(
text:
swatch
.
name
)).
toList
(),
)
),
body:
new
TabBarView
<
ColorSwatch
>
(
body:
new
TabBarView
(
children:
colorSwatches
.
map
((
ColorSwatch
swatch
)
{
return
new
ColorSwatchTabView
(
swatch:
swatch
);
})
.
toList
()
)
)
}).
toList
(),
),
),
);
}
}
examples/flutter_gallery/lib/demo/page_selector_demo.dart
View file @
b23aed7a
...
...
@@ -4,78 +4,83 @@
import
'package:flutter/material.dart'
;
class
PageSelectorDemo
extends
StatelessWidget
{
class
_PageSelector
extends
StatelessWidget
{
_PageSelector
({
this
.
icons
});
static
const
String
routeName
=
'/page-selector'
;
final
List
<
IconData
>
icons
;
void
_handleArrowButtonPress
(
BuildContext
context
,
int
delta
)
{
final
TabBarSelectionState
<
IconData
>
selection
=
TabBarSelection
.
of
/*<IconData>*/
(
context
);
if
(!
selection
.
value
IsChanging
)
selection
.
value
=
selection
.
values
[(
selection
.
index
+
delta
).
clamp
(
0
,
selection
.
values
.
length
-
1
)]
;
TabController
controller
=
DefaultTabController
.
of
(
context
);
if
(!
controller
.
index
IsChanging
)
controller
.
animateTo
(
controller
.
index
+
delta
)
;
}
@override
Widget
build
(
BuildContext
notUsed
)
{
// Can't find the TabBarSelection from this context.
final
List
<
IconData
>
icons
=
<
IconData
>[
Icons
.
event
,
Icons
.
home
,
Icons
.
android
,
Icons
.
alarm
,
Icons
.
face
,
Icons
.
language
,
];
Widget
build
(
BuildContext
context
)
{
final
TabController
controller
=
DefaultTabController
.
of
(
context
);
final
Color
color
=
Theme
.
of
(
context
).
accentColor
;
return
new
Column
(
children:
<
Widget
>[
new
Container
(
margin:
const
EdgeInsets
.
only
(
top:
16.0
),
child:
new
Row
(
children:
<
Widget
>[
new
IconButton
(
icon:
new
Icon
(
Icons
.
chevron_left
),
color:
color
,
onPressed:
()
{
_handleArrowButtonPress
(
context
,
-
1
);
},
tooltip:
'Page back'
),
new
TabPageSelector
(
controller:
controller
),
new
IconButton
(
icon:
new
Icon
(
Icons
.
chevron_right
),
color:
color
,
onPressed:
()
{
_handleArrowButtonPress
(
context
,
1
);
},
tooltip:
'Page forward'
)
],
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
)
),
new
Expanded
(
child:
new
TabBarView
(
children:
icons
.
map
((
IconData
icon
)
{
return
new
Container
(
key:
new
ObjectKey
(
icon
),
padding:
const
EdgeInsets
.
all
(
12.0
),
child:
new
Card
(
child:
new
Center
(
child:
new
Icon
(
icon
,
size:
128.0
,
color:
color
)
),
),
);
}).
toList
()
),
),
],
);
}
}
class
PageSelectorDemo
extends
StatelessWidget
{
static
const
String
routeName
=
'/page-selector'
;
static
final
List
<
IconData
>
icons
=
<
IconData
>[
Icons
.
event
,
Icons
.
home
,
Icons
.
android
,
Icons
.
alarm
,
Icons
.
face
,
Icons
.
language
,
];
@override
Widget
build
(
BuildContext
context
)
{
return
new
Scaffold
(
appBar:
new
AppBar
(
title:
new
Text
(
'Page selector'
)),
body:
new
TabBarSelection
<
IconData
>(
values:
icons
,
child:
new
Builder
(
builder:
(
BuildContext
context
)
{
final
Color
color
=
Theme
.
of
(
context
).
accentColor
;
return
new
Column
(
children:
<
Widget
>[
new
Container
(
margin:
const
EdgeInsets
.
only
(
top:
16.0
),
child:
new
Row
(
children:
<
Widget
>[
new
IconButton
(
icon:
new
Icon
(
Icons
.
chevron_left
),
color:
color
,
onPressed:
()
{
_handleArrowButtonPress
(
context
,
-
1
);
},
tooltip:
'Page back'
),
new
TabPageSelector
<
IconData
>(),
new
IconButton
(
icon:
new
Icon
(
Icons
.
chevron_right
),
color:
color
,
onPressed:
()
{
_handleArrowButtonPress
(
context
,
1
);
},
tooltip:
'Page forward'
)
],
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
)
),
new
Expanded
(
child:
new
TabBarView
<
IconData
>(
children:
icons
.
map
((
IconData
icon
)
{
return
new
Container
(
key:
new
ObjectKey
(
icon
),
padding:
const
EdgeInsets
.
all
(
12.0
),
child:
new
Card
(
child:
new
Center
(
child:
new
Icon
(
icon
,
size:
128.0
,
color:
color
)
)
)
);
})
.
toList
()
)
)
]
);
}
)
)
body:
new
DefaultTabController
(
length:
icons
.
length
,
child:
new
_PageSelector
(
icons:
icons
),
),
);
}
}
examples/flutter_gallery/lib/demo/scrollable_tabs_demo.dart
View file @
b23aed7a
...
...
@@ -10,6 +10,21 @@ enum TabsDemoStyle {
textOnly
}
class
_Page
{
_Page
({
this
.
icon
,
this
.
text
});
final
IconData
icon
;
final
String
text
;
}
final
List
<
_Page
>
_allPages
=
<
_Page
>[
new
_Page
(
icon:
Icons
.
event
,
text:
'EVENT'
),
new
_Page
(
icon:
Icons
.
home
,
text:
'HOME'
),
new
_Page
(
icon:
Icons
.
android
,
text:
'ANDROID'
),
new
_Page
(
icon:
Icons
.
alarm
,
text:
'ALARM'
),
new
_Page
(
icon:
Icons
.
face
,
text:
'FACE'
),
new
_Page
(
icon:
Icons
.
language
,
text:
'LANGAUGE'
),
];
class
ScrollableTabsDemo
extends
StatefulWidget
{
static
const
String
routeName
=
'/scrollable-tabs'
;
...
...
@@ -17,26 +32,21 @@ class ScrollableTabsDemo extends StatefulWidget {
ScrollableTabsDemoState
createState
()
=>
new
ScrollableTabsDemoState
();
}
class
ScrollableTabsDemoState
extends
State
<
ScrollableTabsDemo
>
{
final
List
<
IconData
>
icons
=
<
IconData
>[
Icons
.
event
,
Icons
.
home
,
Icons
.
android
,
Icons
.
alarm
,
Icons
.
face
,
Icons
.
language
,
];
class
ScrollableTabsDemoState
extends
State
<
ScrollableTabsDemo
>
with
SingleTickerProviderStateMixin
{
TabController
_controller
;
TabsDemoStyle
_demoStyle
=
TabsDemoStyle
.
iconsAndText
;
final
Map
<
IconData
,
String
>
labels
=
<
IconData
,
String
>{
Icons
.
event
:
'EVENT'
,
Icons
.
home
:
'HOME'
,
Icons
.
android
:
'ANDROID'
,
Icons
.
alarm
:
'ALARM'
,
Icons
.
face
:
'FACE'
,
Icons
.
language
:
'LANGUAGE'
,
};
@override
void
initState
()
{
super
.
initState
();
_controller
=
new
TabController
(
vsync:
this
,
length:
_allPages
.
length
);
}
TabsDemoStyle
_demoStyle
=
TabsDemoStyle
.
iconsAndText
;
@override
void
dispose
()
{
_controller
.
dispose
();
super
.
dispose
();
}
void
changeDemoStyle
(
TabsDemoStyle
style
)
{
setState
(()
{
...
...
@@ -47,65 +57,61 @@ class ScrollableTabsDemoState extends State<ScrollableTabsDemo> {
@override
Widget
build
(
BuildContext
context
)
{
final
Color
iconColor
=
Theme
.
of
(
context
).
accentColor
;
return
new
TabBarSelection
<
IconData
>(
values:
icons
,
child:
new
Scaffold
(
appBar:
new
AppBar
(
title:
new
Text
(
'Scrollable tabs'
),
actions:
<
Widget
>[
new
PopupMenuButton
<
TabsDemoStyle
>(
onSelected:
changeDemoStyle
,
itemBuilder:
(
BuildContext
context
)
=>
<
PopupMenuItem
<
TabsDemoStyle
>>[
new
PopupMenuItem
<
TabsDemoStyle
>(
value:
TabsDemoStyle
.
iconsAndText
,
child:
new
Text
(
'Icons and text'
)
),
new
PopupMenuItem
<
TabsDemoStyle
>(
value:
TabsDemoStyle
.
iconsOnly
,
child:
new
Text
(
'Icons only'
)
),
new
PopupMenuItem
<
TabsDemoStyle
>(
value:
TabsDemoStyle
.
textOnly
,
child:
new
Text
(
'Text only'
)
),
]
)
],
bottom:
new
TabBar
<
IconData
>(
isScrollable:
true
,
labels:
new
Map
<
IconData
,
TabLabel
>.
fromIterable
(
icons
,
value:
(
IconData
icon
)
{
switch
(
_demoStyle
)
{
case
TabsDemoStyle
.
iconsAndText
:
return
new
TabLabel
(
text:
labels
[
icon
],
icon:
new
Icon
(
icon
));
case
TabsDemoStyle
.
iconsOnly
:
return
new
TabLabel
(
icon:
new
Icon
(
icon
));
case
TabsDemoStyle
.
textOnly
:
return
new
TabLabel
(
text:
labels
[
icon
]);
}
}
)
)
return
new
Scaffold
(
appBar:
new
AppBar
(
title:
new
Text
(
'Scrollable tabs'
),
actions:
<
Widget
>[
new
PopupMenuButton
<
TabsDemoStyle
>(
onSelected:
changeDemoStyle
,
itemBuilder:
(
BuildContext
context
)
=>
<
PopupMenuItem
<
TabsDemoStyle
>>[
new
PopupMenuItem
<
TabsDemoStyle
>(
value:
TabsDemoStyle
.
iconsAndText
,
child:
new
Text
(
'Icons and text'
)
),
new
PopupMenuItem
<
TabsDemoStyle
>(
value:
TabsDemoStyle
.
iconsOnly
,
child:
new
Text
(
'Icons only'
)
),
new
PopupMenuItem
<
TabsDemoStyle
>(
value:
TabsDemoStyle
.
textOnly
,
child:
new
Text
(
'Text only'
)
),
],
),
],
bottom:
new
TabBar
(
controller:
_controller
,
isScrollable:
true
,
tabs:
_allPages
.
map
((
_Page
page
)
{
switch
(
_demoStyle
)
{
case
TabsDemoStyle
.
iconsAndText
:
return
new
Tab
(
text:
page
.
text
,
icon:
new
Icon
(
page
.
icon
));
case
TabsDemoStyle
.
iconsOnly
:
return
new
Tab
(
icon:
new
Icon
(
page
.
icon
));
case
TabsDemoStyle
.
textOnly
:
return
new
Tab
(
text:
page
.
text
);
}
}).
toList
(),
),
body:
new
TabBarView
<
IconData
>(
children:
icons
.
map
((
IconData
icon
)
{
return
new
Container
(
key:
new
ObjectKey
(
icon
),
padding:
const
EdgeInsets
.
all
(
12.0
),
child:
new
Card
(
child:
new
Center
(
child:
new
Icon
(
icon
,
color:
iconColor
,
size:
128.0
)
)
)
);
}).
toList
()
)
)
),
body:
new
TabBarView
(
controller:
_controller
,
children:
_allPages
.
map
((
_Page
page
)
{
return
new
Container
(
key:
new
ObjectKey
(
page
.
icon
),
padding:
const
EdgeInsets
.
all
(
12.0
),
child:
new
Card
(
child:
new
Center
(
child:
new
Icon
(
page
.
icon
,
color:
iconColor
,
size:
128.0
,
),
),
),
);
}).
toList
()
),
);
}
}
examples/flutter_gallery/lib/demo/tabs_demo.dart
View file @
b23aed7a
...
...
@@ -111,30 +111,21 @@ class _CardDataItem extends StatelessWidget {
}
}
class
TabsDemo
extends
StatefulWidget
{
TabsDemo
({
Key
key
})
:
super
(
key:
key
);
class
TabsDemo
extends
StatelessWidget
{
static
const
String
routeName
=
'/tabs'
;
@override
_TabsDemoState
createState
()
=>
new
_TabsDemoState
();
}
class
_TabsDemoState
extends
State
<
TabsDemo
>
{
@override
Widget
build
(
BuildContext
context
)
{
return
new
TabBarSelection
<
_Page
>
(
values:
_allPages
.
keys
.
toList
()
,
return
new
DefaultTabController
(
length:
_allPages
.
length
,
child:
new
Scaffold
(
appBar:
new
AppBar
(
title:
new
Text
(
'Tabs and scrolling'
),
bottom:
new
TabBar
<
_Page
>(
labels:
new
Map
<
_Page
,
TabLabel
>.
fromIterable
(
_allPages
.
keys
,
value:
(
_Page
page
)
{
return
new
TabLabel
(
text:
page
.
label
);
})
)
bottom:
new
TabBar
(
tabs:
_allPages
.
keys
.
map
((
_Page
page
)
=>
new
Tab
(
text:
page
.
label
)).
toList
(),
),
),
body:
new
TabBarView
<
_Page
>
(
body:
new
TabBarView
(
children:
_allPages
.
keys
.
map
((
_Page
page
)
{
return
new
ScrollableList
(
padding:
const
EdgeInsets
.
symmetric
(
vertical:
8.0
,
horizontal:
16.0
),
...
...
@@ -144,11 +135,11 @@ class _TabsDemoState extends State<TabsDemo> {
padding:
const
EdgeInsets
.
symmetric
(
vertical:
8.0
),
child:
new
_CardDataItem
(
page:
page
,
data:
data
)
);
}).
toList
()
}).
toList
()
,
);
}).
toList
()
)
)
}).
toList
()
,
)
,
)
,
);
}
}
examples/flutter_gallery/lib/demo/tabs_fab_demo.dart
View file @
b23aed7a
...
...
@@ -4,6 +4,12 @@
import
'package:flutter/material.dart'
;
const
String
_explanatoryText
=
"When the Scaffold's floating action button changes, the new button fades and "
"turns into view. In this demo, changing tabs can cause the app to be rebuilt "
"with a FloatingActionButton that the Scaffold distinguishes from the others "
"by its key."
;
class
_Page
{
_Page
({
this
.
label
,
this
.
colors
,
this
.
icon
});
...
...
@@ -11,7 +17,6 @@ class _Page {
final
Map
<
int
,
Color
>
colors
;
final
IconData
icon
;
TabLabel
get
tabLabel
=>
new
TabLabel
(
text:
label
.
toUpperCase
());
Color
get
labelColor
=>
colors
!=
null
?
colors
[
300
]
:
Colors
.
grey
[
300
];
bool
get
fabDefined
=>
colors
!=
null
&&
icon
!=
null
;
Color
get
fabColor
=>
colors
[
400
];
...
...
@@ -19,11 +24,13 @@ class _Page {
Key
get
fabKey
=>
new
ValueKey
<
Color
>(
fabColor
);
}
const
String
_explanatoryText
=
"When the Scaffold's floating action button changes, the new button fades and "
"turns into view. In this demo, changing tabs can cause the app to be rebuilt "
"with a FloatingActionButton that the Scaffold distinguishes from the others "
"by its key."
;
final
List
<
_Page
>
_allPages
=
<
_Page
>[
new
_Page
(
label:
'Blue'
,
colors:
Colors
.
indigo
,
icon:
Icons
.
add
),
new
_Page
(
label:
'Eco'
,
colors:
Colors
.
green
,
icon:
Icons
.
create
),
new
_Page
(
label:
'No'
),
new
_Page
(
label:
'Teal'
,
colors:
Colors
.
teal
,
icon:
Icons
.
add
),
new
_Page
(
label:
'Red'
,
colors:
Colors
.
red
,
icon:
Icons
.
create
),
];
class
TabsFabDemo
extends
StatefulWidget
{
static
const
String
routeName
=
'/tabs-fab'
;
...
...
@@ -32,31 +39,34 @@ class TabsFabDemo extends StatefulWidget {
_TabsFabDemoState
createState
()
=>
new
_TabsFabDemoState
();
}
class
_TabsFabDemoState
extends
State
<
TabsFabDemo
>
{
final
GlobalKey
<
ScaffoldState
>
scaffoldKey
=
new
GlobalKey
<
ScaffoldState
>();
final
List
<
_Page
>
pages
=
<
_Page
>[
new
_Page
(
label:
'Blue'
,
colors:
Colors
.
indigo
,
icon:
Icons
.
add
),
new
_Page
(
label:
'Eco'
,
colors:
Colors
.
green
,
icon:
Icons
.
create
),
new
_Page
(
label:
'No'
),
new
_Page
(
label:
'Teal'
,
colors:
Colors
.
teal
,
icon:
Icons
.
add
),
new
_Page
(
label:
'Red'
,
colors:
Colors
.
red
,
icon:
Icons
.
create
),
];
_Page
selectedPage
;
class
_TabsFabDemoState
extends
State
<
TabsFabDemo
>
with
SingleTickerProviderStateMixin
{
final
GlobalKey
<
ScaffoldState
>
_scaffoldKey
=
new
GlobalKey
<
ScaffoldState
>();
TabController
_controller
;
_Page
_selectedPage
;
@override
void
initState
()
{
super
.
initState
();
selectedPage
=
pages
[
0
];
_controller
=
new
TabController
(
vsync:
this
,
length:
_allPages
.
length
);
_controller
.
addListener
(
_handleTabSelection
);
_selectedPage
=
_allPages
[
0
];
}
@override
void
dispose
()
{
_controller
.
dispose
();
super
.
dispose
();
}
void
_handleTabSelection
(
_Page
page
)
{
void
_handleTabSelection
()
{
setState
(()
{
selectedPage
=
page
;
_selectedPage
=
_allPages
[
_controller
.
index
]
;
});
}
void
_showExplanatoryText
()
{
scaffoldKey
.
currentState
.
showBottomSheet
((
BuildContext
context
)
{
_
scaffoldKey
.
currentState
.
showBottomSheet
((
BuildContext
context
)
{
return
new
Container
(
decoration:
new
BoxDecoration
(
border:
new
Border
(
top:
new
BorderSide
(
color:
Theme
.
of
(
context
).
dividerColor
))
...
...
@@ -93,26 +103,26 @@ class _TabsFabDemoState extends State<TabsFabDemo> {
@override
Widget
build
(
BuildContext
context
)
{
return
new
TabBarSelection
<
_Page
>
(
values:
pages
,
onChanged:
_handleTabSelection
,
child:
new
Scaffold
(
key:
scaffoldKey
,
appBar:
new
AppBar
(
t
itle:
new
Text
(
'FAB per tab'
),
bottom:
new
TabBar
<
_Page
>(
labels:
new
Map
<
_Page
,
TabLabel
>.
fromIterable
(
pages
,
value:
(
_Page
page
)
=>
page
.
tabLabel
)
)
)
,
floatingActionButton:
!
selectedPage
.
fabDefined
?
null
:
new
FloatingActionButton
(
key:
selectedPage
.
fabKey
,
tooltip:
'Show explanation'
,
backgroundColor:
selectedPage
.
fabColor
,
child:
selectedPage
.
fabIcon
,
onPressed:
_showExplanatoryText
)
,
body:
new
TabBarView
<
_Page
>(
children:
pages
.
map
(
buildTabView
).
toList
()
)
)
return
new
Scaffold
(
key:
_scaffoldKey
,
appBar:
new
AppBar
(
title:
new
Text
(
'FAB per tab'
),
bottom:
new
TabBar
(
controller:
_controller
,
t
abs:
_allPages
.
map
((
_Page
page
)
=>
new
Tab
(
text:
page
.
label
.
toUpperCase
())).
toList
(
),
)
),
floatingActionButton:
!
_selectedPage
.
fabDefined
?
null
:
new
FloatingActionButton
(
key:
_selectedPage
.
fabKey
,
tooltip:
'Show explanation'
,
backgroundColor:
_selectedPage
.
fabColor
,
child:
_selectedPage
.
fabIcon
,
onPressed:
_showExplanatoryText
)
,
body:
new
TabBarView
(
controller:
_controller
,
children:
_allPages
.
map
(
buildTabView
).
toList
(
)
)
,
);
}
}
examples/flutter_gallery/lib/gallery/demo.dart
View file @
b23aed7a
...
...
@@ -20,13 +20,6 @@ class ComponentDemoTabData {
final
String
description
;
final
String
tabName
;
static
Map
<
ComponentDemoTabData
,
TabLabel
>
buildTabLabels
(
List
<
ComponentDemoTabData
>
demos
)
{
return
new
Map
<
ComponentDemoTabData
,
TabLabel
>.
fromIterable
(
demos
,
value:
(
ComponentDemoTabData
demo
)
=>
new
TabLabel
(
text:
demo
.
tabName
)
);
}
@override
bool
operator
==(
Object
other
)
{
if
(
other
.
runtimeType
!=
runtimeType
)
...
...
@@ -49,8 +42,7 @@ class TabbedComponentDemoScaffold extends StatelessWidget {
final
String
title
;
void
_showExampleCode
(
BuildContext
context
)
{
TabBarSelectionState
<
ComponentDemoTabData
>
selection
=
TabBarSelection
.
of
(
context
);
String
tag
=
selection
.
value
?.
exampleCodeTag
;
String
tag
=
demos
[
DefaultTabController
.
of
(
context
).
index
].
exampleCodeTag
;
if
(
tag
!=
null
)
{
Navigator
.
push
(
context
,
new
MaterialPageRoute
<
FullScreenCodeDialog
>(
builder:
(
BuildContext
context
)
=>
new
FullScreenCodeDialog
(
exampleCodeTag:
tag
)
...
...
@@ -60,8 +52,8 @@ class TabbedComponentDemoScaffold extends StatelessWidget {
@override
Widget
build
(
BuildContext
context
)
{
return
new
TabBarSelection
<
ComponentDemoTabData
>
(
values:
demos
,
return
new
DefaultTabController
(
length:
demos
.
length
,
child:
new
Scaffold
(
appBar:
new
AppBar
(
title:
new
Text
(
title
),
...
...
@@ -71,17 +63,19 @@ class TabbedComponentDemoScaffold extends StatelessWidget {
return
new
IconButton
(
icon:
new
Icon
(
Icons
.
description
),
tooltip:
'Show example code'
,
onPressed:
()
{
_showExampleCode
(
context
);
}
onPressed:
()
{
_showExampleCode
(
context
);
},
);
}
)
}
,
)
,
],
bottom:
new
TabBar
<
ComponentDemoTabData
>
(
bottom:
new
TabBar
(
isScrollable:
true
,
labels:
ComponentDemoTabData
.
buildTabLabels
(
demos
)
)
tabs:
demos
.
map
((
ComponentDemoTabData
data
)
=>
new
Tab
(
text:
data
.
tabName
)).
toList
(),
)
,
),
body:
new
TabBarView
<
ComponentDemoTabData
>
(
body:
new
TabBarView
(
children:
demos
.
map
((
ComponentDemoTabData
demo
)
{
return
new
Column
(
children:
<
Widget
>[
...
...
@@ -92,11 +86,11 @@ class TabbedComponentDemoScaffold extends StatelessWidget {
)
),
new
Expanded
(
child:
demo
.
widget
)
]
]
,
);
}).
toList
()
)
)
}).
toList
()
,
)
,
)
,
);
}
}
...
...
examples/stocks/lib/stock_home.dart
View file @
b23aed7a
...
...
@@ -222,11 +222,11 @@ class StockHomeState extends State<StockHome> {
]
)
],
bottom:
new
TabBar
<
StockHomeTab
>
(
labels:
<
StockHomeTab
,
TabLabel
>{
StockHomeTab
.
market
:
new
TabLabel
(
text:
StockStrings
.
of
(
context
).
market
()),
StockHomeTab
.
portfolio
:
new
TabLabel
(
text:
StockStrings
.
of
(
context
).
portfolio
())
}
bottom:
new
TabBar
(
tabs:
<
Widget
>[
new
Tab
(
text:
StockStrings
.
of
(
context
).
market
()),
new
Tab
(
text:
StockStrings
.
of
(
context
).
portfolio
()),
]
)
);
}
...
...
@@ -318,14 +318,14 @@ class StockHomeState extends State<StockHome> {
@override
Widget
build
(
BuildContext
context
)
{
return
new
TabBarSelection
<
StockHomeTab
>
(
values:
<
StockHomeTab
>[
StockHomeTab
.
market
,
StockHomeTab
.
portfolio
]
,
return
new
DefaultTabController
(
length:
2
,
child:
new
Scaffold
(
key:
_scaffoldKey
,
appBar:
_isSearching
?
buildSearchBar
()
:
buildAppBar
(),
floatingActionButton:
buildFloatingActionButton
(),
drawer:
_buildDrawer
(
context
),
body:
new
TabBarView
<
StockHomeTab
>
(
body:
new
TabBarView
(
children:
<
Widget
>[
_buildStockTab
(
context
,
StockHomeTab
.
market
,
config
.
symbols
),
_buildStockTab
(
context
,
StockHomeTab
.
portfolio
,
portfolioSymbols
),
...
...
packages/flutter/lib/material.dart
View file @
b23aed7a
...
...
@@ -71,6 +71,7 @@ export 'src/material/snack_bar.dart';
export
'src/material/stepper.dart'
;
export
'src/material/switch.dart'
;
export
'src/material/tabs.dart'
;
export
'src/material/tab_controller.dart'
;
export
'src/material/theme.dart'
;
export
'src/material/theme_data.dart'
;
export
'src/material/time_picker.dart'
;
...
...
packages/flutter/lib/src/material/constants.dart
View file @
b23aed7a
...
...
@@ -22,3 +22,6 @@ const Duration kRadialReactionDuration = const Duration(milliseconds: 200);
/// The value of the alpha channel to use when drawing a circular material ink response.
const
int
kRadialReactionAlpha
=
0x33
;
/// The duration
const
Duration
kTabScrollDuration
=
const
Duration
(
milliseconds:
200
);
packages/flutter/lib/src/material/tab_controller.dart
0 → 100644
View file @
b23aed7a
// 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
'package:flutter/foundation.dart'
;
import
'package:flutter/widgets.dart'
;
import
'constants.dart'
;
/// Coordinates tab selection between a [TabBar] and a [TabBarView].
///
/// The [index] property is the index of the selected tab and the [animation]
/// represents the current scroll positions of the tab bar and the tar bar view.
/// The selected tab's index can be changed with [animateTo].
///
/// See also:
///
/// * [DefaultTabController], which simplifies sharing a TabController with
/// its [TabBar] and a [TabBarView] descendants.
class
TabController
extends
ChangeNotifier
{
/// Creates an object that manages the state required by [TabBar] and a [TabBarView].
TabController
({
int
initialIndex:
0
,
@required
this
.
length
,
@required
TickerProvider
vsync
})
:
_index
=
initialIndex
,
_previousIndex
=
initialIndex
,
_animationController
=
new
AnimationController
(
value:
initialIndex
.
toDouble
(),
upperBound:
(
length
-
1
).
toDouble
(),
vsync:
vsync
)
{
assert
(
length
!=
null
&&
length
>
1
);
assert
(
initialIndex
!=
null
&&
initialIndex
>=
0
&&
initialIndex
<
length
);
}
/// An animation whose value represents the current position of the [TabBar]'s
/// selected tab indicator as well as the scrollOffsets of the [TabBar]
/// and [TabBarView].
///
/// The animation's value ranges from 0.0 to [length] - 1.0. After the
/// selected tab is changed, the animation's value equals [index]. The
/// animation's value can be [offset] by +/- 1.0 to reflect [TabBarView]
/// drag scrolling.
final
AnimationController
_animationController
;
Animation
<
double
>
get
animation
=>
_animationController
.
view
;
/// The total number of tabs. Must be greater than one.
final
int
length
;
void
_changeIndex
(
int
value
,
{
Duration
duration
,
Curve
curve
})
{
assert
(
value
!=
null
);
assert
(
value
>=
0
&&
value
<
length
);
assert
(
duration
==
null
?
curve
==
null
:
true
);
assert
(
_indexIsChangingCount
>=
0
);
if
(
value
==
_index
)
return
;
_previousIndex
=
index
;
_index
=
value
;
if
(
duration
!=
null
)
{
_indexIsChangingCount
+=
1
;
_animationController
..
animateTo
(
_index
.
toDouble
(),
duration:
duration
,
curve:
curve
).
then
((
_
)
{
_indexIsChangingCount
-=
1
;
notifyListeners
();
});
}
else
{
_indexIsChangingCount
+=
1
;
_animationController
.
value
=
_index
.
toDouble
();
_indexIsChangingCount
-=
1
;
notifyListeners
();
}
}
/// The index of the currently selected tab. Changing the index also updates
/// [previousIndex], sets the [animation]'s value to index, resets
/// [indexIsChanging] to false, and notifies listeners.
///
/// To change the currently selected tab and play the [animation] use [animateTo].
int
get
index
=>
_index
;
int
_index
;
set
index
(
int
value
)
{
_changeIndex
(
value
);
}
/// The index of the previously selected tab. Initially the same as [index].
int
get
previousIndex
=>
_previousIndex
;
int
_previousIndex
;
/// True while we're animating from [previousIndex] to [index].
bool
get
indexIsChanging
=>
_indexIsChangingCount
!=
0
;
int
_indexIsChangingCount
=
0
;
/// Immediately sets [index] and [previousIndex] and then plays the
/// [animation] from its current value to [index].
///
/// While the animation is running [indexIsChanging] is true. When the
/// animation completes [offset] will be 0.0.
void
animateTo
(
int
value
,
{
Duration
duration:
kTabScrollDuration
,
Curve
curve:
Curves
.
ease
})
{
_changeIndex
(
value
,
duration:
duration
,
curve:
curve
);
}
/// The difference between the [animation]'s value and [index]. The offset
/// value must be between -1.0 and 1.0.
///
/// This property is typically set by the [TabBarView] when the user
/// drags left or right. A value between -1.0 and 0.0 implies that the
/// TabBarView has been dragged to the left. Similarly a value between
/// 0.0 and 1.0 implies that the TabBarView has been dragged to the right.
double
get
offset
=>
_animationController
.
value
-
_index
.
toDouble
();
set
offset
(
double
newOffset
)
{
assert
(
newOffset
!=
null
);
assert
(
newOffset
>=
-
1.0
&&
newOffset
<=
1.0
);
assert
(!
indexIsChanging
);
if
(
newOffset
==
offset
)
return
;
_animationController
.
value
=
newOffset
+
_index
.
toDouble
();
}
@override
void
dispose
()
{
_animationController
.
dispose
();
super
.
dispose
();
}
}
class
_TabControllerScope
extends
InheritedWidget
{
_TabControllerScope
({
Key
key
,
this
.
controller
,
this
.
enabled
,
Widget
child
})
:
super
(
key:
key
,
child:
child
);
final
TabController
controller
;
final
bool
enabled
;
@override
bool
updateShouldNotify
(
_TabControllerScope
old
)
{
return
enabled
!=
old
.
enabled
||
controller
!=
old
.
controller
;
}
}
/// The [TabController] for descendant widgets that don't specify one explicitly.
class
DefaultTabController
extends
StatefulWidget
{
DefaultTabController
({
Key
key
,
@required
this
.
length
,
this
.
initialIndex
:
0
,
this
.
child
})
:
super
(
key:
key
);
/// The total number of tabs. Must be greater than one.
final
int
length
;
/// The initial index of the selected tab.
final
int
initialIndex
;
/// This widget's child. Often a [Scaffold] whose [AppBar] includes a [TabBar].
final
Widget
child
;
/// The closest instance of this class that encloses the given context.
///
/// Typical usage:
///
/// ```dart
/// TabController controller = DefaultTabBarController.of(context);
/// ```
static
TabController
of
(
BuildContext
context
)
{
_TabControllerScope
scope
=
context
.
inheritFromWidgetOfExactType
(
_TabControllerScope
);
return
scope
?.
controller
;
}
@override
_DefaultTabControllerState
createState
()
=>
new
_DefaultTabControllerState
();
}
class
_DefaultTabControllerState
extends
State
<
DefaultTabController
>
with
SingleTickerProviderStateMixin
{
TabController
_controller
;
@override
void
initState
()
{
super
.
initState
();
_controller
=
new
TabController
(
vsync:
this
,
length:
config
.
length
,
initialIndex:
config
.
initialIndex
,
);
}
@override
void
dispose
()
{
_controller
.
dispose
();
super
.
dispose
();
}
@override
Widget
build
(
BuildContext
context
)
{
return
new
_TabControllerScope
(
controller:
_controller
,
enabled:
TickerMode
.
of
(
context
),
child:
config
.
child
,
);
}
}
packages/flutter/lib/src/material/tabs.dart
View file @
b23aed7a
This diff is collapsed.
Click to expand it.
packages/flutter/test/material/tabs_test.dart
View file @
b23aed7a
This diff is collapsed.
Click to expand it.
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