From 65caad4703dac2bcf2131fb802e68e91f0bed504 Mon Sep 17 00:00:00 2001 From: Hans Muller Date: Mon, 27 Jul 2015 08:31:53 -0700 Subject: [PATCH] Enable fling scrolling in TabBar --- packages/flutter/example/widgets/tabs.dart | 17 +++-- .../lib/animation/scroll_behavior.dart | 2 +- packages/flutter/lib/widgets/tabs.dart | 73 ++++++++++++------- packages/flutter/pubspec.yaml | 2 +- 4 files changed, 59 insertions(+), 35 deletions(-) diff --git a/packages/flutter/example/widgets/tabs.dart b/packages/flutter/example/widgets/tabs.dart index 00b4521513..9aa1a4141d 100644 --- a/packages/flutter/example/widgets/tabs.dart +++ b/packages/flutter/example/widgets/tabs.dart @@ -16,11 +16,12 @@ class TabbedNavigatorApp extends App { // The index of the selected tab for each of the TabNavigators constructed below. List selectedIndices = new List.filled(5, 0); - TabNavigator _buildTabNavigator(int n, List views, {scrollable: false}) { + TabNavigator _buildTabNavigator(int n, List views, Key key, {isScrollable: false}) { return new TabNavigator( + key: key, views: views, selectedIndex: selectedIndices[n], - scrollable: scrollable, + isScrollable: isScrollable, onChanged: (tabIndex) { setState(() { selectedIndices[n] = tabIndex; } ); } @@ -41,7 +42,7 @@ class TabbedNavigatorApp extends App { builder: () => _buildContent(text) ); }); - return _buildTabNavigator(n, views.toList()); + return _buildTabNavigator(n, views.toList(), new Key('textLabelsTabNavigator')); } TabNavigator _buildIconLabelsTabNavigator(int n) { @@ -52,7 +53,7 @@ class TabbedNavigatorApp extends App { builder: () => _buildContent(icon_name) ); }); - return _buildTabNavigator(n, views.toList()); + return _buildTabNavigator(n, views.toList(), new Key('iconLabelsTabNavigator')); } TabNavigator _buildTextAndIconLabelsTabNavigator(int n) { @@ -70,7 +71,7 @@ class TabbedNavigatorApp extends App { builder: () => _buildContent("Summary") ) ]; - return _buildTabNavigator(n, views); + return _buildTabNavigator(n, views, new Key('textAndIconLabelsTabNavigator')); } TabNavigator _buildScrollableTabNavigator(int n) { @@ -80,7 +81,7 @@ class TabbedNavigatorApp extends App { "THIS TAB IS PRETTY WIDE TOO", "MORE", "TABS", - "TO", + "TO", "STRETCH", "OUT", "THE", @@ -92,7 +93,7 @@ class TabbedNavigatorApp extends App { builder: () => _buildContent(text) ); }); - return _buildTabNavigator(n, views.toList(), scrollable: true); + return _buildTabNavigator(n, views.toList(), new Key('scrollableTabNavigator'), isScrollable: true); } @@ -124,7 +125,7 @@ class TabbedNavigatorApp extends App { ) ]; - TabNavigator tabNavigator = _buildTabNavigator(4, views); + TabNavigator tabNavigator = _buildTabNavigator(4, views, new Key('tabs')); assert(selectedIndices.length == 5); ToolBar toolbar = new ToolBar( diff --git a/packages/flutter/lib/animation/scroll_behavior.dart b/packages/flutter/lib/animation/scroll_behavior.dart index 2e8c2be7e8..7bd713c9dc 100644 --- a/packages/flutter/lib/animation/scroll_behavior.dart +++ b/packages/flutter/lib/animation/scroll_behavior.dart @@ -60,7 +60,7 @@ class FlingBehavior extends BoundedBehavior { : super(contentsSize: contentsSize, containerSize: containerSize); Simulation release(double position, double velocity) { - return createDefaultScrollSimulation(position, 0.0, minScrollOffset, maxScrollOffset); + return createDefaultScrollSimulation(position, velocity, minScrollOffset, maxScrollOffset); } } diff --git a/packages/flutter/lib/widgets/tabs.dart b/packages/flutter/lib/widgets/tabs.dart index 289fe28319..0676a8de72 100644 --- a/packages/flutter/lib/widgets/tabs.dart +++ b/packages/flutter/lib/widgets/tabs.dart @@ -4,6 +4,7 @@ import 'dart:math' as math; +import 'package:newton/newton.dart'; import 'package:sky/animation/scroll_behavior.dart'; import 'package:sky/painting/text_style.dart'; import 'package:sky/rendering/box.dart'; @@ -32,7 +33,7 @@ const double _kRelativeMaxTabWidth = 56.0; const EdgeDims _kTabLabelPadding = const EdgeDims.symmetric(horizontal: 12.0); const TextStyle _kTabTextStyle = const TextStyle(textAlign: TextAlign.center); const int _kTabIconSize = 24; -const double _kTabBarScrollFriction = 0.005; +const double _kTabBarScrollDrag = 0.025; class TabBarParentData extends BoxParentData with ContainerParentDataMixin { } @@ -79,11 +80,11 @@ class RenderTabBar extends RenderBox with } } - bool _scrollable; - bool get scrollable => _scrollable; - void set scrollable(bool value) { - if (_scrollable != value) { - _scrollable = value; + bool _isScrollable; + bool get isScrollable => _isScrollable; + void set isScrollable(bool value) { + if (_isScrollable != value) { + _isScrollable = value; markNeedsLayout(); } } @@ -104,7 +105,7 @@ class RenderTabBar extends RenderBox with assert(child.parentData is TabBarParentData); child = child.parentData.nextSibling; } - double width = scrollable ? maxWidth : maxWidth * childCount; + double width = isScrollable ? maxWidth : maxWidth * childCount; return constraints.constrainWidth(width); } @@ -119,7 +120,7 @@ class RenderTabBar extends RenderBox with assert(child.parentData is TabBarParentData); child = child.parentData.nextSibling; } - double width = scrollable ? maxWidth : maxWidth * childCount; + double width = isScrollable ? maxWidth : maxWidth * childCount; return constraints.constrainWidth(width); } @@ -172,10 +173,10 @@ class RenderTabBar extends RenderBox with void reportLayoutChangedIfNeeded() { assert(onLayoutChanged != null); List widths = new List(childCount); - if (!scrollable && childCount > 0) { + if (!isScrollable && childCount > 0) { double tabWidth = size.width / childCount; widths.fillRange(0, widths.length - 1, tabWidth); - } else if (scrollable) { + } else if (isScrollable) { RenderBox child = firstChild; int childIndex = 0; while (child != null) { @@ -200,7 +201,7 @@ class RenderTabBar extends RenderBox with if (childCount == 0) return; - if (scrollable) + if (isScrollable) layoutScrollableTabs(); else layoutFixedWidthTabs(); @@ -255,7 +256,7 @@ class TabBarWrapper extends MultiChildRenderObjectWrapper { this.backgroundColor, this.indicatorColor, this.textAndIcons, - this.scrollable: false, + this.isScrollable: false, this.onLayoutChanged }) : super(key: key, children: children); @@ -263,7 +264,7 @@ class TabBarWrapper extends MultiChildRenderObjectWrapper { final Color backgroundColor; final Color indicatorColor; final bool textAndIcons; - final bool scrollable; + final bool isScrollable; final LayoutChanged onLayoutChanged; RenderTabBar get root => super.root; @@ -275,7 +276,7 @@ class TabBarWrapper extends MultiChildRenderObjectWrapper { root.backgroundColor = backgroundColor; root.indicatorColor = indicatorColor; root.textAndIcons = textAndIcons; - root.scrollable = scrollable; + root.isScrollable = isScrollable; root.onLayoutChanged = onLayoutChanged; } } @@ -345,32 +346,54 @@ class Tab extends Component { } } +class _TabsScrollBehavior extends BoundedBehavior { + _TabsScrollBehavior({ double contentsSize: 0.0, double containerSize: 0.0 }) + : super(contentsSize: contentsSize, containerSize: containerSize); + + bool isScrollable = true; + + Simulation release(double position, double velocity) { + if (!isScrollable) + return null; + + double velocityPerSecond = velocity * 1000.0; + return new BoundedFrictionSimulation( + _kTabBarScrollDrag, position, velocityPerSecond, minScrollOffset, maxScrollOffset + ); + } + + double applyCurve(double scrollOffset, double scrollDelta) { + return (isScrollable) ? super.applyCurve(scrollOffset, scrollDelta) : 0.0; + } +} + class TabBar extends Scrollable { TabBar({ Key key, this.labels, this.selectedIndex: 0, this.onChanged, - this.scrollable: false + this.isScrollable: false }) : super(key: key, direction: ScrollDirection.horizontal); Iterable labels; int selectedIndex; SelectedIndexChanged onChanged; - bool scrollable; + bool isScrollable; void syncFields(TabBar source) { super.syncFields(source); labels = source.labels; selectedIndex = source.selectedIndex; onChanged = source.onChanged; - scrollable = source.scrollable; - if (!scrollable) + isScrollable = source.isScrollable; + if (!isScrollable) scrollTo(0.0); + scrollBehavior.isScrollable = source.isScrollable; } - ScrollBehavior createScrollBehavior() => new FlingBehavior(); - FlingBehavior get scrollBehavior => super.scrollBehavior; + ScrollBehavior createScrollBehavior() => new _TabsScrollBehavior(); + _TabsScrollBehavior get scrollBehavior => super.scrollBehavior; void _handleTap(int tabIndex) { if (tabIndex != selectedIndex && onChanged != null) @@ -445,8 +468,8 @@ class TabBar extends Scrollable { backgroundColor: backgroundColor, indicatorColor: indicatorColor, textAndIcons: textAndIcons, - scrollable: scrollable, - onLayoutChanged: scrollable ? _layoutChanged : null + isScrollable: isScrollable, + onLayoutChanged: isScrollable ? _layoutChanged : null ) ) ) @@ -474,13 +497,13 @@ class TabNavigator extends Component { this.views, this.selectedIndex: 0, this.onChanged, - this.scrollable: false + this.isScrollable: false }) : super(key: key); final List views; final int selectedIndex; final SelectedIndexChanged onChanged; - final bool scrollable; + final bool isScrollable; void _handleSelectedIndexChanged(int tabIndex) { if (onChanged != null) @@ -495,7 +518,7 @@ class TabNavigator extends Component { labels: views.map((view) => view.label), onChanged: _handleSelectedIndexChanged, selectedIndex: selectedIndex, - scrollable: scrollable + isScrollable: isScrollable ); Widget content = views[selectedIndex].buildContent(); diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml index e9c0157e26..2740c6e82e 100644 --- a/packages/flutter/pubspec.yaml +++ b/packages/flutter/pubspec.yaml @@ -9,7 +9,7 @@ dependencies: mojo_services: ^0.0.15 mojo: ^0.0.17 mojom: ^0.0.17 - newton: ^0.1.0 + newton: ^0.1.2 sky_engine: ^0.0.1 sky_services: ^0.0.1 sky_tools: ^0.0.4