Let translucent Cupertino bars have its scaffold children automatically pad their heights - second try (#13440)
* Let translucent Cupertino bars have its scaffold children automatically pad their heights (#13194) * Let lists automatically add sliver padding from media query. Translucent nav and tab bars leave behind media query paddings in scaffolds. * tests * const lint * Rename base abstract class to generalized ObstructingPreferredSizeWidget * review * More docs and comments from #13317
This commit is contained in:
parent
80c691993a
commit
ecf5041807
@ -131,17 +131,21 @@ class CupertinoDemoTab1 extends StatelessWidget {
|
|||||||
largeTitle: const Text('Colors'),
|
largeTitle: const Text('Colors'),
|
||||||
trailing: const ExitButton(),
|
trailing: const ExitButton(),
|
||||||
),
|
),
|
||||||
new SliverList(
|
new SliverPadding(
|
||||||
delegate: new SliverChildBuilderDelegate(
|
// Top media query padding already consumed by CupertinoSliverNavigationBar.
|
||||||
(BuildContext context, int index) {
|
padding: MediaQuery.of(context).removePadding(removeTop: true).padding,
|
||||||
return new Tab1RowItem(
|
sliver: new SliverList(
|
||||||
index: index,
|
delegate: new SliverChildBuilderDelegate(
|
||||||
lastItem: index == 49,
|
(BuildContext context, int index) {
|
||||||
color: colorItems[index],
|
return new Tab1RowItem(
|
||||||
colorName: colorNameItems[index],
|
index: index,
|
||||||
);
|
lastItem: index == 49,
|
||||||
},
|
color: colorItems[index],
|
||||||
childCount: 50,
|
colorName: colorNameItems[index],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
childCount: 50,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -271,7 +275,7 @@ class Tab1ItemPageState extends State<Tab1ItemPage> {
|
|||||||
),
|
),
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Padding(padding: const EdgeInsets.only(top: 80.0)),
|
const Padding(padding: const EdgeInsets.only(top: 16.0)),
|
||||||
new Padding(
|
new Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
child: new Row(
|
child: new Row(
|
||||||
@ -396,7 +400,6 @@ class CupertinoDemoTab2 extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Padding(padding: const EdgeInsets.only(top: 60.0)),
|
|
||||||
new Tab2Header(),
|
new Tab2Header(),
|
||||||
]..addAll(buildTab2Conversation()),
|
]..addAll(buildTab2Conversation()),
|
||||||
),
|
),
|
||||||
@ -664,7 +667,6 @@ List<Widget> buildTab2Conversation() {
|
|||||||
const Tab2ConversationRow(
|
const Tab2ConversationRow(
|
||||||
text: "What's that?",
|
text: "What's that?",
|
||||||
),
|
),
|
||||||
const Padding(padding: const EdgeInsets.only(bottom: 80.0)),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,7 +682,7 @@ class CupertinoDemoTab3 extends StatelessWidget {
|
|||||||
decoration: const BoxDecoration(color: const Color(0xFFEFEFF4)),
|
decoration: const BoxDecoration(color: const Color(0xFFEFEFF4)),
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Padding(padding: const EdgeInsets.only(top: 100.0)),
|
const Padding(padding: const EdgeInsets.only(top: 32.0)),
|
||||||
new GestureDetector(
|
new GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context, rootNavigator: true).push(
|
Navigator.of(context, rootNavigator: true).push(
|
||||||
|
@ -11,6 +11,7 @@ import 'package:flutter/widgets.dart';
|
|||||||
import 'button.dart';
|
import 'button.dart';
|
||||||
import 'colors.dart';
|
import 'colors.dart';
|
||||||
import 'icons.dart';
|
import 'icons.dart';
|
||||||
|
import 'page_scaffold.dart';
|
||||||
|
|
||||||
/// Standard iOS navigation bar height without the status bar.
|
/// Standard iOS navigation bar height without the status bar.
|
||||||
const double _kNavBarPersistentHeight = 44.0;
|
const double _kNavBarPersistentHeight = 44.0;
|
||||||
@ -68,7 +69,7 @@ const TextStyle _kLargeTitleTextStyle = const TextStyle(
|
|||||||
/// [CupertinoNavigationBar].
|
/// [CupertinoNavigationBar].
|
||||||
/// * [CupertinoSliverNavigationBar] for a navigation bar to be placed in a
|
/// * [CupertinoSliverNavigationBar] for a navigation bar to be placed in a
|
||||||
/// scrolling list and that supports iOS-11-style large titles.
|
/// scrolling list and that supports iOS-11-style large titles.
|
||||||
class CupertinoNavigationBar extends StatelessWidget implements PreferredSizeWidget {
|
class CupertinoNavigationBar extends StatelessWidget implements ObstructingPreferredSizeWidget {
|
||||||
/// Creates a navigation bar in the iOS style.
|
/// Creates a navigation bar in the iOS style.
|
||||||
const CupertinoNavigationBar({
|
const CupertinoNavigationBar({
|
||||||
Key key,
|
Key key,
|
||||||
@ -116,11 +117,12 @@ class CupertinoNavigationBar extends StatelessWidget implements PreferredSizeWid
|
|||||||
final Color actionsForegroundColor;
|
final Color actionsForegroundColor;
|
||||||
|
|
||||||
/// True if the navigation bar's background color has no transparency.
|
/// True if the navigation bar's background color has no transparency.
|
||||||
bool get opaque => backgroundColor.alpha == 0xFF;
|
@override
|
||||||
|
bool get fullObstruction => backgroundColor.alpha == 0xFF;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Size get preferredSize {
|
Size get preferredSize {
|
||||||
return opaque ? const Size.fromHeight(_kNavBarPersistentHeight) : Size.zero;
|
return const Size.fromHeight(_kNavBarPersistentHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -6,7 +6,6 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'colors.dart';
|
import 'colors.dart';
|
||||||
import 'nav_bar.dart';
|
|
||||||
|
|
||||||
/// Implements a single iOS application page's layout.
|
/// Implements a single iOS application page's layout.
|
||||||
///
|
///
|
||||||
@ -32,41 +31,54 @@ class CupertinoPageScaffold extends StatelessWidget {
|
|||||||
///
|
///
|
||||||
/// If translucent, the main content may slide behind it.
|
/// If translucent, the main content may slide behind it.
|
||||||
/// Otherwise, the main content's top margin will be offset by its height.
|
/// Otherwise, the main content's top margin will be offset by its height.
|
||||||
|
///
|
||||||
|
/// The scaffold assumes the nav bar will consume the [MediaQuery] top padding.
|
||||||
// TODO(xster): document its page transition animation when ready
|
// TODO(xster): document its page transition animation when ready
|
||||||
final PreferredSizeWidget navigationBar;
|
final ObstructingPreferredSizeWidget navigationBar;
|
||||||
|
|
||||||
/// Widget to show in the main content area.
|
/// Widget to show in the main content area.
|
||||||
///
|
///
|
||||||
/// Content can slide under the [navigationBar] when they're translucent.
|
/// Content can slide under the [navigationBar] when they're translucent.
|
||||||
|
/// In that case, the child's [BuildContext]'s [MediaQuery] will have a
|
||||||
|
/// top padding indicating the area of obstructing overlap from the
|
||||||
|
/// [navigationBar].
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<Widget> stacked = <Widget>[];
|
final List<Widget> stacked = <Widget>[];
|
||||||
Widget childWithMediaQuery = child;
|
|
||||||
|
|
||||||
double topPadding = 0.0;
|
Widget paddedContent = child;
|
||||||
if (navigationBar != null) {
|
if (navigationBar != null) {
|
||||||
topPadding += navigationBar.preferredSize.height;
|
final MediaQueryData existingMediaQuery = MediaQuery.of(context);
|
||||||
// If the navigation bar has a preferred size, pad it and the OS status
|
|
||||||
// bar as well. Otherwise, let the content extend to the complete top
|
// TODO(xster): Use real size after partial layout instead of preferred size.
|
||||||
// of the page.
|
// https://github.com/flutter/flutter/issues/12912
|
||||||
if (topPadding > 0.0) {
|
final double topPadding = navigationBar.preferredSize.height
|
||||||
final EdgeInsets mediaQueryPadding = MediaQuery.of(context).padding;
|
+ existingMediaQuery.padding.top;
|
||||||
topPadding += mediaQueryPadding.top;
|
|
||||||
childWithMediaQuery = new MediaQuery.removePadding(
|
// If nav bar is opaquely obstructing, directly shift the main content
|
||||||
context: context,
|
// down. If translucent, let main content draw behind nav bar but hint the
|
||||||
removeTop: true,
|
// obstructed area.
|
||||||
|
if (navigationBar.fullObstruction) {
|
||||||
|
paddedContent = new Padding(
|
||||||
|
padding: new EdgeInsets.only(top: topPadding),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
paddedContent = new MediaQuery(
|
||||||
|
data: existingMediaQuery.copyWith(
|
||||||
|
padding: existingMediaQuery.padding.copyWith(
|
||||||
|
top: topPadding,
|
||||||
|
),
|
||||||
|
),
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The main content being at the bottom is added to the stack first.
|
// The main content being at the bottom is added to the stack first.
|
||||||
stacked.add(new Padding(
|
stacked.add(paddedContent);
|
||||||
padding: new EdgeInsets.only(top: topPadding),
|
|
||||||
child: childWithMediaQuery,
|
|
||||||
));
|
|
||||||
|
|
||||||
if (navigationBar != null) {
|
if (navigationBar != null) {
|
||||||
stacked.add(new Positioned(
|
stacked.add(new Positioned(
|
||||||
@ -84,4 +96,17 @@ class CupertinoPageScaffold extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Widget that has a preferred size and reports whether it fully obstructs
|
||||||
|
/// widgets behind it.
|
||||||
|
///
|
||||||
|
/// Used by [CupertinoPageScaffold] to either shift away fully obstructed content
|
||||||
|
/// or provide a padding guide to partially obstructed content.
|
||||||
|
abstract class ObstructingPreferredSizeWidget extends PreferredSizeWidget {
|
||||||
|
/// If true, this widget fully obstructs widgets behind it by the specified
|
||||||
|
/// size.
|
||||||
|
///
|
||||||
|
/// If false, this widget partially obstructs.
|
||||||
|
bool get fullObstruction;
|
||||||
|
}
|
||||||
|
@ -117,7 +117,10 @@ class CupertinoTabScaffold extends StatefulWidget {
|
|||||||
/// When the tab becomes inactive, its content is still cached in the widget
|
/// When the tab becomes inactive, its content is still cached in the widget
|
||||||
/// tree [Offstage] and its animations disabled.
|
/// tree [Offstage] and its animations disabled.
|
||||||
///
|
///
|
||||||
/// Content can slide under the [tabBar] when it's translucent.
|
/// Content can slide under the [tabBar] when they're translucent.
|
||||||
|
/// In that case, the child's [BuildContext]'s [MediaQuery] will have a
|
||||||
|
/// bottom padding indicating the area of obstructing overlap from the
|
||||||
|
/// [tabBar].
|
||||||
final IndexedWidgetBuilder tabBuilder;
|
final IndexedWidgetBuilder tabBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -131,18 +134,43 @@ class _CupertinoTabScaffoldState extends State<CupertinoTabScaffold> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<Widget> stacked = <Widget>[];
|
final List<Widget> stacked = <Widget>[];
|
||||||
|
|
||||||
// The main content being at the bottom is added to the stack first.
|
Widget content = new _TabView(
|
||||||
stacked.add(
|
currentTabIndex: _currentPage,
|
||||||
new Padding(
|
tabNumber: widget.tabBar.items.length,
|
||||||
padding: new EdgeInsets.only(bottom: widget.tabBar.opaque ? widget.tabBar.preferredSize.height : 0.0),
|
tabBuilder: widget.tabBuilder,
|
||||||
child: new _TabView(
|
|
||||||
currentTabIndex: _currentPage,
|
|
||||||
tabNumber: widget.tabBar.items.length,
|
|
||||||
tabBuilder: widget.tabBuilder,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (widget.tabBar != null) {
|
||||||
|
final MediaQueryData existingMediaQuery = MediaQuery.of(context);
|
||||||
|
|
||||||
|
// TODO(xster): Use real size after partial layout instead of preferred size.
|
||||||
|
// https://github.com/flutter/flutter/issues/12912
|
||||||
|
final double bottomPadding = widget.tabBar.preferredSize.height
|
||||||
|
+ existingMediaQuery.padding.bottom;
|
||||||
|
|
||||||
|
// If tab bar opaque, directly stop the main content higher. If
|
||||||
|
// translucent, let main content draw behind the tab bar but hint the
|
||||||
|
// obstructed area.
|
||||||
|
if (widget.tabBar.opaque) {
|
||||||
|
content = new Padding(
|
||||||
|
padding: new EdgeInsets.only(bottom: bottomPadding),
|
||||||
|
child: content,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
content = new MediaQuery(
|
||||||
|
data: existingMediaQuery.copyWith(
|
||||||
|
padding: existingMediaQuery.padding.copyWith(
|
||||||
|
bottom: bottomPadding,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: content,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The main content being at the bottom is added to the stack first.
|
||||||
|
stacked.add(content);
|
||||||
|
|
||||||
if (widget.tabBar != null) {
|
if (widget.tabBar != null) {
|
||||||
stacked.add(new Align(
|
stacked.add(new Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
|
@ -7,6 +7,7 @@ import 'package:flutter/rendering.dart';
|
|||||||
|
|
||||||
import 'basic.dart';
|
import 'basic.dart';
|
||||||
import 'framework.dart';
|
import 'framework.dart';
|
||||||
|
import 'media_query.dart';
|
||||||
import 'primary_scroll_controller.dart';
|
import 'primary_scroll_controller.dart';
|
||||||
import 'scroll_controller.dart';
|
import 'scroll_controller.dart';
|
||||||
import 'scroll_physics.dart';
|
import 'scroll_physics.dart';
|
||||||
@ -372,8 +373,33 @@ abstract class BoxScrollView extends ScrollView {
|
|||||||
@override
|
@override
|
||||||
List<Widget> buildSlivers(BuildContext context) {
|
List<Widget> buildSlivers(BuildContext context) {
|
||||||
Widget sliver = buildChildLayout(context);
|
Widget sliver = buildChildLayout(context);
|
||||||
if (padding != null)
|
EdgeInsetsGeometry effectivePadding = padding;
|
||||||
sliver = new SliverPadding(padding: padding, sliver: sliver);
|
if (padding == null) {
|
||||||
|
final MediaQueryData mediaQuery = MediaQuery.of(context, nullOk: true);
|
||||||
|
if (mediaQuery != null) {
|
||||||
|
// Automatically pad sliver with padding from MediaQuery.
|
||||||
|
final EdgeInsets mediaQueryHorizontalPadding =
|
||||||
|
mediaQuery.padding.copyWith(top: 0.0, bottom: 0.0);
|
||||||
|
final EdgeInsets mediaQueryVerticalPadding =
|
||||||
|
mediaQuery.padding.copyWith(left: 0.0, right: 0.0);
|
||||||
|
// Consume the main axis padding with SliverPadding.
|
||||||
|
effectivePadding = scrollDirection == Axis.vertical
|
||||||
|
? mediaQueryVerticalPadding
|
||||||
|
: mediaQueryHorizontalPadding;
|
||||||
|
// Leave behind the cross axis padding.
|
||||||
|
sliver = new MediaQuery(
|
||||||
|
data: mediaQuery.copyWith(
|
||||||
|
padding: scrollDirection == Axis.vertical
|
||||||
|
? mediaQueryHorizontalPadding
|
||||||
|
: mediaQueryVerticalPadding,
|
||||||
|
),
|
||||||
|
child: sliver,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (effectivePadding != null)
|
||||||
|
sliver = new SliverPadding(padding: effectivePadding, sliver: sliver);
|
||||||
return <Widget>[ sliver ];
|
return <Widget>[ sliver ];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,6 +491,10 @@ abstract class BoxScrollView extends ScrollView {
|
|||||||
/// [CustomScrollView.slivers] property instead of the list itself, and having
|
/// [CustomScrollView.slivers] property instead of the list itself, and having
|
||||||
/// the [SliverList] instead be a child of the [SliverPadding].
|
/// the [SliverList] instead be a child of the [SliverPadding].
|
||||||
///
|
///
|
||||||
|
/// By default, [ListView] will automatically pad the list's scrollable
|
||||||
|
/// extremities to avoid partial obstructions indicated by [MediaQuery]'s
|
||||||
|
/// padding. To avoid this behavior, override with a zero [padding] property.
|
||||||
|
///
|
||||||
/// Once code has been ported to use [CustomScrollView], other slivers, such as
|
/// Once code has been ported to use [CustomScrollView], other slivers, such as
|
||||||
/// [SliverGrid] or [SliverAppBar], can be put in the [CustomScrollView.slivers]
|
/// [SliverGrid] or [SliverAppBar], can be put in the [CustomScrollView.slivers]
|
||||||
/// list.
|
/// list.
|
||||||
@ -742,6 +772,10 @@ class ListView extends BoxScrollView {
|
|||||||
/// [CustomScrollView.slivers] property instead of the grid itself, and having
|
/// [CustomScrollView.slivers] property instead of the grid itself, and having
|
||||||
/// the [SliverGrid] instead be a child of the [SliverPadding].
|
/// the [SliverGrid] instead be a child of the [SliverPadding].
|
||||||
///
|
///
|
||||||
|
/// By default, [ListView] will automatically pad the list's scrollable
|
||||||
|
/// extremities to avoid partial obstructions indicated by [MediaQuery]'s
|
||||||
|
/// padding. To avoid this behavior, override with a zero [padding] property.
|
||||||
|
///
|
||||||
/// Once code has been ported to use [CustomScrollView], other slivers, such as
|
/// Once code has been ported to use [CustomScrollView], other slivers, such as
|
||||||
/// [SliverList] or [SliverAppBar], can be put in the [CustomScrollView.slivers]
|
/// [SliverList] or [SliverAppBar], can be put in the [CustomScrollView.slivers]
|
||||||
/// list.
|
/// list.
|
||||||
|
@ -78,6 +78,66 @@ void main() {
|
|||||||
expect(tester.getSize(find.byWidget(page1Center)).height, 600.0 - 44.0 - 50.0);
|
expect(tester.getSize(find.byWidget(page1Center)).height, 600.0 - 44.0 - 50.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Contents have automatic sliver padding between translucent bars', (WidgetTester tester) async {
|
||||||
|
final Container content = new Container(height: 600.0, width: 600.0);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
new WidgetsApp(
|
||||||
|
color: const Color(0xFFFFFFFF),
|
||||||
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
|
return new CupertinoPageRoute<Null>(
|
||||||
|
settings: settings,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return new MediaQuery(
|
||||||
|
data: const MediaQueryData(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 20.0),
|
||||||
|
),
|
||||||
|
child: new CupertinoTabScaffold(
|
||||||
|
tabBar: new CupertinoTabBar(
|
||||||
|
items: <BottomNavigationBarItem>[
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
icon: const ImageIcon(const TestImageProvider(24, 24)),
|
||||||
|
title: const Text('Tab 1'),
|
||||||
|
),
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
icon: const ImageIcon(const TestImageProvider(24, 24)),
|
||||||
|
title: const Text('Tab 2'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
tabBuilder: (BuildContext context, int index) {
|
||||||
|
return index == 0
|
||||||
|
? new CupertinoPageScaffold(
|
||||||
|
navigationBar: const CupertinoNavigationBar(
|
||||||
|
middle: const Text('Title'),
|
||||||
|
),
|
||||||
|
child: new ListView(
|
||||||
|
children: <Widget>[
|
||||||
|
content,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: new Stack();
|
||||||
|
}
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// List content automatically padded by nav bar and top media query padding.
|
||||||
|
expect(tester.getTopLeft(find.byWidget(content)).dy, 20.0 + 44.0);
|
||||||
|
|
||||||
|
// Overscroll to the bottom.
|
||||||
|
await tester.drag(find.byWidget(content), const Offset(0.0, -400.0));
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
|
||||||
|
// List content automatically padded by tab bar and bottom media query padding.
|
||||||
|
expect(tester.getBottomLeft(find.byWidget(content)).dy, 600 - 20.0 - 50.0);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('iOS independent tab navigation', (WidgetTester tester) async {
|
testWidgets('iOS independent tab navigation', (WidgetTester tester) async {
|
||||||
// A full on iOS information architecture app with 2 tabs, and 2 pages
|
// A full on iOS information architecture app with 2 tabs, and 2 pages
|
||||||
// in each with independent navigation states.
|
// in each with independent navigation states.
|
||||||
|
@ -265,4 +265,32 @@ void main() {
|
|||||||
expect(delegate.log, equals(<String>['didFinishLayout firstIndex=2 lastIndex=5']));
|
expect(delegate.log, equals(<String>['didFinishLayout firstIndex=2 lastIndex=5']));
|
||||||
delegate.log.clear();
|
delegate.log.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('ListView automatically pad MediaQuery on axis', (WidgetTester tester) async {
|
||||||
|
EdgeInsets innerMediaQueryPadding;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new MediaQuery(
|
||||||
|
data: const MediaQueryData(
|
||||||
|
padding: const EdgeInsets.all(30.0),
|
||||||
|
),
|
||||||
|
child: new ListView(
|
||||||
|
children: <Widget>[
|
||||||
|
const Text('top', textDirection: TextDirection.ltr),
|
||||||
|
new Builder(builder: (BuildContext context) {
|
||||||
|
innerMediaQueryPadding = MediaQuery.of(context).padding;
|
||||||
|
return new Container();
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// Automatically apply the top/bottom padding into sliver.
|
||||||
|
expect(tester.getTopLeft(find.text('top')).dy, 30.0);
|
||||||
|
// Leave left/right padding as is for children.
|
||||||
|
expect(innerMediaQueryPadding, const EdgeInsets.symmetric(horizontal: 30.0));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user