Unverified Commit bfd27f6b authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Audit use of defaultTargetPlatform (#36871)

parent 398ac1f6
......@@ -218,8 +218,18 @@ class _CupertinoPickerState extends State<CupertinoPicker> {
void _handleSelectedItemChanged(int index) {
// Only the haptic engine hardware on iOS devices would produce the
// intended effects.
if (defaultTargetPlatform == TargetPlatform.iOS
&& index != _lastHapticIndex) {
bool hasSuitableHapticHardware;
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
hasSuitableHapticHardware = true;
break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
hasSuitableHapticHardware = false;
break;
}
assert(hasSuitableHapticHardware != null);
if (hasSuitableHapticHardware && index != _lastHapticIndex) {
_lastHapticIndex = index;
HapticFeedback.selectionClick();
}
......
......@@ -425,7 +425,7 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox {
}
void _emitVibration() {
switch(defaultTargetPlatform) {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
HapticFeedback.lightImpact();
break;
......
......@@ -11,11 +11,11 @@ import '_platform_io.dart'
/// This is the default value of [ThemeData.platform] (hence the name). Widgets
/// from the material library should use [Theme.of] to determine the current
/// platform for styling purposes, rather than using [defaultTargetPlatform].
/// However, if there is widget behavior that depends on the actual underlying
/// platform, then depending on [defaultTargetPlatform] makes sense.
/// [dart.io.Platform.environment] should be used directly only when it's
/// critical to actually know the current platform, without any overrides
/// possible (for example, when a system API is about to be called).
/// Widgets and render objects at lower layers that try to emulate the
/// underlying platform can depend on [defaultTargetPlatform] directly. The
/// [dart.io.Platform] object should only be used directly when it's critical to
/// actually know the current platform, without any overrides possible (for
/// example, when a system API is about to be called).
///
/// In a test environment, the platform returned is [TargetPlatform.android]
/// regardless of the host platform. (Android was chosen because the tests were
......
......@@ -384,11 +384,11 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
@override
final Size preferredSize;
bool _getEffectiveCenterTitle(ThemeData themeData) {
bool _getEffectiveCenterTitle(ThemeData theme) {
if (centerTitle != null)
return centerTitle;
assert(themeData.platform != null);
switch (themeData.platform) {
assert(theme.platform != null);
switch (theme.platform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
return false;
......@@ -417,7 +417,7 @@ class _AppBarState extends State<AppBar> {
Widget build(BuildContext context) {
assert(!widget.primary || debugCheckHasMediaQuery(context));
assert(debugCheckHasMaterialLocalizations(context));
final ThemeData themeData = Theme.of(context);
final ThemeData theme = Theme.of(context);
final AppBarTheme appBarTheme = AppBarTheme.of(context);
final ScaffoldState scaffold = Scaffold.of(context, nullOk: true);
final ModalRoute<dynamic> parentRoute = ModalRoute.of(context);
......@@ -429,16 +429,16 @@ class _AppBarState extends State<AppBar> {
IconThemeData overallIconTheme = widget.iconTheme
?? appBarTheme.iconTheme
?? themeData.primaryIconTheme;
?? theme.primaryIconTheme;
IconThemeData actionsIconTheme = widget.actionsIconTheme
?? appBarTheme.actionsIconTheme
?? overallIconTheme;
TextStyle centerStyle = widget.textTheme?.title
?? appBarTheme.textTheme?.title
?? themeData.primaryTextTheme.title;
?? theme.primaryTextTheme.title;
TextStyle sideStyle = widget.textTheme?.body1
?? appBarTheme.textTheme?.body1
?? themeData.primaryTextTheme.body1;
?? theme.primaryTextTheme.body1;
if (widget.toolbarOpacity != 1.0) {
final double opacity = const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn).transform(widget.toolbarOpacity);
......@@ -477,7 +477,7 @@ class _AppBarState extends State<AppBar> {
Widget title = widget.title;
if (title != null) {
bool namesRoute;
switch (defaultTargetPlatform) {
switch (theme.platform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
namesRoute = true;
......@@ -524,7 +524,7 @@ class _AppBarState extends State<AppBar> {
leading: leading,
middle: title,
trailing: actions,
centerMiddle: widget._getEffectiveCenterTitle(themeData),
centerMiddle: widget._getEffectiveCenterTitle(theme),
middleSpacing: widget.titleSpacing,
);
......@@ -584,7 +584,7 @@ class _AppBarState extends State<AppBar> {
}
final Brightness brightness = widget.brightness
?? appBarTheme.brightness
?? themeData.primaryColorBrightness;
?? theme.primaryColorBrightness;
final SystemUiOverlayStyle overlayStyle = brightness == Brightness.dark
? SystemUiOverlayStyle.light
: SystemUiOverlayStyle.dark;
......@@ -596,7 +596,7 @@ class _AppBarState extends State<AppBar> {
child: Material(
color: widget.backgroundColor
?? appBarTheme.color
?? themeData.primaryColor,
?? theme.primaryColor,
elevation: widget.elevation
?? appBarTheme.elevation
?? _defaultElevation,
......
......@@ -263,7 +263,7 @@ class _ModalBottomSheet<T> extends StatefulWidget {
class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
String _getRouteLabel(MaterialLocalizations localizations) {
switch (defaultTargetPlatform) {
switch (Theme.of(context).platform) {
case TargetPlatform.iOS:
return '';
case TargetPlatform.android:
......
......@@ -319,7 +319,7 @@ class AlertDialog extends StatelessWidget {
),
));
} else {
switch (defaultTargetPlatform) {
switch (theme.platform) {
case TargetPlatform.iOS:
label = semanticLabel;
break;
......@@ -587,16 +587,18 @@ class SimpleDialog extends StatelessWidget {
final List<Widget> body = <Widget>[];
String label = semanticLabel;
final ThemeData theme = Theme.of(context);
if (title != null) {
body.add(Padding(
padding: titlePadding,
child: DefaultTextStyle(
style: Theme.of(context).textTheme.title,
style: theme.textTheme.title,
child: Semantics(namesRoute: true, child: title),
),
));
} else {
switch (defaultTargetPlatform) {
switch (theme.platform) {
case TargetPlatform.iOS:
label = semanticLabel;
break;
......
......@@ -11,6 +11,7 @@ import 'debug.dart';
import 'list_tile.dart';
import 'material.dart';
import 'material_localizations.dart';
import 'theme.dart';
/// The possible alignments of a [Drawer].
enum DrawerAlignment {
......@@ -125,7 +126,7 @@ class Drawer extends StatelessWidget {
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
String label = semanticLabel;
switch (defaultTargetPlatform) {
switch (Theme.of(context).platform) {
case TargetPlatform.iOS:
label = semanticLabel;
break;
......@@ -469,6 +470,17 @@ class DrawerControllerState extends State<DrawerController> with SingleTickerPro
),
);
} else {
bool platformHasBackButton;
switch (Theme.of(context).platform) {
case TargetPlatform.android:
platformHasBackButton = true;
break;
case TargetPlatform.iOS:
case TargetPlatform.fuchsia:
platformHasBackButton = false;
break;
}
assert(platformHasBackButton != null);
return GestureDetector(
key: _gestureDetectorKey,
onHorizontalDragDown: _handleDragDown,
......@@ -483,7 +495,7 @@ class DrawerControllerState extends State<DrawerController> with SingleTickerPro
BlockSemantics(
child: GestureDetector(
// On Android, the back button is used to dismiss a modal.
excludeFromSemantics: defaultTargetPlatform == TargetPlatform.android,
excludeFromSemantics: platformHasBackButton,
onTap: close,
child: Semantics(
label: MaterialLocalizations.of(context)?.modalBarrierDismissLabel,
......
......@@ -199,8 +199,10 @@ class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> {
}
if (widget.title != null) {
final ThemeData theme = Theme.of(context);
Widget title;
switch (defaultTargetPlatform) {
switch (theme.platform) {
case TargetPlatform.iOS:
title = widget.title;
break;
......@@ -212,7 +214,6 @@ class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> {
);
}
final ThemeData theme = Theme.of(context);
final double opacity = settings.toolbarOpacity;
if (opacity > 0.0) {
TextStyle titleStyle = theme.primaryTextTheme.title;
......
......@@ -748,8 +748,9 @@ Future<T> showMenu<T>({
assert(position != null);
assert(items != null && items.isNotEmpty);
assert(debugCheckHasMaterialLocalizations(context));
String label = semanticLabel;
switch (defaultTargetPlatform) {
switch (Theme.of(context).platform) {
case TargetPlatform.iOS:
label = semanticLabel;
break;
......
......@@ -53,10 +53,9 @@ class Scrollbar extends StatefulWidget {
class _ScrollbarState extends State<Scrollbar> with TickerProviderStateMixin {
ScrollbarPainter _materialPainter;
TargetPlatform _currentPlatform;
TextDirection _textDirection;
Color _themeColor;
bool _useCupertinoScrollbar;
AnimationController _fadeoutAnimationController;
Animation<double> _fadeoutOpacityAnimation;
Timer _fadeoutTimer;
......@@ -77,35 +76,36 @@ class _ScrollbarState extends State<Scrollbar> with TickerProviderStateMixin {
@override
void didChangeDependencies() {
super.didChangeDependencies();
assert((() { _useCupertinoScrollbar = null; return true; })());
final ThemeData theme = Theme.of(context);
_currentPlatform = theme.platform;
switch (_currentPlatform) {
switch (theme.platform) {
case TargetPlatform.iOS:
// On iOS, stop all local animations. CupertinoScrollbar has its own
// animations.
_fadeoutTimer?.cancel();
_fadeoutTimer = null;
_fadeoutAnimationController.reset();
_useCupertinoScrollbar = true;
break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
_themeColor = theme.highlightColor.withOpacity(1.0);
_textDirection = Directionality.of(context);
_materialPainter = _buildMaterialScrollbarPainter();
_useCupertinoScrollbar = false;
break;
}
assert(_useCupertinoScrollbar != null);
}
ScrollbarPainter _buildMaterialScrollbarPainter() {
return ScrollbarPainter(
color: _themeColor,
textDirection: _textDirection,
thickness: _kScrollbarThickness,
fadeoutOpacityAnimation: _fadeoutOpacityAnimation,
padding: MediaQuery.of(context).padding,
);
color: _themeColor,
textDirection: _textDirection,
thickness: _kScrollbarThickness,
fadeoutOpacityAnimation: _fadeoutOpacityAnimation,
padding: MediaQuery.of(context).padding,
);
}
bool _handleScrollNotification(ScrollNotification notification) {
......@@ -116,9 +116,8 @@ class _ScrollbarState extends State<Scrollbar> with TickerProviderStateMixin {
// iOS sub-delegates to the CupertinoScrollbar instead and doesn't handle
// scroll notifications here.
if (_currentPlatform != TargetPlatform.iOS
&& (notification is ScrollUpdateNotification
|| notification is OverscrollNotification)) {
if (!_useCupertinoScrollbar &&
(notification is ScrollUpdateNotification || notification is OverscrollNotification)) {
if (_fadeoutAnimationController.status != AnimationStatus.forward) {
_fadeoutAnimationController.forward();
}
......@@ -143,25 +142,21 @@ class _ScrollbarState extends State<Scrollbar> with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
switch (_currentPlatform) {
case TargetPlatform.iOS:
return CupertinoScrollbar(
child: widget.child,
);
case TargetPlatform.android:
case TargetPlatform.fuchsia:
return NotificationListener<ScrollNotification>(
onNotification: _handleScrollNotification,
if (_useCupertinoScrollbar) {
return CupertinoScrollbar(
child: widget.child,
);
}
return NotificationListener<ScrollNotification>(
onNotification: _handleScrollNotification,
child: RepaintBoundary(
child: CustomPaint(
foregroundPainter: _materialPainter,
child: RepaintBoundary(
child: CustomPaint(
foregroundPainter: _materialPainter,
child: RepaintBoundary(
child: widget.child,
),
),
child: widget.child,
),
);
}
throw FlutterError('Unknown platform for scrollbar insertion');
),
),
);
}
}
......@@ -485,7 +485,7 @@ class _SearchPageState<T> extends State<_SearchPage<T>> {
break;
}
String routeName;
switch (defaultTargetPlatform) {
switch (theme.platform) {
case TargetPlatform.iOS:
routeName = '';
break;
......
......@@ -774,14 +774,26 @@ class ThemeData extends Diagnosticable {
/// The platform the material widgets should adapt to target.
///
/// Defaults to the current platform. This should be used in order to style UI
/// elements according to platform conventions.
/// Defaults to the current platform, as exposed by [defaultTargetPlatform].
/// This should be used in order to style UI elements according to platform
/// conventions.
///
/// [Platform.defaultTargetPlatform] should be used directly instead only in
/// rare cases where it's necessary to determine behavior based on the
/// platform. [dart.io.Platform.environment] should be used when it's critical
/// Widgets from the material library should use this getter (via [Theme.of])
/// to determine the current platform for the purpose of emulating the
/// platform behavior (e.g. scrolling or haptic effects). Widgets and render
/// objects at lower layers that try to emulate the underlying platform
/// platform can depend on [defaultTargetPlatform] directly, or may require
/// that the target platform be provided as an argument. The
/// [dart.io.Platform] object should only be used directly when it's critical
/// to actually know the current platform, without any overrides possible (for
/// example, when a system API is about to be called).
///
/// In a test environment, the platform returned is [TargetPlatform.android]
/// regardless of the host platform. (Android was chosen because the tests
/// were originally written assuming Android-like behavior, and we added
/// platform adaptations for iOS later). Tests can check iOS behavior by
/// setting the [platform] of the [Theme] explicitly to [TargetPlatform.iOS],
/// or by setting [debugDefaultTargetPlatformOverride].
final TargetPlatform platform;
/// Configures the hit test size of certain Material widgets.
......
......@@ -203,8 +203,13 @@ class _DayPeriodControl extends StatelessWidget {
if (fragmentContext.selectedTime.period == DayPeriod.am) {
return;
}
if (fragmentContext.targetPlatform == TargetPlatform.android) {
_announceToAccessibility(context, MaterialLocalizations.of(context).anteMeridiemAbbreviation);
switch (fragmentContext.targetPlatform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
_announceToAccessibility(context, MaterialLocalizations.of(context).anteMeridiemAbbreviation);
break;
case TargetPlatform.iOS:
break;
}
_togglePeriod();
}
......@@ -213,8 +218,13 @@ class _DayPeriodControl extends StatelessWidget {
if (fragmentContext.selectedTime.period == DayPeriod.pm) {
return;
}
if (fragmentContext.targetPlatform == TargetPlatform.android) {
_announceToAccessibility(context, MaterialLocalizations.of(context).postMeridiemAbbreviation);
switch (fragmentContext.targetPlatform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
_announceToAccessibility(context, MaterialLocalizations.of(context).postMeridiemAbbreviation);
break;
case TargetPlatform.iOS:
break;
}
_togglePeriod();
}
......
......@@ -391,13 +391,8 @@ class RenderEditable extends RenderBox {
// TODO(goderbauer): doesn't handle extended grapheme clusters with more than one Unicode scalar value (https://github.com/flutter/flutter/issues/13404).
void _handleKeyEvent(RawKeyEvent keyEvent) {
// Only handle key events on Android.
switch (defaultTargetPlatform) {
case TargetPlatform.android:
break;
case TargetPlatform.iOS:
case TargetPlatform.fuchsia:
return;
}
if (keyEvent.data is! RawKeyEventDataAndroid)
return;
if (keyEvent is RawKeyUpEvent)
return;
......@@ -1594,12 +1589,15 @@ class RenderEditable extends RenderBox {
/// of the cursor for iOS is approximate and obtained through an eyeball
/// comparison.
Rect get _getCaretPrototype {
switch(defaultTargetPlatform){
assert(defaultTargetPlatform != null);
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
return Rect.fromLTWH(0.0, 0.0, cursorWidth, preferredLineHeight + 2);
default:
case TargetPlatform.android:
case TargetPlatform.fuchsia:
return Rect.fromLTWH(0.0, _kCaretHeightOffset, cursorWidth, preferredLineHeight - 2.0 * _kCaretHeightOffset);
}
return null;
}
@override
void performLayout() {
......@@ -1647,10 +1645,11 @@ class RenderEditable extends RenderBox {
if (_cursorOffset != null)
caretRect = caretRect.shift(_cursorOffset);
if (_textPainter.getFullHeightForCaret(textPosition, _caretPrototype) != null) {
final double caretHeight = _textPainter.getFullHeightForCaret(textPosition, _caretPrototype);
if (caretHeight != null) {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS: {
final double heightDiff = _textPainter.getFullHeightForCaret(textPosition, _caretPrototype) - caretRect.height;
case TargetPlatform.iOS:
final double heightDiff = caretHeight - caretRect.height;
// Center the caret vertically along the text.
caretRect = Rect.fromLTWH(
caretRect.left,
......@@ -1659,8 +1658,8 @@ class RenderEditable extends RenderBox {
caretRect.height,
);
break;
}
default: {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
// Override the height to take the full height of the glyph at the TextPosition
// when not on iOS. iOS has special handling that creates a taller caret.
// TODO(garyq): See the TODO for _getCaretPrototype.
......@@ -1668,10 +1667,9 @@ class RenderEditable extends RenderBox {
caretRect.left,
caretRect.top - _kCaretHeightOffset,
caretRect.width,
_textPainter.getFullHeightForCaret(textPosition, _caretPrototype),
caretHeight,
);
break;
}
}
}
......
......@@ -75,7 +75,18 @@ class ModalBarrier extends StatelessWidget {
@override
Widget build(BuildContext context) {
assert(!dismissible || semanticsLabel == null || debugCheckHasDirectionality(context));
final bool semanticsDismissible = dismissible && defaultTargetPlatform != TargetPlatform.android;
bool platformSupportsDismissingBarrier;
switch (defaultTargetPlatform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
platformSupportsDismissingBarrier = false;
break;
case TargetPlatform.iOS:
platformSupportsDismissingBarrier = true;
break;
}
assert(platformSupportsDismissingBarrier != null);
final bool semanticsDismissible = dismissible && platformSupportsDismissingBarrier;
final bool modalBarrierSemanticsDismissible = barrierSemanticsDismissible ?? semanticsDismissible;
return BlockSemantics(
child: ExcludeSemantics(
......
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