Unverified Commit 0ba8c2cd authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

Fix text scale factor for non-content components of Cupertino scaffolds (#38593)

parent 1d8deb1b
......@@ -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,7 +668,9 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
// Lint ignore to maintain backward compatibility.
widget.actionsForegroundColor, // ignore: deprecated_member_use_from_same_package
context,
SliverPersistentHeader(
MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: 1),
child: SliverPersistentHeader(
pinned: true, // iOS navigation bars are always pinned.
delegate: _LargeTitleNavigationBarSliverDelegate(
keys: keys,
......@@ -668,6 +686,7 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
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: 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,8 +400,10 @@ 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(
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
......@@ -407,8 +417,9 @@ class _CupertinoTabScaffoldState extends State<CupertinoTabScaffold> {
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 }) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment