From 7cdf314d0675064ee6f7b2b94935b250edae2f71 Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Tue, 5 Sep 2023 13:01:51 -0700 Subject: [PATCH] CupertinoAlertDialog should not create ScrollController on every build, if null values are passed in constructor. (#133918) --- .../flutter/lib/src/cupertino/dialog.dart | 48 ++++++++++++------- .../flutter/test/material/dialog_test.dart | 2 +- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/packages/flutter/lib/src/cupertino/dialog.dart b/packages/flutter/lib/src/cupertino/dialog.dart index ef8c10936043..2e4d27af7574 100644 --- a/packages/flutter/lib/src/cupertino/dialog.dart +++ b/packages/flutter/lib/src/cupertino/dialog.dart @@ -188,7 +188,7 @@ bool _isInAccessibilityMode(BuildContext context) { /// * [CupertinoDialogAction], which is an iOS-style dialog button. /// * [AlertDialog], a Material Design alert dialog. /// * -class CupertinoAlertDialog extends StatelessWidget { +class CupertinoAlertDialog extends StatefulWidget { /// Creates an iOS-style alert dialog. /// /// The [actions] must not be null. @@ -233,9 +233,6 @@ class CupertinoAlertDialog extends StatelessWidget { /// section when there are many actions. final ScrollController? scrollController; - ScrollController get _effectiveScrollController => - scrollController ?? ScrollController(); - /// A scroll controller that can be used to control the scrolling of the /// actions in the dialog. /// @@ -247,37 +244,49 @@ class CupertinoAlertDialog extends StatelessWidget { /// section when it is long. final ScrollController? actionScrollController; - ScrollController get _effectiveActionScrollController => - actionScrollController ?? ScrollController(); - /// {@macro flutter.material.dialog.insetAnimationDuration} final Duration insetAnimationDuration; /// {@macro flutter.material.dialog.insetAnimationCurve} final Curve insetAnimationCurve; + @override + State createState() => _CupertinoAlertDialogState(); +} + +class _CupertinoAlertDialogState extends State { + ScrollController? _backupScrollController; + + ScrollController? _backupActionScrollController; + + ScrollController get _effectiveScrollController => + widget.scrollController ?? (_backupScrollController ??= ScrollController()); + + ScrollController get _effectiveActionScrollController => + widget.actionScrollController ?? (_backupActionScrollController ??= ScrollController()); + Widget _buildContent(BuildContext context) { final double textScaleFactor = MediaQuery.textScalerOf(context).textScaleFactor; final List children = [ - if (title != null || content != null) + if (widget.title != null || widget.content != null) Flexible( flex: 3, child: _CupertinoAlertContentSection( - title: title, - message: content, + title: widget.title, + message: widget.content, scrollController: _effectiveScrollController, titlePadding: EdgeInsets.only( left: _kDialogEdgePadding, right: _kDialogEdgePadding, - bottom: content == null ? _kDialogEdgePadding : 1.0, + bottom: widget.content == null ? _kDialogEdgePadding : 1.0, top: _kDialogEdgePadding * textScaleFactor, ), messagePadding: EdgeInsets.only( left: _kDialogEdgePadding, right: _kDialogEdgePadding, bottom: _kDialogEdgePadding * textScaleFactor, - top: title == null ? _kDialogEdgePadding : 1.0, + top: widget.title == null ? _kDialogEdgePadding : 1.0, ), titleTextStyle: _kCupertinoDialogTitleStyle.copyWith( color: CupertinoDynamicColor.resolve(CupertinoColors.label, context), @@ -303,10 +312,10 @@ class CupertinoAlertDialog extends StatelessWidget { Widget actionSection = Container( height: 0.0, ); - if (actions.isNotEmpty) { + if (widget.actions.isNotEmpty) { actionSection = _CupertinoAlertActionSection( scrollController: _effectiveActionScrollController, - children: actions, + children: widget.actions, ); } @@ -330,8 +339,8 @@ class CupertinoAlertDialog extends StatelessWidget { return AnimatedPadding( padding: MediaQuery.viewInsetsOf(context) + const EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0), - duration: insetAnimationDuration, - curve: insetAnimationCurve, + duration: widget.insetAnimationDuration, + curve: widget.insetAnimationCurve, child: MediaQuery.removeViewInsets( removeLeft: true, removeTop: true, @@ -368,6 +377,13 @@ class CupertinoAlertDialog extends StatelessWidget { ), ); } + + @override + void dispose() { + _backupScrollController?.dispose(); + _backupActionScrollController?.dispose(); + super.dispose(); + } } /// Rounded rectangle surface that looks like an iOS popup surface, e.g., alert dialog diff --git a/packages/flutter/test/material/dialog_test.dart b/packages/flutter/test/material/dialog_test.dart index d26707e98629..5e8d9f2ff963 100644 --- a/packages/flutter/test/material/dialog_test.dart +++ b/packages/flutter/test/material/dialog_test.dart @@ -2664,7 +2664,7 @@ void main() { okNode.dispose(); }); - testWidgets('Adaptive AlertDialog shows correct widget on each platform', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Adaptive AlertDialog shows correct widget on each platform', (WidgetTester tester) async { final AlertDialog dialog = AlertDialog.adaptive( content: Container( height: 5000.0,