| OLD | NEW |
| 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/generators.dart'; | |
| 8 import 'package:sky/animation/mechanics.dart'; | |
| 9 import 'package:sky/animation/scroll_behavior.dart'; | 7 import 'package:sky/animation/scroll_behavior.dart'; |
| 10 import 'package:sky/painting/text_style.dart'; | 8 import 'package:sky/painting/text_style.dart'; |
| 11 import 'package:sky/rendering/box.dart'; | 9 import 'package:sky/rendering/box.dart'; |
| 12 import 'package:sky/rendering/object.dart'; | 10 import 'package:sky/rendering/object.dart'; |
| 13 import 'package:vector_math/vector_math.dart'; | |
| 14 import 'package:sky/theme/colors.dart' as colors; | 11 import 'package:sky/theme/colors.dart' as colors; |
| 15 import 'package:sky/theme/typography.dart' as typography; | 12 import 'package:sky/theme/typography.dart' as typography; |
| 16 import 'package:sky/widgets/basic.dart'; | 13 import 'package:sky/widgets/basic.dart'; |
| 17 import 'package:sky/widgets/default_text_style.dart'; | 14 import 'package:sky/widgets/default_text_style.dart'; |
| 18 import 'package:sky/widgets/icon.dart'; | 15 import 'package:sky/widgets/icon.dart'; |
| 19 import 'package:sky/widgets/ink_well.dart'; | 16 import 'package:sky/widgets/ink_well.dart'; |
| 20 import 'package:sky/widgets/scrollable.dart'; | 17 import 'package:sky/widgets/scrollable.dart'; |
| 21 import 'package:sky/widgets/theme.dart'; | 18 import 'package:sky/widgets/theme.dart'; |
| 22 import 'package:sky/widgets/widget.dart'; | 19 import 'package:sky/widgets/widget.dart'; |
| 20 import 'package:vector_math/vector_math.dart'; |
| 23 | 21 |
| 24 typedef void SelectedIndexChanged(int selectedIndex); | 22 typedef void SelectedIndexChanged(int selectedIndex); |
| 25 typedef void LayoutChanged(Size size, List<double> widths); | 23 typedef void LayoutChanged(Size size, List<double> widths); |
| 26 | 24 |
| 27 // See https://www.google.com/design/spec/components/tabs.html#tabs-specs | 25 // See https://www.google.com/design/spec/components/tabs.html#tabs-specs |
| 28 const double _kTabHeight = 46.0; | 26 const double _kTabHeight = 46.0; |
| 29 const double _kTextAndIconTabHeight = 72.0; | 27 const double _kTextAndIconTabHeight = 72.0; |
| 30 const double _kTabIndicatorHeight = 2.0; | 28 const double _kTabIndicatorHeight = 2.0; |
| 31 const double _kMinTabWidth = 72.0; | 29 const double _kMinTabWidth = 72.0; |
| 32 const double _kMaxTabWidth = 264.0; | 30 const double _kMaxTabWidth = 264.0; |
| (...skipping 307 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 340 Container centeredLabel = new Container( | 338 Container centeredLabel = new Container( |
| 341 child: new Center(child: highlightedLabel), | 339 child: new Center(child: highlightedLabel), |
| 342 constraints: new BoxConstraints(minWidth: _kMinTabWidth), | 340 constraints: new BoxConstraints(minWidth: _kMinTabWidth), |
| 343 padding: _kTabLabelPadding | 341 padding: _kTabLabelPadding |
| 344 ); | 342 ); |
| 345 | 343 |
| 346 return new InkWell(child: centeredLabel); | 344 return new InkWell(child: centeredLabel); |
| 347 } | 345 } |
| 348 } | 346 } |
| 349 | 347 |
| 350 class TabBarScrollBehavior extends ScrollBehavior { | |
| 351 TabBarScrollBehavior({ this.maxScrollOffset: 0.0 }); | |
| 352 | |
| 353 double maxScrollOffset; | |
| 354 | |
| 355 Simulation release(Particle particle) { | |
| 356 if (particle.velocity == 0.0 || particle.position < 0.0 || particle.position
>= maxScrollOffset) | |
| 357 return null; | |
| 358 | |
| 359 System system = new ParticleInBoxWithFriction( | |
| 360 particle: particle, | |
| 361 friction: _kTabBarScrollFriction, | |
| 362 box: new ClosedBox(min: 0.0, max: maxScrollOffset)); | |
| 363 return new Simulation(system, terminationCondition: () => particle.position
== 0.0); | |
| 364 } | |
| 365 | |
| 366 double applyCurve(double scrollOffset, double scrollDelta) { | |
| 367 return (scrollOffset + scrollDelta).clamp(0.0, maxScrollOffset); | |
| 368 } | |
| 369 } | |
| 370 | |
| 371 class TabBar extends Scrollable { | 348 class TabBar extends Scrollable { |
| 372 TabBar({ | 349 TabBar({ |
| 373 String key, | 350 String key, |
| 374 this.labels, | 351 this.labels, |
| 375 this.selectedIndex: 0, | 352 this.selectedIndex: 0, |
| 376 this.onChanged, | 353 this.onChanged, |
| 377 this.scrollable: false | 354 this.scrollable: false |
| 378 }) : super(key: key, direction: ScrollDirection.horizontal); | 355 }) : super(key: key, direction: ScrollDirection.horizontal); |
| 379 | 356 |
| 380 Iterable<TabLabel> labels; | 357 Iterable<TabLabel> labels; |
| 381 int selectedIndex; | 358 int selectedIndex; |
| 382 SelectedIndexChanged onChanged; | 359 SelectedIndexChanged onChanged; |
| 383 bool scrollable; | 360 bool scrollable; |
| 384 | 361 |
| 385 void syncFields(TabBar source) { | 362 void syncFields(TabBar source) { |
| 386 super.syncFields(source); | 363 super.syncFields(source); |
| 387 labels = source.labels; | 364 labels = source.labels; |
| 388 selectedIndex = source.selectedIndex; | 365 selectedIndex = source.selectedIndex; |
| 389 onChanged = source.onChanged; | 366 onChanged = source.onChanged; |
| 390 scrollable = source.scrollable; | 367 scrollable = source.scrollable; |
| 391 if (!scrollable) | 368 if (!scrollable) |
| 392 scrollTo(0.0); | 369 scrollTo(0.0); |
| 393 } | 370 } |
| 394 | 371 |
| 395 ScrollBehavior createScrollBehavior() => new TabBarScrollBehavior(); | 372 ScrollBehavior createScrollBehavior() => new FlingBehavior(); |
| 396 TabBarScrollBehavior get scrollBehavior => super.scrollBehavior; | 373 FlingBehavior get scrollBehavior => super.scrollBehavior; |
| 397 | 374 |
| 398 void _handleTap(int tabIndex) { | 375 void _handleTap(int tabIndex) { |
| 399 if (tabIndex != selectedIndex && onChanged != null) | 376 if (tabIndex != selectedIndex && onChanged != null) |
| 400 onChanged(tabIndex); | 377 onChanged(tabIndex); |
| 401 } | 378 } |
| 402 | 379 |
| 403 Widget _toTab(TabLabel label, int tabIndex) { | 380 Widget _toTab(TabLabel label, int tabIndex) { |
| 404 Tab tab = new Tab( | 381 Tab tab = new Tab( |
| 405 label: label, | 382 label: label, |
| 406 selected: tabIndex == selectedIndex, | 383 selected: tabIndex == selectedIndex, |
| 407 key: label.text == null ? label.icon : label.text | 384 key: label.text == null ? label.icon : label.text |
| 408 ); | 385 ); |
| 409 return new Listener( | 386 return new Listener( |
| 410 child: tab, | 387 child: tab, |
| 411 onGestureTap: (_) => _handleTap(tabIndex) | 388 onGestureTap: (_) => _handleTap(tabIndex) |
| 412 ); | 389 ); |
| 413 } | 390 } |
| 414 | 391 |
| 415 Size _tabBarSize; | 392 Size _tabBarSize; |
| 416 List<double> _tabWidths; | 393 List<double> _tabWidths; |
| 417 | 394 |
| 418 void _layoutChanged(Size tabBarSize, List<double> tabWidths) { | 395 void _layoutChanged(Size tabBarSize, List<double> tabWidths) { |
| 419 setState(() { | 396 setState(() { |
| 420 _tabBarSize = tabBarSize; | 397 _tabBarSize = tabBarSize; |
| 421 _tabWidths = tabWidths; | 398 _tabWidths = tabWidths; |
| 422 scrollBehavior.maxScrollOffset = | 399 scrollBehavior.containerSize = _tabBarSize.width; |
| 423 _tabWidths.reduce((sum, width) => sum + width) - _tabBarSize.width; | 400 scrollBehavior.contentsSize = _tabWidths.reduce((sum, width) => sum + widt
h); |
| 424 }); | 401 }); |
| 425 } | 402 } |
| 426 | 403 |
| 427 Widget buildContent() { | 404 Widget buildContent() { |
| 428 assert(labels != null && labels.isNotEmpty); | 405 assert(labels != null && labels.isNotEmpty); |
| 429 List<Widget> tabs = <Widget>[]; | 406 List<Widget> tabs = <Widget>[]; |
| 430 bool textAndIcons = false; | 407 bool textAndIcons = false; |
| 431 int tabIndex = 0; | 408 int tabIndex = 0; |
| 432 for (TabLabel label in labels) { | 409 for (TabLabel label in labels) { |
| 433 tabs.add(_toTab(label, tabIndex++)); | 410 tabs.add(_toTab(label, tabIndex++)); |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 522 selectedIndex: selectedIndex, | 499 selectedIndex: selectedIndex, |
| 523 scrollable: scrollable | 500 scrollable: scrollable |
| 524 ); | 501 ); |
| 525 | 502 |
| 526 Widget content = views[selectedIndex].buildContent(); | 503 Widget content = views[selectedIndex].buildContent(); |
| 527 return new Flex([tabBar, new Flexible(child: content)], | 504 return new Flex([tabBar, new Flexible(child: content)], |
| 528 direction: FlexDirection.vertical | 505 direction: FlexDirection.vertical |
| 529 ); | 506 ); |
| 530 } | 507 } |
| 531 } | 508 } |
| OLD | NEW |