From 9bc3bc98222a03babdd0bc8ee231e1ad00fdaa5a Mon Sep 17 00:00:00 2001 From: amirh Date: Wed, 14 Feb 2018 14:08:13 -0800 Subject: [PATCH] expose scaled fab area in ScaffoldGeometry (#14683) --- .../flutter/lib/src/material/scaffold.dart | 67 +++++++--------- .../flutter/test/material/scaffold_test.dart | 78 +++++++++++++++---- 2 files changed, 92 insertions(+), 53 deletions(-) diff --git a/packages/flutter/lib/src/material/scaffold.dart b/packages/flutter/lib/src/material/scaffold.dart index c4f78738f4..d1f0942837 100644 --- a/packages/flutter/lib/src/material/scaffold.dart +++ b/packages/flutter/lib/src/material/scaffold.dart @@ -37,9 +37,6 @@ enum _ScaffoldSlot { statusBar, } -// Examples can assume: -// ScaffoldGeometry scaffoldGeometry; - /// Geometry information for scaffold components. /// /// To get a [ValueNotifier] for the scaffold geometry call @@ -49,7 +46,6 @@ class ScaffoldGeometry { const ScaffoldGeometry({ this.bottomNavigationBarTop, this.floatingActionButtonArea, - this.floatingActionButtonScale: 1.0, }); /// The distance from the scaffold's top edge to the top edge of the @@ -62,38 +58,35 @@ class ScaffoldGeometry { /// The rectangle in which the scaffold is laying out /// [Scaffold.floatingActionButton]. /// - /// The floating action button might be scaled inside this rectangle, to get - /// the bounding rectangle in which the floating action is painted scale this - /// value by [floatingActionButtonScale]. - /// - /// ## Sample code - /// - /// ```dart - /// final Rect scaledFab = Rect.lerp( - /// scaffoldGeometry.floatingActionButtonArea.center & Size.zero, - /// scaffoldGeometry.floatingActionButtonArea, - /// scaffoldGeometry.floatingActionButtonScale - /// ); - /// ``` - /// /// This is null when there is no floating action button showing. final Rect floatingActionButtonArea; - /// The amount by which the [Scaffold.floatingActionButton] is scaled. - /// - /// To get the bounding rectangle in which the floating action button is - /// painted scaled [floatingActionPosition] by this proportion. - /// - /// This will be 0 when there is no [Scaffold.floatingActionButton] set. - final double floatingActionButtonScale; + ScaffoldGeometry _scaleFab(double scaleFactor) { + if (scaleFactor == 1.0) + return this; + + if (scaleFactor == 0.0) + return new ScaffoldGeometry(bottomNavigationBarTop: bottomNavigationBarTop); + + final Rect scaledFab = Rect.lerp( + floatingActionButtonArea.center & Size.zero, + floatingActionButtonArea, + scaleFactor + ); + return new ScaffoldGeometry( + bottomNavigationBarTop: bottomNavigationBarTop, + floatingActionButtonArea: scaledFab, + ); + } } -class _ScaffoldGeometryNotifier extends ValueNotifier { - _ScaffoldGeometryNotifier(ScaffoldGeometry geometry, this.context) - : assert (context != null), - super(geometry); +class _ScaffoldGeometryNotifier extends ChangeNotifier implements ValueListenable { + _ScaffoldGeometryNotifier(this.geometry, this.context) + : assert (context != null); final BuildContext context; + double fabScale; + ScaffoldGeometry geometry; @override ScaffoldGeometry get value { @@ -107,7 +100,7 @@ class _ScaffoldGeometryNotifier extends ValueNotifier { ); return true; }()); - return super.value; + return geometry._scaleFab(fabScale); } void _updateWith({ @@ -115,16 +108,12 @@ class _ScaffoldGeometryNotifier extends ValueNotifier { Rect floatingActionButtonArea, double floatingActionButtonScale, }) { - final double newFloatingActionButtonScale = floatingActionButtonScale ?? super.value?.floatingActionButtonScale; - Rect newFloatingActionButtonArea; - if (newFloatingActionButtonScale != 0.0) - newFloatingActionButtonArea = floatingActionButtonArea ?? super.value?.floatingActionButtonArea; - - value = new ScaffoldGeometry( - bottomNavigationBarTop: bottomNavigationBarTop ?? super.value?.bottomNavigationBarTop, - floatingActionButtonArea: newFloatingActionButtonArea, - floatingActionButtonScale: newFloatingActionButtonScale, + fabScale = floatingActionButtonScale ?? fabScale; + geometry = new ScaffoldGeometry( + bottomNavigationBarTop: bottomNavigationBarTop ?? geometry?.bottomNavigationBarTop, + floatingActionButtonArea: floatingActionButtonArea ?? geometry?.floatingActionButtonArea, ); + notifyListeners(); } } diff --git a/packages/flutter/test/material/scaffold_test.dart b/packages/flutter/test/material/scaffold_test.dart index c312382aea..8de68c815a 100644 --- a/packages/flutter/test/material/scaffold_test.dart +++ b/packages/flutter/test/material/scaffold_test.dart @@ -832,10 +832,6 @@ void main() { geometry.floatingActionButtonArea, fabRect ); - expect( - geometry.floatingActionButtonScale, - 1.0 - ); }); testWidgets('no floatingActionButton', (WidgetTester tester) async { @@ -849,11 +845,6 @@ void main() { final GeometryListenerState listenerState = tester.state(find.byType(GeometryListener)); final ScaffoldGeometry geometry = listenerState.cache.value; - expect( - geometry.floatingActionButtonScale, - 0.0 - ); - expect( geometry.floatingActionButtonArea, null @@ -878,18 +869,77 @@ void main() { ), ))); + final GeometryListenerState listenerState = tester.state(find.byType(GeometryListener)); await tester.pump(const Duration(milliseconds: 50)); - final GeometryListenerState listenerState = tester.state(find.byType(GeometryListener)); - final ScaffoldGeometry geometry = listenerState.cache.value; + ScaffoldGeometry geometry = listenerState.cache.value; + + final Rect transitioningFabRect = geometry.floatingActionButtonArea; + + await tester.pump(const Duration(seconds: 3)); + geometry = listenerState.cache.value; + final RenderBox floatingActionButtonBox = tester.renderObject(find.byKey(key)); + final Rect fabRect = floatingActionButtonBox.localToGlobal(Offset.zero) & floatingActionButtonBox.size; expect( - geometry.floatingActionButtonScale, - inExclusiveRange(0.0, 1.0), + geometry.floatingActionButtonArea, + fabRect + ); + + expect( + geometry.floatingActionButtonArea.center, + transitioningFabRect.center + ); + + expect( + geometry.floatingActionButtonArea.width, + greaterThan(transitioningFabRect.width) + ); + + expect( + geometry.floatingActionButtonArea.height, + greaterThan(transitioningFabRect.height) ); }); - }); + testWidgets('change notifications', (WidgetTester tester) async { + final GlobalKey key = new GlobalKey(); + int numNotificationsAtLastFrame = 0; + await tester.pumpWidget(new MaterialApp(home: new Scaffold( + body: new ConstrainedBox( + constraints: const BoxConstraints.expand(height: 80.0), + child: new GeometryListener(), + ), + ))); + + final GeometryListenerState listenerState = tester.state(find.byType(GeometryListener)); + + expect(listenerState.numNotifications, greaterThan(numNotificationsAtLastFrame)); + numNotificationsAtLastFrame = listenerState.numNotifications; + + await tester.pumpWidget(new MaterialApp(home: new Scaffold( + body: new Container(), + floatingActionButton: new FloatingActionButton( + key: key, + child: new GeometryListener(), + onPressed: () {}, + ), + ))); + + expect(listenerState.numNotifications, greaterThan(numNotificationsAtLastFrame)); + numNotificationsAtLastFrame = listenerState.numNotifications; + + await tester.pump(const Duration(milliseconds: 50)); + + expect(listenerState.numNotifications, greaterThan(numNotificationsAtLastFrame)); + numNotificationsAtLastFrame = listenerState.numNotifications; + + await tester.pump(const Duration(seconds: 3)); + + expect(listenerState.numNotifications, greaterThan(numNotificationsAtLastFrame)); + numNotificationsAtLastFrame = listenerState.numNotifications; + }); + }); } class GeometryListener extends StatefulWidget {