Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(162)

Side by Side Diff: sky/sdk/lib/widgets/tabs.dart

Issue 1221673006: Scrollable TabBar Version 0 (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Updated per review feedback Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sky/sdk/lib/widgets/scrollable.dart ('k') | sky/tests/examples/stocks-expected.txt » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 import 'dart:math' as math; 5 import 'dart:math' as math;
6 6
7 import 'package:sky/animation/scroll_behavior.dart';
8 import 'package:sky/painting/text_style.dart';
7 import 'package:sky/rendering/box.dart'; 9 import 'package:sky/rendering/box.dart';
8 import 'package:sky/rendering/object.dart'; 10 import 'package:sky/rendering/object.dart';
11 import 'package:vector_math/vector_math.dart';
9 import 'package:sky/widgets/basic.dart'; 12 import 'package:sky/widgets/basic.dart';
10 import 'package:sky/widgets/icon.dart'; 13 import 'package:sky/widgets/icon.dart';
11 import 'package:sky/widgets/ink_well.dart'; 14 import 'package:sky/widgets/ink_well.dart';
15 import 'package:sky/widgets/scrollable.dart';
12 import 'package:sky/widgets/theme.dart'; 16 import 'package:sky/widgets/theme.dart';
13 import 'package:sky/widgets/widget.dart'; 17 import 'package:sky/widgets/widget.dart';
14 18
15 typedef void SelectedIndexChanged(int selectedIndex); 19 typedef void SelectedIndexChanged(int selectedIndex);
20 typedef void LayoutChanged(Size size, List<double> widths);
16 21
22 // See https://www.google.com/design/spec/components/tabs.html#tabs-specs
17 const double _kTabHeight = 46.0; 23 const double _kTabHeight = 46.0;
18 const double _kTextAndIconTabHeight = 72.0; 24 const double _kTextAndIconTabHeight = 72.0;
19 const double _kTabIndicatorHeight = 2.0; 25 const double _kTabIndicatorHeight = 2.0;
20 const double _kMinTabWidth = 72.0; 26 const double _kMinTabWidth = 72.0;
27 const double _kMaxTabWidth = 264.0;
28 const double _kRelativeMaxTabWidth = 56.0;
29 const EdgeDims _kTabLabelPadding = const EdgeDims.symmetric(horizontal: 12.0);
30 const TextStyle _kTabTextStyle = const TextStyle(textAlign: TextAlign.center);
21 const int _kTabIconSize = 24; 31 const int _kTabIconSize = 24;
22 32
23 class TabBarParentData extends BoxParentData with 33 class TabBarParentData extends BoxParentData with
24 ContainerParentDataMixin<RenderBox> { } 34 ContainerParentDataMixin<RenderBox> { }
25 35
26 class RenderTabBar extends RenderBox with 36 class RenderTabBar extends RenderBox with
27 ContainerRenderObjectMixin<RenderBox, TabBarParentData>, 37 ContainerRenderObjectMixin<RenderBox, TabBarParentData>,
28 RenderBoxContainerDefaultsMixin<RenderBox, TabBarParentData> { 38 RenderBoxContainerDefaultsMixin<RenderBox, TabBarParentData> {
29 39
40 RenderTabBar(this.onLayoutChanged);
41
30 int _selectedIndex; 42 int _selectedIndex;
31 int get selectedIndex => _selectedIndex; 43 int get selectedIndex => _selectedIndex;
32 void set selectedIndex(int value) { 44 void set selectedIndex(int value) {
33 if (_selectedIndex != value) { 45 if (_selectedIndex != value) {
34 _selectedIndex = value; 46 _selectedIndex = value;
35 markNeedsPaint(); 47 markNeedsPaint();
36 } 48 }
37 } 49 }
38 50
39 Color _backgroundColor; 51 Color _backgroundColor;
(...skipping 16 matching lines...) Expand all
56 68
57 bool _textAndIcons; 69 bool _textAndIcons;
58 bool get textAndIcons => _textAndIcons; 70 bool get textAndIcons => _textAndIcons;
59 void set textAndIcons(bool value) { 71 void set textAndIcons(bool value) {
60 if (_textAndIcons != value) { 72 if (_textAndIcons != value) {
61 _textAndIcons = value; 73 _textAndIcons = value;
62 markNeedsLayout(); 74 markNeedsLayout();
63 } 75 }
64 } 76 }
65 77
78 bool _scrollable;
79 bool get scrollable => _scrollable;
80 void set scrollable(bool value) {
81 if (_scrollable != value) {
82 _scrollable = value;
83 markNeedsLayout();
84 }
85 }
86
66 void setupParentData(RenderBox child) { 87 void setupParentData(RenderBox child) {
67 if (child.parentData is! TabBarParentData) 88 if (child.parentData is! TabBarParentData)
68 child.parentData = new TabBarParentData(); 89 child.parentData = new TabBarParentData();
69 } 90 }
70 91
71 double getMinIntrinsicWidth(BoxConstraints constraints) { 92 double getMinIntrinsicWidth(BoxConstraints constraints) {
72 BoxConstraints widthConstraints = 93 BoxConstraints widthConstraints =
73 new BoxConstraints(maxWidth: constraints.maxWidth, maxHeight: constraint s.maxHeight); 94 new BoxConstraints(maxWidth: constraints.maxWidth, maxHeight: constraint s.maxHeight);
95
74 double maxWidth = 0.0; 96 double maxWidth = 0.0;
75 RenderBox child = firstChild; 97 RenderBox child = firstChild;
76 while (child != null) { 98 while (child != null) {
77 maxWidth = math.max(maxWidth, child.getMinIntrinsicWidth(widthConstraints) ); 99 maxWidth = math.max(maxWidth, child.getMinIntrinsicWidth(widthConstraints) );
78 assert(child.parentData is TabBarParentData); 100 assert(child.parentData is TabBarParentData);
79 child = child.parentData.nextSibling; 101 child = child.parentData.nextSibling;
80 } 102 }
81 return constraints.constrainWidth(maxWidth * childCount); 103 double width = scrollable ? maxWidth : maxWidth * childCount;
104 return constraints.constrainWidth(width);
82 } 105 }
83 106
84 double getMaxIntrinsicWidth(BoxConstraints constraints) { 107 double getMaxIntrinsicWidth(BoxConstraints constraints) {
85 BoxConstraints widthConstraints = 108 BoxConstraints widthConstraints =
86 new BoxConstraints(maxWidth: constraints.maxWidth, maxHeight: constraint s.maxHeight); 109 new BoxConstraints(maxWidth: constraints.maxWidth, maxHeight: constraint s.maxHeight);
110
87 double maxWidth = 0.0; 111 double maxWidth = 0.0;
88 RenderBox child = firstChild; 112 RenderBox child = firstChild;
89 while (child != null) { 113 while (child != null) {
90 maxWidth = math.max(maxWidth, child.getMaxIntrinsicWidth(widthConstraints) ); 114 maxWidth = math.max(maxWidth, child.getMaxIntrinsicWidth(widthConstraints) );
91 assert(child.parentData is TabBarParentData); 115 assert(child.parentData is TabBarParentData);
92 child = child.parentData.nextSibling; 116 child = child.parentData.nextSibling;
93 } 117 }
94 return constraints.constrainWidth(maxWidth * childCount); 118 double width = scrollable ? maxWidth : maxWidth * childCount;
119 return constraints.constrainWidth(width);
95 } 120 }
96 121
97 double get _tabBarHeight { 122 double get _tabBarHeight {
98 return (textAndIcons ? _kTextAndIconTabHeight : _kTabHeight) + _kTabIndicato rHeight; 123 return (textAndIcons ? _kTextAndIconTabHeight : _kTabHeight) + _kTabIndicato rHeight;
99 } 124 }
100 125
101 double _getIntrinsicHeight(BoxConstraints constraints) => constraints.constrai nHeight(_tabBarHeight); 126 double _getIntrinsicHeight(BoxConstraints constraints) => constraints.constrai nHeight(_tabBarHeight);
102 127
103 double getMinIntrinsicHeight(BoxConstraints constraints) => _getIntrinsicHeigh t(constraints); 128 double getMinIntrinsicHeight(BoxConstraints constraints) => _getIntrinsicHeigh t(constraints);
104 129
105 double getMaxIntrinsicHeight(BoxConstraints constraints) => _getIntrinsicHeigh t(constraints); 130 double getMaxIntrinsicHeight(BoxConstraints constraints) => _getIntrinsicHeigh t(constraints);
106 131
107 void performLayout() { 132 void layoutFixedWidthTabs() {
108 assert(constraints is BoxConstraints);
109
110 size = constraints.constrain(new Size(constraints.maxWidth, _tabBarHeight));
111 assert(!size.isInfinite);
112
113 if (childCount == 0)
114 return;
115
116 double tabWidth = size.width / childCount; 133 double tabWidth = size.width / childCount;
117 BoxConstraints tabConstraints = 134 BoxConstraints tabConstraints =
118 new BoxConstraints.tightFor(width: tabWidth, height: size.height); 135 new BoxConstraints.tightFor(width: tabWidth, height: size.height);
119 double x = 0.0; 136 double x = 0.0;
120 RenderBox child = firstChild; 137 RenderBox child = firstChild;
121 while (child != null) { 138 while (child != null) {
122 child.layout(tabConstraints); 139 child.layout(tabConstraints);
123 assert(child.parentData is TabBarParentData); 140 assert(child.parentData is TabBarParentData);
124 child.parentData.position = new Point(x, 0.0); 141 child.parentData.position = new Point(x, 0.0);
125 x += tabWidth; 142 x += tabWidth;
126 child = child.parentData.nextSibling; 143 child = child.parentData.nextSibling;
127 } 144 }
128 } 145 }
129 146
147 void layoutScrollableTabs() {
148 BoxConstraints tabConstraints = new BoxConstraints(
149 minWidth: _kMinTabWidth,
150 maxWidth: math.min(size.width - _kRelativeMaxTabWidth, _kMaxTabWidth),
151 minHeight: size.height,
152 maxHeight: size.height);
153 double x = 0.0;
154 RenderBox child = firstChild;
155 while (child != null) {
156 child.layout(tabConstraints, parentUsesSize: true);
157 assert(child.parentData is TabBarParentData);
158 child.parentData.position = new Point(x, 0.0);
159 x += child.size.width;
160 child = child.parentData.nextSibling;
161 }
162 }
163
164 Size layoutSize;
165 List<double> layoutWidths;
166 LayoutChanged onLayoutChanged;
167
168 void reportLayoutChangedIfNeeded() {
169 assert(onLayoutChanged != null);
170 List<double> widths = new List<double>(childCount);
171 if (!scrollable && childCount > 0) {
172 double tabWidth = size.width / childCount;
173 widths.fillRange(0, widths.length - 1, tabWidth);
174 } else if (scrollable) {
175 RenderBox child = firstChild;
176 int childIndex = 0;
177 while (child != null) {
178 widths[childIndex++] = child.size.width;
179 child = child.parentData.nextSibling;
180 }
181 assert(childIndex == widths.length);
182 }
183 if (size != layoutSize || widths != layoutWidths) {
184 layoutSize = size;
185 layoutWidths = widths;
186 onLayoutChanged(layoutSize, layoutWidths);
187 }
188 }
189
190 void performLayout() {
191 assert(constraints is BoxConstraints);
192
193 size = constraints.constrain(new Size(constraints.maxWidth, _tabBarHeight));
194 assert(!size.isInfinite);
195
196 if (childCount == 0)
197 return;
198
199 if (scrollable)
200 layoutScrollableTabs();
201 else
202 layoutFixedWidthTabs();
203
204 if (onLayoutChanged != null)
205 reportLayoutChangedIfNeeded();
206 }
207
130 void hitTestChildren(HitTestResult result, { Point position }) { 208 void hitTestChildren(HitTestResult result, { Point position }) {
131 defaultHitTestChildren(result, position: position); 209 defaultHitTestChildren(result, position: position);
132 } 210 }
133 211
134 void _paintIndicator(PaintingCanvas canvas, RenderBox selectedTab, Offset offs et) { 212 void _paintIndicator(PaintingCanvas canvas, RenderBox selectedTab, Offset offs et) {
135 if (indicatorColor == null) 213 if (indicatorColor == null)
136 return; 214 return;
137 215
138 var size = new Size(selectedTab.size.width, _kTabIndicatorHeight); 216 var size = new Size(selectedTab.size.width, _kTabIndicatorHeight);
139 var point = new Point( 217 var point = new Point(
140 selectedTab.parentData.position.x, 218 selectedTab.parentData.position.x,
141 _tabBarHeight - _kTabIndicatorHeight 219 _tabBarHeight - _kTabIndicatorHeight
142 ); 220 );
143 Rect rect = (point + offset) & size; 221 Rect rect = (point + offset) & size;
144 canvas.drawRect(rect, new Paint()..color = indicatorColor); 222 canvas.drawRect(rect, new Paint()..color = indicatorColor);
145 } 223 }
146 224
147 void paint(PaintingCanvas canvas, Offset offset) { 225 void paint(PaintingCanvas canvas, Offset offset) {
148 if (backgroundColor != null) { 226 if (backgroundColor != null) {
149 Rect rect = offset & size; 227 double width = layoutWidths != null
228 ? layoutWidths.reduce((sum, width) => sum + width)
229 : size.width;
230 Rect rect = offset & new Size(width, size.height);
150 canvas.drawRect(rect, new Paint()..color = backgroundColor); 231 canvas.drawRect(rect, new Paint()..color = backgroundColor);
151 } 232 }
152 233
153 int index = 0; 234 int index = 0;
154 RenderBox child = firstChild; 235 RenderBox child = firstChild;
155 while (child != null) { 236 while (child != null) {
156 assert(child.parentData is TabBarParentData); 237 assert(child.parentData is TabBarParentData);
157 canvas.paintChild(child, child.parentData.position + offset); 238 canvas.paintChild(child, child.parentData.position + offset);
158 if (index++ == selectedIndex) 239 if (index++ == selectedIndex)
159 _paintIndicator(canvas, child, offset); 240 _paintIndicator(canvas, child, offset);
160 child = child.parentData.nextSibling; 241 child = child.parentData.nextSibling;
161 } 242 }
162 } 243 }
163 } 244 }
164 245
165 class TabBarWrapper extends MultiChildRenderObjectWrapper { 246 class TabBarWrapper extends MultiChildRenderObjectWrapper {
166 TabBarWrapper({ 247 TabBarWrapper({
167 List<Widget> children, 248 List<Widget> children,
168 this.selectedIndex, 249 this.selectedIndex,
169 this.backgroundColor, 250 this.backgroundColor,
170 this.indicatorColor, 251 this.indicatorColor,
171 this.textAndIcons, 252 this.textAndIcons,
253 this.scrollable: false,
254 this.onLayoutChanged,
172 String key 255 String key
173 }) : super(key: key, children: children); 256 }) : super(key: key, children: children);
174 257
175 final int selectedIndex; 258 final int selectedIndex;
176 final Color backgroundColor; 259 final Color backgroundColor;
177 final Color indicatorColor; 260 final Color indicatorColor;
178 final bool textAndIcons; 261 final bool textAndIcons;
262 final bool scrollable;
263 final LayoutChanged onLayoutChanged;
179 264
180 RenderTabBar get root => super.root; 265 RenderTabBar get root => super.root;
181 RenderTabBar createNode() => new RenderTabBar(); 266 RenderTabBar createNode() => new RenderTabBar(onLayoutChanged);
182 267
183 void syncRenderObject(Widget old) { 268 void syncRenderObject(Widget old) {
184 super.syncRenderObject(old); 269 super.syncRenderObject(old);
185 root.selectedIndex = selectedIndex; 270 root.selectedIndex = selectedIndex;
186 root.backgroundColor = backgroundColor; 271 root.backgroundColor = backgroundColor;
187 root.indicatorColor = indicatorColor; 272 root.indicatorColor = indicatorColor;
188 root.textAndIcons = textAndIcons; 273 root.textAndIcons = textAndIcons;
274 root.scrollable = scrollable;
275 root.onLayoutChanged = onLayoutChanged;
189 } 276 }
190 } 277 }
191 278
192 class TabLabel { 279 class TabLabel {
193 const TabLabel({ this.text, this.icon }); 280 const TabLabel({ this.text, this.icon });
194 281
195 final String text; 282 final String text;
196 final String icon; 283 final String icon;
197 } 284 }
198 285
199 class Tab extends Component { 286 class Tab extends Component {
200 Tab({ 287 Tab({
201 String key, 288 String key,
202 this.label, 289 this.label,
203 this.selected: false 290 this.selected: false
204 }) : super(key: key) { 291 }) : super(key: key) {
205 assert(label.text != null || label.icon != null); 292 assert(label.text != null || label.icon != null);
206 } 293 }
207 294
208 final TabLabel label; 295 final TabLabel label;
209 final bool selected; 296 final bool selected;
210 297
211 Widget _buildLabelText() { 298 Widget _buildLabelText() {
212 assert(label.text != null); 299 assert(label.text != null);
213 return new Text(label.text, style: Theme.of(this).toolbarText.button); 300 TextStyle textStyle = Theme.of(this).toolbarText.button.merge(_kTabTextStyle );
301 return new Text(label.text, style: textStyle);
214 } 302 }
215 303
216 Widget _buildLabelIcon() { 304 Widget _buildLabelIcon() {
217 assert(label.icon != null); 305 assert(label.icon != null);
218 return new Icon(type: label.icon, size: _kTabIconSize); 306 return new Icon(type: label.icon, size: _kTabIconSize);
219 } 307 }
220 308
221 Widget build() { 309 Widget build() {
222 Widget labelContents; 310 Widget labelContents;
223 if (label.icon == null) { 311 if (label.icon == null) {
(...skipping 15 matching lines...) Expand all
239 ); 327 );
240 } 328 }
241 329
242 Widget highlightedLabel = new Opacity( 330 Widget highlightedLabel = new Opacity(
243 child: labelContents, 331 child: labelContents,
244 opacity: selected ? 1.0 : 0.7 332 opacity: selected ? 1.0 : 0.7
245 ); 333 );
246 334
247 Container centeredLabel = new Container( 335 Container centeredLabel = new Container(
248 child: new Center(child: highlightedLabel), 336 child: new Center(child: highlightedLabel),
249 constraints: new BoxConstraints(minWidth: _kMinTabWidth) 337 constraints: new BoxConstraints(minWidth: _kMinTabWidth),
338 padding: _kTabLabelPadding
250 ); 339 );
251 340
252 return new InkWell(child: centeredLabel); 341 return new InkWell(child: centeredLabel);
253 } 342 }
254 } 343 }
255 344
256 class TabBar extends Component { 345 class TabBar extends Scrollable {
257 TabBar({ 346 TabBar({
258 String key, 347 String key,
259 this.labels, 348 this.labels,
260 this.selectedIndex: 0, 349 this.selectedIndex: 0,
261 this.onChanged 350 this.onChanged,
262 }) : super(key: key); 351 this.scrollable: false
352 }) : super(key: key, direction: ScrollDirection.horizontal);
263 353
264 final Iterable<TabLabel> labels; 354 Iterable<TabLabel> labels;
265 final int selectedIndex; 355 int selectedIndex;
266 final SelectedIndexChanged onChanged; 356 SelectedIndexChanged onChanged;
357 bool scrollable;
358
359 void syncFields(TabBar source) {
360 super.syncFields(source);
361 labels = source.labels;
362 selectedIndex = source.selectedIndex;
363 onChanged = source.onChanged;
364 scrollable = source.scrollable;
365 if (!scrollable)
366 scrollTo(0.0);
367 }
368
369 ScrollBehavior createScrollBehavior() => new BoundedScrollBehavior();
370 BoundedScrollBehavior get scrollBehavior => super.scrollBehavior;
267 371
268 void _handleTap(int tabIndex) { 372 void _handleTap(int tabIndex) {
269 if (tabIndex != selectedIndex && onChanged != null) 373 if (tabIndex != selectedIndex && onChanged != null)
270 onChanged(tabIndex); 374 onChanged(tabIndex);
271 } 375 }
272 376
273 Widget _toTab(TabLabel label, int tabIndex) { 377 Widget _toTab(TabLabel label, int tabIndex) {
274 Tab tab = new Tab( 378 Tab tab = new Tab(
275 label: label, 379 label: label,
276 selected: tabIndex == selectedIndex, 380 selected: tabIndex == selectedIndex,
277 key: label.text == null ? label.icon : label.text 381 key: label.text == null ? label.icon : label.text
278 ); 382 );
279 return new Listener( 383 return new Listener(
280 child: tab, 384 child: tab,
281 onGestureTap: (_) => _handleTap(tabIndex) 385 onGestureTap: (_) => _handleTap(tabIndex)
282 ); 386 );
283 } 387 }
284 388
285 Widget build() { 389 Size _tabBarSize;
390 List<double> _tabWidths;
391
392 void _layoutChanged(Size tabBarSize, List<double> tabWidths) {
393 setState(() {
394 _tabBarSize = tabBarSize;
395 _tabWidths = tabWidths;
396 scrollBehavior.maxOffset =
397 _tabWidths.reduce((sum, width) => sum + width) - _tabBarSize.width;
398 });
399 }
400
401 Widget buildContent() {
286 assert(labels != null && labels.isNotEmpty); 402 assert(labels != null && labels.isNotEmpty);
287 List<Widget> tabs = <Widget>[]; 403 List<Widget> tabs = <Widget>[];
288 bool textAndIcons = false; 404 bool textAndIcons = false;
289 int tabIndex = 0; 405 int tabIndex = 0;
290 for (TabLabel label in labels) { 406 for (TabLabel label in labels) {
291 tabs.add(_toTab(label, tabIndex++)); 407 tabs.add(_toTab(label, tabIndex++));
292 if (label.text != null && label.icon != null) 408 if (label.text != null && label.icon != null)
293 textAndIcons = true; 409 textAndIcons = true;
294 } 410 }
295 return new TabBarWrapper( 411
412 TabBarWrapper tabBarWrapper = new TabBarWrapper(
296 children: tabs, 413 children: tabs,
297 selectedIndex: selectedIndex, 414 selectedIndex: selectedIndex,
298 backgroundColor: Theme.of(this).primaryColor, 415 backgroundColor: Theme.of(this).primaryColor,
299 indicatorColor: Theme.of(this).accentColor, 416 indicatorColor: Theme.of(this).accentColor,
300 textAndIcons: textAndIcons 417 textAndIcons: textAndIcons,
418 scrollable: scrollable,
419 onLayoutChanged: scrollable ? _layoutChanged : null
301 ); 420 );
421
422 Matrix4 transform = new Matrix4.identity();
423 transform.translate(-scrollOffset, 0.0);
424 return new Transform(child: tabBarWrapper, transform: transform);
302 } 425 }
303 } 426 }
304 427
305 class TabNavigatorView { 428 class TabNavigatorView {
306 TabNavigatorView({ this.label, this.builder }); 429 TabNavigatorView({ this.label, this.builder });
307 430
308 final TabLabel label; 431 final TabLabel label;
309 final Builder builder; 432 final Builder builder;
310 433
311 Widget buildContent() { 434 Widget buildContent() {
312 assert(builder != null); 435 assert(builder != null);
313 Widget content = builder(); 436 Widget content = builder();
314 assert(content != null); 437 assert(content != null);
315 return content; 438 return content;
316 } 439 }
317 } 440 }
318 441
319 class TabNavigator extends Component { 442 class TabNavigator extends Component {
320 TabNavigator({ 443 TabNavigator({
321 String key, 444 String key,
322 this.views, 445 this.views,
323 this.selectedIndex: 0, 446 this.selectedIndex: 0,
324 this.onChanged 447 this.onChanged,
448 this.scrollable: false
325 }) : super(key: key); 449 }) : super(key: key);
326 450
327 final List<TabNavigatorView> views; 451 final List<TabNavigatorView> views;
328 final int selectedIndex; 452 final int selectedIndex;
329 final SelectedIndexChanged onChanged; 453 final SelectedIndexChanged onChanged;
454 final bool scrollable;
330 455
331 void _handleSelectedIndexChanged(int tabIndex) { 456 void _handleSelectedIndexChanged(int tabIndex) {
332 if (onChanged != null) 457 if (onChanged != null)
333 onChanged(tabIndex); 458 onChanged(tabIndex);
334 } 459 }
335 460
336 Widget build() { 461 Widget build() {
337 assert(views != null && views.isNotEmpty); 462 assert(views != null && views.isNotEmpty);
338 assert(selectedIndex >= 0 && selectedIndex < views.length); 463 assert(selectedIndex >= 0 && selectedIndex < views.length);
339 464
340 TabBar tabBar = new TabBar( 465 TabBar tabBar = new TabBar(
341 labels: views.map((view) => view.label), 466 labels: views.map((view) => view.label),
342 onChanged: _handleSelectedIndexChanged, 467 onChanged: _handleSelectedIndexChanged,
343 selectedIndex: selectedIndex 468 selectedIndex: selectedIndex,
469 scrollable: scrollable
344 ); 470 );
345 471
346 Widget content = views[selectedIndex].buildContent(); 472 Widget content = views[selectedIndex].buildContent();
347 return new Flex([tabBar, new Flexible(child: content)], 473 return new Flex([tabBar, new Flexible(child: content)],
348 direction: FlexDirection.vertical 474 direction: FlexDirection.vertical
349 ); 475 );
350 } 476 }
351 } 477 }
OLDNEW
« no previous file with comments | « sky/sdk/lib/widgets/scrollable.dart ('k') | sky/tests/examples/stocks-expected.txt » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698