From f5ce638959083e2af42ead2d98f54582dedea4b8 Mon Sep 17 00:00:00 2001 From: Michael Goderbauer Date: Tue, 27 Oct 2020 14:44:50 -0700 Subject: [PATCH] Add Directionality.maybeOf (#69117) --- packages/flutter/lib/src/material/chip.dart | 2 +- packages/flutter/lib/src/material/dialog.dart | 4 +- packages/flutter/lib/src/material/drawer.dart | 4 +- .../flutter/lib/src/material/dropdown.dart | 4 +- .../flutter/lib/src/material/material.dart | 8 +-- .../pickers/calendar_date_picker.dart | 8 +-- .../pickers/calendar_date_range_picker.dart | 2 +- .../lib/src/widgets/animated_size.dart | 4 +- packages/flutter/lib/src/widgets/basic.dart | 63 ++++++++++++------- .../flutter/lib/src/widgets/container.dart | 2 +- packages/flutter/lib/src/widgets/image.dart | 2 +- .../test/widgets/directionality_test.dart | 20 +++++- 12 files changed, 79 insertions(+), 44 deletions(-) diff --git a/packages/flutter/lib/src/material/chip.dart b/packages/flutter/lib/src/material/chip.dart index 22f1604548..d43226627b 100644 --- a/packages/flutter/lib/src/material/chip.dart +++ b/packages/flutter/lib/src/material/chip.dart @@ -1834,7 +1834,7 @@ class _RawChipState extends State with TickerProviderStateMixin with SingleTickerPro Widget _buildDrawer(BuildContext context) { final bool drawerIsStart = widget.alignment == DrawerAlignment.start; final EdgeInsets padding = MediaQuery.of(context)!.padding; - final TextDirection? textDirection = Directionality.of(context); + final TextDirection textDirection = Directionality.of(context)!; double? dragAreaWidth = widget.edgeDragWidth; if (widget.edgeDragWidth == null) { - switch (textDirection!) { + switch (textDirection) { case TextDirection.ltr: dragAreaWidth = _kEdgeDragWidth + (drawerIsStart ? padding.left : padding.right); diff --git a/packages/flutter/lib/src/material/dropdown.dart b/packages/flutter/lib/src/material/dropdown.dart index a9b259d4ff..59a2fa98a1 100644 --- a/packages/flutter/lib/src/material/dropdown.dart +++ b/packages/flutter/lib/src/material/dropdown.dart @@ -564,7 +564,7 @@ class _DropdownRoutePage extends StatelessWidget { route.scrollController = ScrollController(initialScrollOffset: menuLimits.scrollOffset); } - final TextDirection? textDirection = Directionality.of(context); + final TextDirection? textDirection = Directionality.maybeOf(context); final Widget menu = _DropdownMenu( route: route, padding: padding.resolve(textDirection), @@ -1178,7 +1178,7 @@ class _DropdownButtonState extends State> with WidgetsBindi void _handleTap() { final RenderBox itemBox = context.findRenderObject()! as RenderBox; final Rect itemRect = itemBox.localToGlobal(Offset.zero) & itemBox.size; - final TextDirection? textDirection = Directionality.of(context); + final TextDirection? textDirection = Directionality.maybeOf(context); final EdgeInsetsGeometry menuMargin = ButtonTheme.of(context).alignedDropdown ? _kAlignedMenuMargin : _kUnalignedMenuMargin; diff --git a/packages/flutter/lib/src/material/material.dart b/packages/flutter/lib/src/material/material.dart index 7530ec5b2c..e5bdb5aeca 100644 --- a/packages/flutter/lib/src/material/material.dart +++ b/packages/flutter/lib/src/material/material.dart @@ -453,7 +453,7 @@ class _MaterialState extends State with TickerProviderStateMixin { child: child, clipper: ShapeBorderClipper( shape: shape, - textDirection: Directionality.of(context), + textDirection: Directionality.maybeOf(context), ), clipBehavior: clipBehavior, ); @@ -787,7 +787,7 @@ class _MaterialInteriorState extends AnimatedWidgetBaseState<_MaterialInterior> ), clipper: ShapeBorderClipper( shape: shape, - textDirection: Directionality.of(context), + textDirection: Directionality.maybeOf(context), ), clipBehavior: widget.clipBehavior, elevation: elevation, @@ -812,8 +812,8 @@ class _ShapeBorderPaint extends StatelessWidget { Widget build(BuildContext context) { return CustomPaint( child: child, - painter: borderOnForeground ? null : _ShapeBorderPainter(shape, Directionality.of(context)), - foregroundPainter: borderOnForeground ? _ShapeBorderPainter(shape, Directionality.of(context)) : null, + painter: borderOnForeground ? null : _ShapeBorderPainter(shape, Directionality.maybeOf(context)), + foregroundPainter: borderOnForeground ? _ShapeBorderPainter(shape, Directionality.maybeOf(context)) : null, ); } } diff --git a/packages/flutter/lib/src/material/pickers/calendar_date_picker.dart b/packages/flutter/lib/src/material/pickers/calendar_date_picker.dart index 858136436a..c490f34197 100644 --- a/packages/flutter/lib/src/material/pickers/calendar_date_picker.dart +++ b/packages/flutter/lib/src/material/pickers/calendar_date_picker.dart @@ -494,7 +494,7 @@ class _MonthPickerState extends State<_MonthPicker> { late DateTime _previousMonthDate; PageController? _pageController; late MaterialLocalizations _localizations; - TextDirection? _textDirection; + late TextDirection _textDirection; Map? _shortcutMap; Map>? _actionMap; FocusNode? _dayGridFocus; @@ -525,7 +525,7 @@ class _MonthPickerState extends State<_MonthPicker> { void didChangeDependencies() { super.didChangeDependencies(); _localizations = MaterialLocalizations.of(context); - _textDirection = Directionality.of(context); + _textDirection = Directionality.of(context)!; } @override @@ -595,7 +595,7 @@ class _MonthPickerState extends State<_MonthPicker> { if (!_isDisplayingLastMonth) { SemanticsService.announce( _localizations.formatMonthYear(_nextMonthDate), - _textDirection!, + _textDirection, ); _pageController!.nextPage( duration: _monthScrollDuration, @@ -609,7 +609,7 @@ class _MonthPickerState extends State<_MonthPicker> { if (!_isDisplayingFirstMonth) { SemanticsService.announce( _localizations.formatMonthYear(_previousMonthDate), - _textDirection!, + _textDirection, ); _pageController!.previousPage( duration: _monthScrollDuration, diff --git a/packages/flutter/lib/src/material/pickers/calendar_date_range_picker.dart b/packages/flutter/lib/src/material/pickers/calendar_date_range_picker.dart index e175db1ece..b9b00117b8 100644 --- a/packages/flutter/lib/src/material/pickers/calendar_date_range_picker.dart +++ b/packages/flutter/lib/src/material/pickers/calendar_date_range_picker.dart @@ -707,7 +707,7 @@ class _MonthItemState extends State<_MonthItem> { final ColorScheme colorScheme = theme.colorScheme; final TextTheme textTheme = theme.textTheme; final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final TextDirection? textDirection = Directionality.of(context); + final TextDirection textDirection = Directionality.of(context)!; final Color highlightColor = _highlightColor(context); final int day = dayToBuild.day; diff --git a/packages/flutter/lib/src/widgets/animated_size.dart b/packages/flutter/lib/src/widgets/animated_size.dart index fe9ce552eb..0593da413d 100644 --- a/packages/flutter/lib/src/widgets/animated_size.dart +++ b/packages/flutter/lib/src/widgets/animated_size.dart @@ -116,7 +116,7 @@ class AnimatedSize extends SingleChildRenderObjectWidget { reverseDuration: reverseDuration, curve: curve, vsync: vsync, - textDirection: Directionality.of(context), + textDirection: Directionality.maybeOf(context), clipBehavior: clipBehavior, ); } @@ -129,7 +129,7 @@ class AnimatedSize extends SingleChildRenderObjectWidget { ..reverseDuration = reverseDuration ..curve = curve ..vsync = vsync - ..textDirection = Directionality.of(context) + ..textDirection = Directionality.maybeOf(context) ..clipBehavior = clipBehavior; } diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index cfb1f3333f..7cc1e76952 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -107,11 +107,28 @@ class Directionality extends InheritedWidget { /// ```dart /// TextDirection textDirection = Directionality.of(context); /// ``` + // TODO(goderbauer): Make this non-null when customers have upgraded to Directionality.maybeOf. static TextDirection? of(BuildContext context) { final Directionality? widget = context.dependOnInheritedWidgetOfExactType(); return widget?.textDirection; } + /// The text direction from the closest instance of this class that encloses + /// the given context. + /// + /// If there is no [Directionality] ancestor widget in the tree at the given + /// context, then this will return null. + /// + /// Typical usage is as follows: + /// + /// ```dart + /// TextDirection? textDirection = Directionality.maybeOf(context); + /// ``` + static TextDirection? maybeOf(BuildContext context) { + final Directionality? widget = context.dependOnInheritedWidgetOfExactType(); + return widget?.textDirection; + } + @override bool updateShouldNotify(Directionality oldWidget) => textDirection != oldWidget.textDirection; @@ -831,7 +848,7 @@ class ClipPath extends SingleChildRenderObjectWidget { return ClipPath( clipper: ShapeBorderClipper( shape: shape, - textDirection: Directionality.of(context), + textDirection: Directionality.maybeOf(context), ), clipBehavior: clipBehavior, child: child, @@ -1258,7 +1275,7 @@ class Transform extends SingleChildRenderObjectWidget { transform: transform, origin: origin, alignment: alignment, - textDirection: Directionality.of(context), + textDirection: Directionality.maybeOf(context), transformHitTests: transformHitTests, ); } @@ -1269,7 +1286,7 @@ class Transform extends SingleChildRenderObjectWidget { ..transform = transform ..origin = origin ..alignment = alignment - ..textDirection = Directionality.of(context) + ..textDirection = Directionality.maybeOf(context) ..transformHitTests = transformHitTests; } } @@ -1495,7 +1512,7 @@ class FittedBox extends SingleChildRenderObjectWidget { return RenderFittedBox( fit: fit, alignment: alignment, - textDirection: Directionality.of(context), + textDirection: Directionality.maybeOf(context), clipBehavior: clipBehavior, ); } @@ -1505,7 +1522,7 @@ class FittedBox extends SingleChildRenderObjectWidget { renderObject ..fit = fit ..alignment = alignment - ..textDirection = Directionality.of(context) + ..textDirection = Directionality.maybeOf(context) ..clipBehavior = clipBehavior; } @@ -1689,7 +1706,7 @@ class Padding extends SingleChildRenderObjectWidget { RenderPadding createRenderObject(BuildContext context) { return RenderPadding( padding: padding, - textDirection: Directionality.of(context), + textDirection: Directionality.maybeOf(context), ); } @@ -1697,7 +1714,7 @@ class Padding extends SingleChildRenderObjectWidget { void updateRenderObject(BuildContext context, RenderPadding renderObject) { renderObject ..padding = padding - ..textDirection = Directionality.of(context); + ..textDirection = Directionality.maybeOf(context); } @override @@ -1884,7 +1901,7 @@ class Align extends SingleChildRenderObjectWidget { alignment: alignment, widthFactor: widthFactor, heightFactor: heightFactor, - textDirection: Directionality.of(context), + textDirection: Directionality.maybeOf(context), ); } @@ -1894,7 +1911,7 @@ class Align extends SingleChildRenderObjectWidget { ..alignment = alignment ..widthFactor = widthFactor ..heightFactor = heightFactor - ..textDirection = Directionality.of(context); + ..textDirection = Directionality.maybeOf(context); } @override @@ -2334,7 +2351,7 @@ class UnconstrainedBox extends SingleChildRenderObjectWidget { @override void updateRenderObject(BuildContext context, covariant RenderUnconstrainedBox renderObject) { renderObject - ..textDirection = textDirection ?? Directionality.of(context) + ..textDirection = textDirection ?? Directionality.maybeOf(context) ..alignment = alignment ..constrainedAxis = constrainedAxis ..clipBehavior = clipBehavior; @@ -2342,7 +2359,7 @@ class UnconstrainedBox extends SingleChildRenderObjectWidget { @override RenderUnconstrainedBox createRenderObject(BuildContext context) => RenderUnconstrainedBox( - textDirection: textDirection ?? Directionality.of(context), + textDirection: textDirection ?? Directionality.maybeOf(context), alignment: alignment, constrainedAxis: constrainedAxis, clipBehavior: clipBehavior, @@ -2431,7 +2448,7 @@ class FractionallySizedBox extends SingleChildRenderObjectWidget { alignment: alignment, widthFactor: widthFactor, heightFactor: heightFactor, - textDirection: Directionality.of(context), + textDirection: Directionality.maybeOf(context), ); } @@ -2441,7 +2458,7 @@ class FractionallySizedBox extends SingleChildRenderObjectWidget { ..alignment = alignment ..widthFactor = widthFactor ..heightFactor = heightFactor - ..textDirection = Directionality.of(context); + ..textDirection = Directionality.maybeOf(context); } @override @@ -2594,7 +2611,7 @@ class OverflowBox extends SingleChildRenderObjectWidget { maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight, - textDirection: Directionality.of(context), + textDirection: Directionality.maybeOf(context), ); } @@ -2606,7 +2623,7 @@ class OverflowBox extends SingleChildRenderObjectWidget { ..maxWidth = maxWidth ..minHeight = minHeight ..maxHeight = maxHeight - ..textDirection = Directionality.of(context); + ..textDirection = Directionality.maybeOf(context); } @override @@ -3400,7 +3417,7 @@ class Stack extends MultiChildRenderObjectWidget { assert(_debugCheckHasDirectionality(context)); return RenderStack( alignment: alignment, - textDirection: textDirection ?? Directionality.of(context), + textDirection: textDirection ?? Directionality.maybeOf(context), fit: fit, clipBehavior: overflow == Overflow.visible ? Clip.none : clipBehavior, ); @@ -3411,7 +3428,7 @@ class Stack extends MultiChildRenderObjectWidget { assert(_debugCheckHasDirectionality(context)); renderObject ..alignment = alignment - ..textDirection = textDirection ?? Directionality.of(context) + ..textDirection = textDirection ?? Directionality.maybeOf(context) ..fit = fit ..clipBehavior = overflow == Overflow.visible ? Clip.none : clipBehavior; } @@ -3461,7 +3478,7 @@ class IndexedStack extends Stack { return RenderIndexedStack( index: index, alignment: alignment, - textDirection: textDirection ?? Directionality.of(context), + textDirection: textDirection ?? Directionality.maybeOf(context), ); } @@ -3471,7 +3488,7 @@ class IndexedStack extends Stack { renderObject ..index = index ..alignment = alignment - ..textDirection = textDirection ?? Directionality.of(context); + ..textDirection = textDirection ?? Directionality.maybeOf(context); } } @@ -4084,7 +4101,7 @@ class Flex extends MultiChildRenderObjectWidget { /// the logic for providing a text direction only when it is necessary. @protected TextDirection? getEffectiveTextDirection(BuildContext context) { - return textDirection ?? (_needTextDirection ? Directionality.of(context) : null); + return textDirection ?? (_needTextDirection ? Directionality.maybeOf(context) : null); } @override @@ -4954,7 +4971,7 @@ class Wrap extends MultiChildRenderObjectWidget { runAlignment: runAlignment, runSpacing: runSpacing, crossAxisAlignment: crossAxisAlignment, - textDirection: textDirection ?? Directionality.of(context), + textDirection: textDirection ?? Directionality.maybeOf(context), verticalDirection: verticalDirection, clipBehavior: clipBehavior, ); @@ -4969,7 +4986,7 @@ class Wrap extends MultiChildRenderObjectWidget { ..runAlignment = runAlignment ..runSpacing = runSpacing ..crossAxisAlignment = crossAxisAlignment - ..textDirection = textDirection ?? Directionality.of(context) + ..textDirection = textDirection ?? Directionality.maybeOf(context) ..verticalDirection = verticalDirection ..clipBehavior = clipBehavior; } @@ -6879,7 +6896,7 @@ class Semantics extends SingleChildRenderObjectWidget { if (!containsText) return null; - return Directionality.of(context); + return Directionality.maybeOf(context); } @override diff --git a/packages/flutter/lib/src/widgets/container.dart b/packages/flutter/lib/src/widgets/container.dart index fc16fcd60a..6f156101d4 100644 --- a/packages/flutter/lib/src/widgets/container.dart +++ b/packages/flutter/lib/src/widgets/container.dart @@ -406,7 +406,7 @@ class Container extends StatelessWidget { assert(decoration != null); current = ClipPath( clipper: _DecorationClipper( - textDirection: Directionality.of(context), + textDirection: Directionality.maybeOf(context), decoration: decoration!, ), clipBehavior: clipBehavior, diff --git a/packages/flutter/lib/src/widgets/image.dart b/packages/flutter/lib/src/widgets/image.dart index c18d32d2a4..b36f174164 100644 --- a/packages/flutter/lib/src/widgets/image.dart +++ b/packages/flutter/lib/src/widgets/image.dart @@ -52,7 +52,7 @@ ImageConfiguration createLocalImageConfiguration(BuildContext context, { Size? s bundle: DefaultAssetBundle.of(context), devicePixelRatio: MediaQuery.of(context, nullOk: true)?.devicePixelRatio ?? 1.0, locale: Localizations.localeOf(context, nullOk: true), - textDirection: Directionality.of(context), + textDirection: Directionality.maybeOf(context), size: size, platform: defaultTargetPlatform, ); diff --git a/packages/flutter/test/widgets/directionality_test.dart b/packages/flutter/test/widgets/directionality_test.dart index 00f914583b..01fc85353c 100644 --- a/packages/flutter/test/widgets/directionality_test.dart +++ b/packages/flutter/test/widgets/directionality_test.dart @@ -55,11 +55,29 @@ void main() { bool good = false; await tester.pumpWidget(Builder( builder: (BuildContext context) { - expect(Directionality.of(context), isNull); + expect(Directionality.maybeOf(context), isNull); good = true; return const Placeholder(); }, )); expect(good, isTrue); }); + + testWidgets('Directionality.maybeOf', (WidgetTester tester) async { + final GlobalKey hasDirectionality = GlobalKey(); + final GlobalKey noDirectionality = GlobalKey(); + await tester.pumpWidget( + Container( + key: noDirectionality, + child: Directionality( + textDirection: TextDirection.rtl, + child: Container( + key: hasDirectionality, + ), + ) + ) + ); + expect(Directionality.maybeOf(noDirectionality.currentContext!), isNull); + expect(Directionality.maybeOf(hasDirectionality.currentContext!), TextDirection.rtl); + }); }