forked from firka/flutter
Fix text scale factor for non-content components of Cupertino scaffolds (#38593)
This commit is contained in:
@@ -31,6 +31,14 @@ const Color _kDefaultTabBarBorderColor = Color(0x4C000000);
|
||||
/// If the given [backgroundColor]'s opacity is not 1.0 (which is the case by
|
||||
/// default), it will produce a blurring effect to the content behind it.
|
||||
///
|
||||
/// When used as [CupertinoTabScaffold.tabBar], by default `CupertinoTabBar` has
|
||||
/// its text scale factor set to 1.0 and does not respond to text scale factor
|
||||
/// changes from the operating system, to match the native iOS behavior. To override
|
||||
/// this behavior, wrap each of the `navigationBar`'s components inside a [MediaQuery]
|
||||
/// with the desired [MediaQueryData.textScaleFactor] value. The text scale factor
|
||||
/// value from the operating system can be retrieved in many ways, such as querying
|
||||
/// [MediaQuery.textScaleFactorOf] against [CupertinoApp]'s [BuildContext].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [CupertinoTabScaffold], which hosts the [CupertinoTabBar] at the bottom.
|
||||
|
||||
@@ -179,6 +179,14 @@ bool _isTransitionable(BuildContext context) {
|
||||
/// Use [transitionBetweenRoutes] or [heroTag] to customize the transition
|
||||
/// behavior for multiple navigation bars per route.
|
||||
///
|
||||
/// When used in a [CupertinoPageScaffold], [CupertinoPageScaffold.navigationBar]
|
||||
/// has its text scale factor set to 1.0 and does not respond to text scale factor
|
||||
/// changes from the operating system, to match the native iOS behavior. To override
|
||||
/// this behavior, wrap each of the `navigationBar`'s components inside a [MediaQuery]
|
||||
/// with the desired [MediaQueryData.textScaleFactor] value. The text scale factor
|
||||
/// value from the operating system can be retrieved in many ways, such as querying
|
||||
/// [MediaQuery.textScaleFactorOf] against [CupertinoApp]'s [BuildContext].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [CupertinoPageScaffold], a page layout helper typically hosting the
|
||||
@@ -499,6 +507,14 @@ class _CupertinoNavigationBarState extends State<CupertinoNavigationBar> {
|
||||
/// Use [transitionBetweenRoutes] or [heroTag] to customize the transition
|
||||
/// behavior for multiple navigation bars per route.
|
||||
///
|
||||
/// `CupertinoSliverNavigationBar` has its text scale factor set to 1.0 by default
|
||||
/// and does not respond to text scale factor changes from the operating system,
|
||||
/// to match the native iOS behavior. To override this behavior, wrap each of the
|
||||
/// `CupertinoSliverNavigationBar`'s components inside a [MediaQuery] with the
|
||||
/// desired [MediaQueryData.textScaleFactor] value. The text scale factor value
|
||||
/// from the operating system can be retrieved in many ways, such as querying
|
||||
/// [MediaQuery.textScaleFactorOf] against [CupertinoApp]'s [BuildContext].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [CupertinoNavigationBar], an iOS navigation bar for use on non-scrolling
|
||||
@@ -652,20 +668,23 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
|
||||
// Lint ignore to maintain backward compatibility.
|
||||
widget.actionsForegroundColor, // ignore: deprecated_member_use_from_same_package
|
||||
context,
|
||||
SliverPersistentHeader(
|
||||
pinned: true, // iOS navigation bars are always pinned.
|
||||
delegate: _LargeTitleNavigationBarSliverDelegate(
|
||||
keys: keys,
|
||||
components: components,
|
||||
userMiddle: widget.middle,
|
||||
backgroundColor: widget.backgroundColor ?? CupertinoTheme.of(context).barBackgroundColor,
|
||||
border: widget.border,
|
||||
padding: widget.padding,
|
||||
actionsForegroundColor: actionsForegroundColor,
|
||||
transitionBetweenRoutes: widget.transitionBetweenRoutes,
|
||||
heroTag: widget.heroTag,
|
||||
persistentHeight: _kNavBarPersistentHeight + MediaQuery.of(context).padding.top,
|
||||
alwaysShowMiddle: widget.middle != null,
|
||||
MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(textScaleFactor: 1),
|
||||
child: SliverPersistentHeader(
|
||||
pinned: true, // iOS navigation bars are always pinned.
|
||||
delegate: _LargeTitleNavigationBarSliverDelegate(
|
||||
keys: keys,
|
||||
components: components,
|
||||
userMiddle: widget.middle,
|
||||
backgroundColor: widget.backgroundColor ?? CupertinoTheme.of(context).barBackgroundColor,
|
||||
border: widget.border,
|
||||
padding: widget.padding,
|
||||
actionsForegroundColor: actionsForegroundColor,
|
||||
transitionBetweenRoutes: widget.transitionBetweenRoutes,
|
||||
heroTag: widget.heroTag,
|
||||
persistentHeight: _kNavBarPersistentHeight + MediaQuery.of(context).padding.top,
|
||||
alwaysShowMiddle: widget.middle != null,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -34,8 +34,16 @@ class CupertinoPageScaffold extends StatefulWidget {
|
||||
/// If translucent, the main content may slide behind it.
|
||||
/// Otherwise, the main content's top margin will be offset by its height.
|
||||
///
|
||||
/// The scaffold assumes the navigation bar will account for the [MediaQuery] top padding,
|
||||
/// also consume it if the navigation bar is opaque.
|
||||
/// The scaffold assumes the navigation bar will account for the [MediaQuery]
|
||||
/// top padding, also consume it if the navigation bar is opaque.
|
||||
///
|
||||
/// By default `navigationBar` has its text scale factor set to 1.0 and does
|
||||
/// not respond to text scale factor changes from the operating system, to match
|
||||
/// the native iOS behavior. To override such behavior, wrap each of the `navigationBar`'s
|
||||
/// components inside a [MediaQuery] with the desired [MediaQueryData.textScaleFactor]
|
||||
/// value. The text scale factor value from the operating system can be retrieved
|
||||
/// in many ways, such as querying [MediaQuery.textScaleFactorOf] against
|
||||
/// [CupertinoApp]'s [BuildContext].
|
||||
// TODO(xster): document its page transition animation when ready
|
||||
final ObstructingPreferredSizeWidget navigationBar;
|
||||
|
||||
@@ -160,7 +168,10 @@ class _CupertinoPageScaffoldState extends State<CupertinoPageScaffold> {
|
||||
top: 0.0,
|
||||
left: 0.0,
|
||||
right: 0.0,
|
||||
child: widget.navigationBar,
|
||||
child: MediaQuery(
|
||||
data: existingMediaQuery.copyWith(textScaleFactor: 1),
|
||||
child: widget.navigationBar,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -237,6 +237,14 @@ class CupertinoTabScaffold extends StatefulWidget {
|
||||
/// If translucent, the main content may slide behind it.
|
||||
/// Otherwise, the main content's bottom margin will be offset by its height.
|
||||
///
|
||||
/// By default `tabBar` has its text scale factor set to 1.0 and does not
|
||||
/// respond to text scale factor changes from the operating system, to match
|
||||
/// the native iOS behavior. To override this behavior, wrap each of the `tabBar`'s
|
||||
/// items inside a [MediaQuery] with the desired [MediaQueryData.textScaleFactor]
|
||||
/// value. The text scale factor value from the operating system can be retrieved
|
||||
/// int many ways, such as querying [MediaQuery.textScaleFactorOf] against
|
||||
/// [CupertinoApp]'s [BuildContext].
|
||||
///
|
||||
/// Must not be null.
|
||||
final CupertinoTabBar tabBar;
|
||||
|
||||
@@ -392,23 +400,26 @@ class _CupertinoTabScaffoldState extends State<CupertinoTabScaffold> {
|
||||
// The main content being at the bottom is added to the stack first.
|
||||
stacked.add(content);
|
||||
|
||||
if (widget.tabBar != null) {
|
||||
stacked.add(Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
// Override the tab bar's currentIndex to the current tab and hook in
|
||||
// our own listener to update the [_controller.currentIndex] on top of a possibly user
|
||||
// provided callback.
|
||||
child: widget.tabBar.copyWith(
|
||||
currentIndex: _controller.index,
|
||||
onTap: (int newIndex) {
|
||||
_controller.index = newIndex;
|
||||
// Chain the user's original callback.
|
||||
if (widget.tabBar.onTap != null)
|
||||
stacked.add(
|
||||
MediaQuery(
|
||||
data: existingMediaQuery.copyWith(textScaleFactor: 1),
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
// Override the tab bar's currentIndex to the current tab and hook in
|
||||
// our own listener to update the [_controller.currentIndex] on top of a possibly user
|
||||
// provided callback.
|
||||
child: widget.tabBar.copyWith(
|
||||
currentIndex: _controller.index,
|
||||
onTap: (int newIndex) {
|
||||
_controller.index = newIndex;
|
||||
// Chain the user's original callback.
|
||||
if (widget.tabBar.onTap != null)
|
||||
widget.tabBar.onTap(newIndex);
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
),
|
||||
);
|
||||
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
|
||||
@@ -221,6 +221,48 @@ void main() {
|
||||
expect(tester.state<EditableTextState>(find.byType(EditableText)), editableState);
|
||||
expect(find.text("don't lose me"), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('textScaleFactor is set to 1.0', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Builder(builder: (BuildContext context) {
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(textScaleFactor: 99),
|
||||
child: CupertinoTabScaffold(
|
||||
tabBar: CupertinoTabBar(
|
||||
items: List<BottomNavigationBarItem>.generate(
|
||||
10,
|
||||
(int i) => BottomNavigationBarItem(icon: const ImageIcon(TestImageProvider(24, 23)), title: Text('$i'))
|
||||
),
|
||||
),
|
||||
tabBuilder: (BuildContext context, int index) => const Text('content'),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
final Iterable<RichText> barItems = tester.widgetList<RichText>(
|
||||
find.descendant(
|
||||
of: find.byType(CupertinoTabBar),
|
||||
matching: find.byType(RichText),
|
||||
),
|
||||
);
|
||||
|
||||
final Iterable<RichText> contents = tester.widgetList<RichText>(
|
||||
find.descendant(
|
||||
of: find.text('content'),
|
||||
matching: find.byType(RichText),
|
||||
skipOffstage: false,
|
||||
),
|
||||
);
|
||||
|
||||
expect(barItems.length, greaterThan(0));
|
||||
expect(barItems.any((RichText t) => t.textScaleFactor != 1), isFalse);
|
||||
|
||||
expect(contents.length, greaterThan(0));
|
||||
expect(contents.any((RichText t) => t.textScaleFactor != 99), isFalse);
|
||||
});
|
||||
}
|
||||
|
||||
CupertinoTabBar _buildTabBar({ int selectedTab = 0 }) {
|
||||
|
||||
@@ -1018,6 +1018,91 @@ void main() {
|
||||
expect(backPressed, true);
|
||||
}
|
||||
);
|
||||
|
||||
testWidgets('textScaleFactor is set to 1.0', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: Builder(builder: (BuildContext context) {
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(textScaleFactor: 99),
|
||||
child: CupertinoPageScaffold(
|
||||
child: CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
const CupertinoSliverNavigationBar(
|
||||
leading: Text('leading'),
|
||||
middle: Text('middle'),
|
||||
largeTitle: Text('Large Title'),
|
||||
trailing: Text('trailing'),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
child: const Text('content'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
final Iterable<RichText> barItems = tester.widgetList<RichText>(
|
||||
find.descendant(
|
||||
of: find.byType(CupertinoSliverNavigationBar),
|
||||
matching: find.byType(RichText),
|
||||
),
|
||||
);
|
||||
|
||||
final Iterable<RichText> contents = tester.widgetList<RichText>(
|
||||
find.descendant(
|
||||
of: find.text('content'),
|
||||
matching: find.byType(RichText),
|
||||
),
|
||||
);
|
||||
|
||||
expect(barItems.length, greaterThan(0));
|
||||
expect(barItems.any((RichText t) => t.textScaleFactor != 1), isFalse);
|
||||
|
||||
expect(contents.length, greaterThan(0));
|
||||
expect(contents.any((RichText t) => t.textScaleFactor != 99), isFalse);
|
||||
|
||||
// Also works with implicitly added widgets.
|
||||
tester.state<NavigatorState>(find.byType(Navigator)).push(CupertinoPageRoute<void>(
|
||||
title: 'title',
|
||||
builder: (BuildContext context) {
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(textScaleFactor: 99),
|
||||
child: Container(
|
||||
child: const CupertinoPageScaffold(
|
||||
child: CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
CupertinoSliverNavigationBar(
|
||||
automaticallyImplyLeading: true,
|
||||
automaticallyImplyTitle: true,
|
||||
previousPageTitle: 'previous title',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
));
|
||||
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
|
||||
final Iterable<RichText> barItems2 = tester.widgetList<RichText>(
|
||||
find.descendant(
|
||||
of: find.byType(CupertinoSliverNavigationBar),
|
||||
matching: find.byType(RichText),
|
||||
),
|
||||
);
|
||||
|
||||
expect(barItems2.length, greaterThan(0));
|
||||
expect(barItems2.any((RichText t) => t.textScaleFactor != 1), isFalse);
|
||||
});
|
||||
}
|
||||
|
||||
class _ExpectStyles extends StatelessWidget {
|
||||
|
||||
@@ -481,4 +481,32 @@ void main() {
|
||||
expect(positionWithInsetNoNavBar.dy, lessThan(positionNoInsetNoNavBar.dy));
|
||||
expect(positionWithInsetNoNavBar, equals(positionWithInsetWithNavBar));
|
||||
});
|
||||
|
||||
testWidgets('textScaleFactor is set to 1.0', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: Builder(builder: (BuildContext context) {
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(textScaleFactor: 99),
|
||||
child: const CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text('middle'),
|
||||
leading: Text('leading'),
|
||||
trailing: Text('trailing'),
|
||||
),
|
||||
child: Text('content'),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
final Iterable<RichText> richTextList = tester.widgetList<RichText>(
|
||||
find.descendant(of: find.byType(CupertinoNavigationBar), matching: find.byType(RichText)),
|
||||
);
|
||||
|
||||
expect(richTextList.length, greaterThan(0));
|
||||
expect(richTextList.any((RichText text) => text.textScaleFactor != 1), isFalse);
|
||||
|
||||
expect(tester.widget<RichText>(find.descendant(of: find.text('content'), matching: find.byType(RichText))).textScaleFactor, 99);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -981,6 +981,48 @@ void main() {
|
||||
expect(tester.state<EditableTextState>(find.byType(EditableText)), editableState);
|
||||
expect(find.text("don't lose me"), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('textScaleFactor is set to 1.0', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: Builder(builder: (BuildContext context) {
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(textScaleFactor: 99),
|
||||
child: CupertinoTabScaffold(
|
||||
tabBar: CupertinoTabBar(
|
||||
items: List<BottomNavigationBarItem>.generate(
|
||||
10,
|
||||
(int i) => BottomNavigationBarItem(icon: const ImageIcon(TestImageProvider(24, 23)), title: Text('$i'))
|
||||
),
|
||||
),
|
||||
tabBuilder: (BuildContext context, int index) => const Text('content'),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
final Iterable<RichText> barItems = tester.widgetList<RichText>(
|
||||
find.descendant(
|
||||
of: find.byType(CupertinoTabBar),
|
||||
matching: find.byType(RichText),
|
||||
),
|
||||
);
|
||||
|
||||
final Iterable<RichText> contents = tester.widgetList<RichText>(
|
||||
find.descendant(
|
||||
of: find.text('content'),
|
||||
matching: find.byType(RichText),
|
||||
skipOffstage: false,
|
||||
),
|
||||
);
|
||||
|
||||
expect(barItems.length, greaterThan(0));
|
||||
expect(barItems.any((RichText t) => t.textScaleFactor != 1), isFalse);
|
||||
|
||||
expect(contents.length, greaterThan(0));
|
||||
expect(contents.any((RichText t) => t.textScaleFactor != 99), isFalse);
|
||||
});
|
||||
}
|
||||
|
||||
CupertinoTabBar _buildTabBar({ int selectedTab = 0 }) {
|
||||
|
||||
Reference in New Issue
Block a user